是否故意浪费token?

明明已经把需要修改的功能整理成文档了,就差自己写了,一个很简单的功能,还有循环调用工具,然后巨费时间,最后什么也修改不出来,token直接没了,我实在想不明白,你们设计的分析文档是怎么设计的,对我而言,纯粹的浪费token,

文档如下:# 问卷模块设计文档

> 阶段:需求 / 设计(未编码)

> 范围:问卷生命周期、题目管理、作答与导出、统计报表

> 技术基线:Spring Boot + JdbcTemplate + MySQL(`basic`)、JWT 鉴权、手动 SQL 迁移(Flyway 禁用)

-–

## 1. 模块概述

问卷模块覆盖「**设计 → 收集 → 分析 → 管理**」完整闭环:

- **设计**:创建问卷、绑定分类、维护题目(多题型)、草稿编辑

- **收集**:发布后用户作答、作答限制(限次/时间窗口)

- **分析**:回收数量、各题选项分布统计

- **管理**:列表查询、回收站(软删/恢复/彻底删)、审计、导出

对「自己动手查/分析」的应用场景,问卷是最常见的收集工具。

-–

## 2. 功能清单

### 2.1 问卷生命周期(管理端 admin/super_admin)

| 功能 | 说明 |

|------|------|

| 创建问卷 | 新建空问卷(草稿态),绑定一个分类 `type_id` |

| 编辑问卷 | 草稿态可改标题/描述/分类/限答次数/时间窗口 |

| 发布 / 下线 | `status`:`0草稿 → 1已发布 → 2已下线`;发布后可被作答,下线后停止作答 |

| 复制问卷 | 一键深拷贝问卷及全部题目为新问卷(草稿态,拷贝后独立编辑,不含历史作答) |

| 排序 / 置顶 | `sort` 控制库内排序,可置顶,列表按 sort+createTime 倒序 |

| 删除问卷 | 软删,置 `delete_flag=1`,进入回收站 |

| 回收站 | 列表展示已删问卷;恢复(`delete_flag=0`);彻底删除 = 清库(级联题目/作答明细)+ **同时清理磁盘上的媒体文件** |

### 2.2 题目管理

| 功能 | 说明 |

|------|------|

| 题目 CRUD | 增删改查,放在某问卷下 |

| 题型 | 见下方「题型体系」 |

| 排序 | `sort` 字段控制展示顺序,按 sort 升序 |

| 必填 | `required` 1/0,提交时后端校验(备注类题型不适用) |

| 计分 | `hasScore` 是否有分数 + `score` 分值(用于打分制试卷) |

> 约定:已发布且有作答记录的问卷,`question` 题目**不允许删改**(防止历史数据错位),只允许草稿态修改。

#### 2.2.1 题型体系(type 为字符串题型码,可扩展)

| 大类 | type 题型码 | 说明 | 需要 options | 可作答 | required/计分 |

|------|------------|------|:—:|:—:|:—:expressionless:

| 选择类 | `SINGLE` 单选 | 单选一组选项 | ✓ | ✓ | ✓ / ✓ |

| | `MULTI` 多选 | 多选一组选项 | ✓ | ✓ | ✓ / ✓ |

| | `DROPDOWN` 下拉 | 从下拉列表选一项 | ✓ | ✓ | ✓ / ✓ |

| | `JUDGE` 判断 | 选项固定「是/否」(也可自定义对错文案) | 否 | ✓ | ✓ / ✓ |

| 评价类 | `SCORE` 打分/打星 | N 分范围,前端星评 | ✓(存 min/max) | ✓ | ✓ / ✓ |

| 文本类 | `FILL` 填空 | 单行输入 | 否 | ✓ | ✓ / ✓ |

| | `TEXTAREA` 简答 | 多行文本 | 否 | ✓ | ✓ / ✓ |

| | `FILL_MULTI` 多项填空 | 多个空位,options 空位标题 | ✓ | ✓ | ✓ / ✓ |

| | `FILL_TABLE` 横向填空 | 表格矩阵:列=空位,行为分组 | ✓ | ✓ | ✓ / ✓ |

| 备注类 | `REMARK` 备注说明 | 纯展示说明块 | 否 | ✗ | — / — |

| | `PARAGRAPH` 完成提示 | 页内提示/段落文字 | 否 | ✗ | — / — |

| | `FOOTER` 结尾落款 | 问卷尾部致谢/落款 | 否 | ✗ | — / — |

| 媒体类 | `IMAGE` 图片 | 上传图片 | 否 | ✓(传文件) | ✓ / — |

| | `AUDIO` 音频 | 上传音频 | 否 | ✓(传文件) | ✓ / — |

| | `VIDEO` 视频 | 上传视频 | 否 | ✓(传文件) | ✓ / — |

| | `FILE` 文件 | 上传文件 | 否 | ✓(传文件) | ✓ / — |

> 备注类题型**不回写 answer_detail**(无作答内容),仅参与排序展示。

> 媒体类题型作答 = 上传文件,`options` 可限定后缀/大小上限;通常不计分。

### 2.3 作答与导出

| 功能 | 说明 |

|------|------|

| 提交作答 | 登录用户提交一整套答案 |

| 作答限制 | 每个登录用户在 `limit_times` 内可提交次数(0=不限) |

| 时间窗口 | `start_time / end_time` 之间才允许作答 |

| 结果公布 | `publishResult` 控制答题后是否/何时对用户公开统计结果(0不公布 / 1实时 / 2截止公布) |

| 导出 | 汇总统计 + 作答明细,导出 **CSV / Excel** |

### 2.4 统计报表

| 功能 | 说明 |

|------|------|

| 回收统计 | 问卷回收总数、按日趋势 |

| 选项分布 | 每道题各选项的勾选数与占比(单选/多选/评分) |

| 单份详情 | 管理员按记录查看某一份答卷的原始作答(题 + 结构化作答 + 得分) |

| 明细 | 导出时带出每份答卷的每题明细 |

-–

## 3. 数据模型

> 迁移文件:`db/migration/V5__create_questionnaire_tables.sql`(**需手动执行**

> 命名沿用项目驼峰列名习惯。

### questionnaire_type — 问卷分类(绑定类型)

| 字段 | 类型 | 说明 |

|------|------|------|

| id | BIGINT PK AI | |

| name | VARCHAR(50) | 分类名 |

| status | TINYINT | 1 启用 / 0 停用 |

| createTime | DATETIME | |

### questionnaire — 问卷主表

| 字段 | 类型 | 说明 |

|------|------|------|

| id | BIGINT PK AI | |

| typeId | BIGINT | 绑定分类 `questionnaire_type.id` |

| title | VARCHAR(200) | 问卷标题 |

| description | TEXT | 问卷说明 |

| status | TINYINT | 0 草稿 / 1 已发布 / 2 已下线 |

| deleteFlag | TINYINT | 0 正常 / 1 回收站 |

| limitTimes | INT | 每用户限答次数,0=不限 |

| startTime | DATETIME NULL | 可作答开始 |

| endTime | DATETIME NULL | 可作答结束 |

| publishResult | TINYINT | 结果公布:0 不公布 / 1 实时公布 / 2 截止后公布 |

| sort | INT | 库内排序,越大越靠前(支持置顶) |

| createTime | DATETIME | |

| updateTime | DATETIME | |

### question — 题目

| 字段 | 类型 | 说明 |

|------|------|------|

| id | BIGINT PK AI | |

| questionnaireId | BIGINT | 所属问卷 |

| type | VARCHAR(30) | 题型码,见 2.2.1(SINGLE/MULTI/DROPDOWN/JUDGE/SCORE/FILL/TEXTAREA/FILL_MULTI/FILL_TABLE/REMARK/PARAGRAPH/FOOTER/IMAGE/AUDIO/VIDEO/FILE) |

| title | VARCHAR(500) | 题干(备注类为说明/致谢文案) |

| required | TINYINT | 1 必填 / 0 选填(备注类恒为 0) |

| hasScore | TINYINT | 1 该题计分 / 0 不计分 |

| score | INT | 题级分值,默认 1(hasScore=0 时忽略);当选项各自带 `score` 时启用「选项级分值」模式、忽略本值 |

| minSelect | INT NULL | 最少可选几项,0/空=不限(单选/下拉/判断固定为 1) |

| maxSelect | INT NULL | 最多可选几项,0/空=不限;「只能选几项」= minSelect=maxSelect=N |

| sort | INT | 排序,升序 |

| options | TEXT | 选项配置 JSON:选择类选项元素 `{id, label, score?}`(score=选项分值,可空);SCORE 存 `{min,max}`;FILL_MULTI/FILL_TABLE 存空位标题;媒体类存 `{suffix,sizeLimit}` |

| ref | TEXT NULL | 关联关系(JSON):**业务链路**(跳题/条件联动,影响计分与参与统计)**后端必须最小解析****纯展示联动**由 `style` 承载、仍透传。业务跳题如 `{linkType:“JUMP_TO”, targetId:3, triggerOptionId:5}` |

| style | TEXT NULL | 前端样式(JSON,后端透传):`{width, color, fontSize, layout, clazz}` |

| createTime | DATETIME | |

### answer_record — 作答记录

| 字段 | 类型 | 说明 |

|------|------|------|

| id | BIGINT PK AI | |

| questionnaireId | BIGINT | 问卷 |

| userName | VARCHAR(50) | 作答人 |

| submitTime | DATETIME | 提交时间 |

### answer_detail — 作答明细(结构化,按题型落到对应列)

| 字段 | 类型 | 说明 |

|------|------|------|

| id | BIGINT PK AI | |

| recordId | BIGINT | 关联 answer_record |

| questionId | BIGINT | 题目 |

| optionIds | TEXT NULL | 选择类(单选/多选/下拉/判断):勾选项或值,单选/下拉/判断=单值,多选=JSON数组 |

| scoreValue | INT NULL | 评价类(SCORE):分数值 |

| textValue | TEXT NULL | 文本类(填空/简答/多项填空/横向填空):文本或 JSON |

| fileUrl | TEXT NULL | 媒体类(图片/音频/视频/文件):上传文件地址 JSON `{name,url}` |

| createTime | DATETIME | |

> 题型与列**一一对应**:某题作答时仅对应列非空,其余列 NULL。校验 = 该题型对应列必须非空。

> 备注类题型(REMARK/PARAGRAPH/FOOTER)不产生 answer_detail 记录。

> JdbcTemplate 查询时按 `optionIds` 有值即判为选择类等,便于统计;无需反解析 JSON。

> 状态约束:`status`(发布状态)+ `deleteFlag`(回收站)是两个独立维度;

> 作答校验:`deleteFlag=0 && status=1 && 在当前时间窗口内 && 次数未超限`。

#### 计分与跳题口径(重要)

由于跳题/联动存在,分析时必须按**每份答卷**解析 `ref` 的跳题链,得出「该答卷实际适用的题目集合 S」:

- **总分** = Σ(属于 S 且已作答计分题的分值);被跳过的计分题(不在 S)**不计 0 分**

- **计分来源(选项级分值)**:选择类若选项带 `score`,该题得分 = Σ(选中选项.score),忽略题级 `score`;选项无 `score` 则用题级 `score` 计分。评价类用作答的 `scoreValue`。

- **多选数量约束**:`MULTI` 按 `minSelect/maxSelect` 校验选中数(`min=max=N` 表示只能选 N 项),不满足则提交被拒。

- **每题有效分母** = 该题出现在 S 的答卷数(被跳过则该答卷不进入该题分母),避免拉低正确率/参与率

- **校验**:`hasScore=1` 的题若被设为跳题目标或触发条件,后端解析时校验其必填性,避免与计分互相矛盾

-–

## 4. 对外接口

### 4.1 问卷管理(admin / super_admin)

| 方法 | 路径 | 说明 |

|------|------|------|

| POST | `/admin/surveys` | 创建问卷(typeId, title, description, limitTimes, startTime, endTime, publishResult, sort) |

| PUT | `/admin/surveys/{id}` | 编辑问卷(仅草稿态) |

| PUT | `/admin/surveys/{id}/status` | 发布 / 下线(status=1/2) |

| POST | `/admin/surveys/{id}/copy` | 复制问卷(深拷贝题目为新问卷,草稿态) |

| DELETE | `/admin/surveys/{id}` | 软删进回收站 |

| POST | `/admin/surveys/{id}/restore` | 从回收站恢复 |

| DELETE | `/admin/surveys/{id}/purge` | 彻底删除(级联题目/明细 + 清理磁盘媒体文件) |

| GET | `/admin/surveys` | 列表分页查询(?keyword=&typeId=&status=&deleteFlag=&page=&pageSize=,按 sort+createTime 倒序) |

### 4.2 题目管理(admin / super_admin)

| 方法 | 路径 | 说明 |

|------|------|------|

| POST | `/admin/surveys/{id}/questions` | 新增题目 |

| PUT | `/admin/questions/{questionId}` | 编辑题目 |

| DELETE | `/admin/questions/{questionId}` | 删除题目(草稿态) |

| PUT | `/admin/surveys/{id}/questions/sort` | 批量保存排序 |

| GET | `/admin/surveys/{id}/questions` | 问卷下题目列表 |

### 4.3 作答(任意登录用户)

| 方法 | 路径 | 说明 |

|------|------|------|

| GET | `/surveys` | 当前可作答的问卷列表(已发布且在窗口内) |

| GET | `/surveys/{id}` | 问卷详情 + 题目详情(空自动绑定,不暴露作答记录) |

| POST | `/surveys/{id}/submit` | 提交作答(body 数组,元素按题型带对应字段:`{questionId, optionIds?, scoreValue?, textValue?, fileUrl?}`;后端校验必填/对应列非空/时间/次数) |

| GET | `/surveys/{id}/answered` | 当前用户在该问卷的作答次数(命中已答提示) |

| GET | `/surveys/{id}/result` | 用户端统计结果(受 `publishResult` 控制:0 返回无权限提示;2 仅在截止后开放) |

### 4.4 统计与导出(admin / super_admin)

| 方法 | 路径 | 说明 |

|------|------|------|

| GET | `/admin/surveys/{id}/stats` | 回收总数 + 每题目选项分布/评分均值 |

| GET | `/admin/surveys/{id}/records/{recordId}` | 单份答卷详情(结构化作答 + 得分) |

| GET | `/admin/surveys/{id}/export` | 导出 CSV(汇总 + 明细两张 work) |

-–

## 5. 权限 / 审计 / 菜单复用(对齐项目现有机制)

- **权限**:管理端接口复用 `RoleUtil.hasAdminPermission(roleIds)`(super_admin/admin 可管理);作答接口任意登录用户可访问。

- **审计日志**:在现有 `permission_log` 引入新操作类型:

`SURVEY_CREATE / SURVEY_UPDATE / SURVEY_DELETE / SURVEY_RESTORE / SURVEY_PURGE / SURVEY_STATUS`

由 `PermissionLogService.log(…)` 记录 operator/目标/变更前后/IP。

- **菜单**:`menu` 表新增「问卷管理」菜单项(走角色菜单权限),未建目录则一并补父级。

- **错误返回**:统一 `Result`(`code/msg/data`)。

-–

## 6. 待办 / 决策点

- [ ] 迁移文件 V5:类型/问卷/题目/作答 4 张表的 DDL(需手动执行到 `basic` 库)

- [ ] 实体 + Service + Controller 分模块实现

- [ ] `type` 用**字符串题型码**(可扩展),非数字枚举 — 已按此设计

- [ ] `options` 用 JSON 字符串存储,不同题型的结构约定见 question 表;需定解析细节

- [ ] `ref`(关联/跳题):**业务链路后端需最小解析**(用于计分与参与统计),纯展示由 `style` 透传。解析口径见「计分与跳题口径」— 已按此设计

- [ ] `style`(前端样式):仅存储透传、后端不解析,结构由前端商定

- [ ] 确认导出格式偏好(Excel 依赖引入 或 纯 CSV 零依赖)— 项目偏轻,倾向 CSV 优先

- [ ] 媒体类上传:文件落盘目录 / 是否走复用评论图片接口或独立上传接口 — 待定

- [ ] `purge` 需遍历该问卷答题媒体文件并 `Files.delete`,落盘目录需可配置

- [ ] 复制问卷:深拷贝题目(含 options/ref/style),并 `sort` 归 `0` 置草稿,不含作答

- [ ] 计价(hasScore/score)后端是否要做总分汇总接口 — 默认仅存储,本期可不做总分互斥校验

-–

## 7. 与现有模块的边界

- 与私信 `mail`、广播 `notice` 的关系:**互不影响**。问卷作答数据独立入 `answer_record/answer_detail`,不并入广播/私信。

- 若后续要做"同分类批量推送问卷",可复用 `notice` 的类别已读机制,但**本期不做**

您好!

关于您反馈的问题,建议您点击 Qoder 界面右上角的「问题上报」按钮进行提交,并发送下反馈码。我们会尽快分析并排查处理。感谢您的支持!