# 后勤满意度调查API文档 ## 文档信息 - **版本**:1.0.0 - **最后更新**:2025-07-01 - **作者**:Cursor AI - **分类**:后勤管理模块 ## 概述 本文档详细说明后勤满意度调查系统的API接口设计,包括问卷管理、题目管理、选项管理、答题记录等功能。系统采用前后端分离架构,接口采用RESTful API风格设计,所有接口均需通过Token进行身份验证。 ## 通用响应格式 ### 成功响应 ```json { "code": 200, "msg": "操作成功", "data": {} // 响应数据,具体结构取决于不同接口 } ``` ### 分页数据响应 ```json { "total": 100, // 总记录数 "rows": [], // 当前页记录列表 "code": 200, // 状态码 "msg": "查询成功" // 提示信息 } ``` ### 错误响应 ```json { "code": 500, // 错误码 "msg": "操作失败", // 错误消息 "data": null // 通常为空 } ``` ## 认证与授权 所有接口均需要在请求头中携带Token进行身份验证: ``` Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` ## 接口清单 | 接口路径 | 方法 | 描述 | 权限标识 | |---------|-----|------|---------| | `/hq/satSurvey/list` | GET | 查询问卷列表 | hq:satSurvey:list | | `/hq/satSurvey/{id}` | GET | 获取问卷详情 | hq:satSurvey:query | | `/hq/satSurvey` | POST | 新增问卷 | hq:satSurvey:add | | `/hq/satSurvey` | PUT | 修改问卷 | hq:satSurvey:edit | | `/hq/satSurvey/{ids}` | DELETE | 删除问卷 | hq:satSurvey:remove | | `/hq/satSurvey/publish/{id}` | PUT | 发布问卷 | hq:satSurvey:publish | | `/hq/satQuestion/list` | GET | 查询题目列表 | hq:satQuestion:list | | `/hq/satQuestion/{id}` | GET | 获取题目详情 | hq:satQuestion:query | | `/hq/satQuestion` | POST | 新增题目 | hq:satQuestion:add | | `/hq/satQuestion` | PUT | 修改题目 | hq:satQuestion:edit | | `/hq/satQuestion/{ids}` | DELETE | 删除题目 | hq:satQuestion:remove | | **`/hq/satQuestion/{questionId}/options`** | GET | 获取题目的选项列表 | hq:satQuestion:query | | **`/hq/satOption/list`** | GET | 查询题目选项列表 | hq:satOption:list | | **`/hq/satOption/{id}`** | GET | 获取题目选项详情 | hq:satOption:query | | **`/hq/satOption`** | POST | 新增题目选项 | hq:satOption:add | | **`/hq/satOption`** | PUT | 修改题目选项 | hq:satOption:edit | | **`/hq/satOption/{ids}`** | DELETE | 删除题目选项 | hq:satOption:remove | | `/hq/satRecord/list` | GET | 查询答题记录列表 | hq:satRecord:list | | `/hq/satRecord/{id}` | GET | 获取答题记录详情 | hq:satRecord:query | | `/hq/satSurvey/{id}/statistics` | GET | 获取问卷统计分析 | hq:satSurvey:result | | `/hq/satSurvey/{id}/records` | GET | 获取问卷答题记录 | hq:satSurvey:result | | `/hq/satSurvey/{id}/exportReport` | POST | 导出调查报告 | hq:satSurvey:export | ## API详情 ### 题目选项管理API #### 1. 查询题目选项列表 **请求方式**:GET **请求路径**:`/hq/satOption/list` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | questionId | Long | 否 | 题目ID | | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | **请求示例**: ``` GET /hq/satOption/list?questionId=1937751489358733313&pageNum=1&pageSize=10 ``` **响应示例**: ```json { "total": 5, "rows": [ { "id": 1937810382736855041, "questionId": 1937751489358733313, "optionContent": "非常满意", "optionScore": 5, "optionOrder": 1, "isDefault": "N", "tenantId": "000000", "createBy": 1, "createTime": "2025-06-25 17:50:18", "updateBy": 1, "updateTime": "2025-06-25 17:50:18", "deleted": "0" }, { "id": 1937810382736855042, "questionId": 1937751489358733313, "optionContent": "满意", "optionScore": 4, "optionOrder": 2, "isDefault": "N", "tenantId": "000000", "createBy": 1, "createTime": "2025-06-25 17:50:18", "updateBy": 1, "updateTime": "2025-06-25 17:50:18", "deleted": "0" } // ... 更多选项数据 ], "code": 200, "msg": "查询成功" } ``` #### 2. 获取题目的选项列表 **请求方式**:GET **请求路径**:`/hq/satQuestion/{questionId}/options` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | questionId | Long | 是 | 题目ID | **请求示例**: ``` GET /hq/satQuestion/1937751489358733313/options ``` **响应示例**: ```json { "code": 200, "msg": "操作成功", "data": [ { "id": 1937810382736855041, "questionId": 1937751489358733313, "optionContent": "非常满意", "optionScore": 5, "optionOrder": 1, "isDefault": "N", "tenantId": "000000", "createBy": 1, "createTime": "2025-06-25 17:50:18", "updateBy": 1, "updateTime": "2025-06-25 17:50:18", "deleted": "0" }, { "id": 1937810382736855042, "questionId": 1937751489358733313, "optionContent": "满意", "optionScore": 4, "optionOrder": 2, "isDefault": "N", "tenantId": "000000", "createBy": 1, "createTime": "2025-06-25 17:50:18", "updateBy": 1, "updateTime": "2025-06-25 17:50:18", "deleted": "0" }, // ... 更多选项数据 ] } ``` **前端使用说明**: - 此接口返回指定题目的全部选项,数据结构为数组,不分页 - **实现变更(2025-07-17):** - 接口现在根据questionId参数过滤选项,只返回特定题目的选项数据 - 返回的选项数据已按照optionOrder字段升序排序,无需前端再次排序 - 适用于问题选择界面或答题场景,可一次性获取题目所有选项 - 对于单选题和多选题应显示选项,对于问答题则不需要显示 #### 3. 获取题目选项详情 **请求方式**:GET **请求路径**:`/hq/satOption/{id}` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | id | Long | 是 | 选项ID | **请求示例**: ``` GET /hq/satOption/1937810382736855041 ``` **响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "id": 1937810382736855041, "questionId": 1937751489358733313, "optionContent": "非常满意", "optionScore": 5, "optionOrder": 1, "isDefault": "N", "tenantId": "000000", "createBy": 1, "createTime": "2025-06-25 17:50:18", "updateBy": 1, "updateTime": "2025-06-25 17:50:18", "deleted": "0" } } ``` #### 4. 新增题目选项 **请求方式**:POST **请求路径**:`/hq/satOption` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | questionId | Long | 是 | 题目ID | | optionContent | String | 是 | 选项内容 | | optionScore | Integer | 否 | 选项分值,默认0 | | optionOrder | Integer | 否 | 选项排序,默认0 | | isDefault | String | 否 | 是否默认选项(Y是 N否),默认N | **请求示例**: ```json { "questionId": 1937751489358733313, "optionContent": "一般般", "optionScore": 3, "optionOrder": 3, "isDefault": "N" } ``` **响应示例**: ```json { "code": 200, "msg": "操作成功", "data": null } ``` #### 5. 修改题目选项 **请求方式**:PUT **请求路径**:`/hq/satOption` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | id | Long | 是 | 选项ID | | questionId | Long | 是 | 题目ID | | optionContent | String | 是 | 选项内容 | | optionScore | Integer | 否 | 选项分值 | | optionOrder | Integer | 否 | 选项排序 | | isDefault | String | 否 | 是否默认选项(Y是 N否) | **请求示例**: ```json { "id": 1937810382736855041, "questionId": 1937751489358733313, "optionContent": "非常满意", "optionScore": 5, "optionOrder": 1, "isDefault": "Y" } ``` **响应示例**: ```json { "code": 200, "msg": "操作成功", "data": null } ``` #### 6. 删除题目选项 **请求方式**:DELETE **请求路径**:`/hq/satOption/{ids}` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | ids | String | 是 | 选项ID,多个用逗号分隔 | **请求示例**: ``` DELETE /hq/satOption/1937810382736855041,1937810382736855042 ``` **响应示例**: ```json { "code": 200, "msg": "操作成功", "data": null } ``` ## 最佳实践 ### 题目选项批量管理 虽然当前API不直接支持选项批量操作,但前端可以通过组合使用现有API实现选项的批量管理,步骤如下: 1. **加载选项**:使用 `GET /hq/satOption/list?questionId={questionId}` 获取题目当前所有选项 2. **处理更新**: - 对于有ID的选项,调用 `PUT /hq/satOption` 更新内容 - 对于新增的选项,调用 `POST /hq/satOption` 创建 - 对比前后选项ID差异,对不存在的选项调用 `DELETE /hq/satOption/{ids}` 删除 这种实现方式允许在一次操作中完成多个选项的增删改,但需注意以下几点: - 操作非事务性:多个API调用之间没有事务保证,如遇网络问题可能导致数据部分更新 - 性能考虑:选项数量大时,多次API调用可能影响性能 - 字段映射:注意前后端字段命名差异(如 `optionContent` → `optionText`) **示例代码**: ```javascript // 1. 获取当前题目的所有已有选项 const res = await listSatOption({ questionId: questionId }); const existingOptions = res.rows || []; // 2. 处理选项数据 const promises = []; // 新增或修改选项 for (const option of options) { if (!option.optionContent) continue; // 跳过空选项 const params = { id: option.id, questionId: questionId, optionText: option.optionContent, optionValue: option.optionScore || 0, sortOrder: option.optionOrder || 1, status: '0', isDefault: option.isDefault || 'N' }; if (option.id) { // 已有选项,执行更新 promises.push(updateSatOption(params)); } else { // 新选项,执行新增 promises.push(addSatOption(params)); } } // 删除不再存在的选项 const currentOptionIds = options.filter(opt => opt.id).map(opt => opt.id); const optionsToDelete = existingOptions .filter(opt => !currentOptionIds.includes(opt.id)) .map(opt => opt.id); if (optionsToDelete.length > 0) { promises.push(delSatOption(optionsToDelete)); } // 3. 等待所有操作完成 await Promise.all(promises); ``` **建议改进**:后续可考虑在后端添加批量操作API,如 `POST /hq/satOption/batch` 以提供事务保证和性能优化。 ## 数据结构说明 ### 题目选项表(hq_sat_option) | 字段名 | 类型 | 必填 | 描述 | |-------|-----|------|------| | id | bigint(20) | 是 | 选项ID,主键(雪花ID) | | question_id | bigint(20) | 是 | 题目ID,关联题目表 | | option_content | varchar(255) | 是 | 选项内容 | | option_score | int(11) | 否 | 选项分值,用于计算满意度评分 | | option_order | int(11) | 否 | 选项排序,控制前端显示顺序 | | is_default | char(1) | 否 | 是否默认选项(Y是 N否) | | tenant_id | varchar(20) | 是 | 租户编号 | | create_by | bigint(20) | 否 | 创建者 | | create_time | datetime | 否 | 创建时间 | | update_by | bigint(20) | 否 | 更新者 | | update_time | datetime | 否 | 更新时间 | | deleted | char(1) | 否 | 删除标志(0代表存在 1代表删除) | ## 错误码说明 | 错误码 | 描述 | 解决方案 | |-------|-----|---------| | 200 | 操作成功 | 请求正常处理完毕 | | 401 | 认证失败 | 检查用户Token是否正确 | | 403 | 权限不足 | 检查用户是否有操作权限 | | 404 | 资源不存在 | 检查请求的资源是否存在 | | 500 | 系统内部错误 | 联系系统管理员 | ## 变更记录 | 版本号 | 更新日期 | 更新内容 | 更新人 | |-------|---------|---------|-------| | 1.0.0 | 2025-07-01 | 初始版本:创建满意度调查API文档 | Cursor | | 1.0.0 | 2025-07-01 | 更新题目选项表(hq_sat_option)结构,完善选项管理API | Cursor | | 1.0.1 | 2025-07-17 | 优化题目选项获取接口,增强前端题目选项显示功能 | Cursor | | 1.0.2 | 2025-07-17 | 修复选项排序值保存后实时更新功能,优化用户体验 | wuzilong | | 1.0.3 | 2025-07-17 | 添加选项保存防重复提交机制,解决幂等性控制冲突 | wuzilong | | 1.0.4 | 2025-07-18 | 优化问答题文本回答展示功能,提高Vue渲染稳定性 | wuzilong | | 1.0.5 | 2025-07-18 | 增强问卷答题记录接口功能,返回答题详情数据 | wuzilong | ## 附录 ### 选项分值计算说明 选项分值(option_score)用于计算满意度评分,在统计分析时使用,具体规则如下: 1. 单选题:直接使用所选选项的分值作为该题得分 2. 多选题:将所选所有选项的分值相加作为该题得分 3. 问答题:不计分,分值为0 满意度问卷总分计算方式: - 总分(total_score) = 所有题目的得分之和 - 平均分(avg_score) = 总分 ÷ 有分值的题目数量(不包括问答题) ### 问答题文本回答数据结构 **统计分析接口中问答题文本回答数据结构**: 问卷统计分析接口(`/hq/satSurvey/{id}/statistics`)返回的问答题文本回答数据结构如下: ```json "textAnswers": [ { "id": 393, "answer_text": "用户回答内容示例", "text_length": 12, "answer_time": "2025-07-18 10:40:35" } ] ``` **字段说明**: | 字段名 | 类型 | 描述 | |-------|-----|------| | id | Integer | 回答ID,唯一标识 | | answer_text | String | 回答文本内容 | | text_length | Integer | 文本长度(字符数) | | answer_time | String | 回答时间,格式:yyyy-MM-dd HH:mm:ss | **前端使用注意事项**: - 在Vue循环渲染textAnswers数组时,应使用answer.id作为key值,而非索引,确保Vue的虚拟DOM正确更新 - 示例:`