# 05 题库与问卷（for AI Agents）

> 关联代码：internal/model/questionnaire.go、internal/service/questionnaire/、internal/handler/{mp,admin}/questionnaire*.go
> 管理端页面：web/src/views/questionnaire/；小程序页面：pages/question/*、pages/questionnaire/*

====================================================================
1. 功能概述
====================================================================

题库（Question）+ 组卷（Questionnaire）两级模型，用于两类场景：
- 参与门槛：抽奖绑定问卷，用户参与前必须提交（cfg.need_questionnaire，见 03 的资格链）。
- 独立调研：发起人自建问卷收集信息。

双端归属规则（重要）：题库与问卷带 OwnerType（mp/admin）。
- mp 端（小程序发起人）：仅能看到和管理自己创建的（CreatorID 过滤），题目进入「个人题库」。
- admin 端（后台）：共享池，后台创建的题目/问卷可被任何后台管理员使用；归属分流在 service 层按 CreatorID=0 与否判定。

====================================================================
2. 底层逻辑
====================================================================

2.1 模型
- Question：题目。题型 question_type = single（单选）/ multi（多选）/ text（填空）；Options JSON 选项数组；必答标志。
- Questionnaire：问卷。标题 + 描述 + QuestionnaireItem（有序题目关联，排序号决定展示顺序）。
- QuestionnaireSubmission：答卷。唯一索引（lottery_id + mp_user_id）——每用户每抽奖只能提交一份；Answers JSON 存答案。

2.2 绑定校验三入口（抽奖绑定问卷后，防止删除被引用的题/卷）
- 删除题目：被任何问卷引用 → 拒绝（后台题库页提示）。
- 删除问卷：被任何抽奖绑定 → 拒绝。
- 绑定抽奖：后台/小程序表单选择问卷时校验问卷存在且非空。

2.3 参与闭环
- 用户参与抽奖（join）时若 cfg.need_questionnaire 且无提交记录 → 2111「请先填写问卷后再参与」；抽奖绑定的问卷不存在/无题 → 2125「活动问卷配置异常，请联系发起人」（CheckJoinConditions 判定）。
- 小程序详情页参与按钮文案变为「填写问卷后参与」；填卷页提交成功 navigateBack 回详情页，onShow 刷新 questionnaire status（GET /mp/lottery/:id/questionnaire/status）。
- 注意：Lottery 模型存在 need_quiz 字样的旧字段/配置，是死配置不生效；真正生效的是 cfg.need_questionnaire + 绑定记录。

2.4 数据消费
- 后台暂无答卷统计/导出页面；答卷数据在 QuestionnaireSubmission 表，运营分析需直查库。

====================================================================
3. 后台操作（问卷管理菜单）
====================================================================

3.1 题库管理 /questionnaire/questions（views/questionnaire/questions.vue）
- 筛选：关键词。
- 表格列：ID/题目/类型/排序/操作（题型标签：single 单选/multi 多选/text 填空、选项预览、必答标记）。
- 「新建/编辑」弹窗：题型、标题、必答开关、选项 A/B/…（可增删，至少 2 项；text 题无需选项）。
- 「删除」：被问卷引用的题目不可删（后端拒绝）。

3.2 问卷管理 /questionnaire/questionnaires（views/questionnaire/questionnaires.vue）
- 筛选：关键词。
- 表格列：ID/标题/题目数/操作。
- 行操作：编辑、删除。
- 「新建/编辑」弹窗：标题、描述、题目列表（上移/下移/移除）+「从题库添加」多选弹窗（勾选表格+分页）。
- 「删除」：被抽奖绑定的问卷不可删。

3.3 绑定入口
- 后台抽奖表单（/lotteries/create|edit）：问卷门槛开关 + 选择问卷（QuestionnaireSelectModal）。
- 小程序创建页同样有问卷门槛配置。小程序发起人创建的活动（creator_id>0）后台不可改其问卷绑定。

3.4 后台操作逻辑备注（隐藏约束，UI 不直接体现）
- 题目删除引用检查：删除题目时后端检查是否被任何问卷的 QuestionnaireItem 引用；有引用则拒绝删除（返回错误），需先从所有引用该题目的问卷中移除。
- 问卷删除绑定检查：删除问卷时后端检查是否被任何抽奖的 cfg.need_questionnaire 绑定；有绑定则拒绝删除，需先解除所有关联抽奖的问卷门槛。
- 答卷不可修改：QuestionnaireSubmission 唯一索引（lottery_id + mp_user_id）确保每用户每抽奖一份；提交后不可修改、不可重填；修改问卷题目不影响已提交的答卷。
- 题库/问卷双端隔离：mp 端（CreatorID>0）只能看到自己创建的；admin 端（CreatorID=0）为共享池。两端数据互不可见，不可跨端操作（用后台接口改小程序用户的问卷会被属主校验拒绝）。

====================================================================
4. 小程序端操作
====================================================================

发起人侧（管理自己的题库/问卷）：
- pages/question/bank/index：题库列表 + 搜索
- pages/question/edit/index：题目编辑（单选/多选/填空）
- pages/questionnaire/list/index：问卷列表
- pages/questionnaire/edit/index：问卷编辑（从题库选题组装、排序）

参与者侧（填卷）：
- pages/questionnaire/fill/index：?lotteryId= 进入；渲染 single（单选）/multi（多选）/text（填空）；必答校验未过不可提交；成功后 navigateBack 回抽奖详情页。

====================================================================
5. API 速查
====================================================================

小程序端（/api/mp，JWT；题库问卷仅本人数据）：
GET|POST|PUT|DELETE /api/mp/questions(:id)           个人题库 CRUD
GET|POST|PUT|DELETE /api/mp/questionnaires(:id)      个人问卷 CRUD
GET  /api/mp/lottery/:id/questionnaire               参与前获取绑定的问卷（题目结构）
GET  /api/mp/lottery/:id/questionnaire/status        我是否已提交
POST /api/mp/lottery/:id/questionnaire/submit        提交答卷（唯一索引幂等）

管理端（/api/admin，共享池）：
GET|POST|PUT|DELETE /api/admin/questions(:id)
GET|POST|PUT|DELETE /api/admin/questionnaires(:id)

====================================================================
6. 注意事项
====================================================================

- 2111/2125 是排查「参与被拒」的高频码：2111=未填卷（正常引导），2125=配置异常（发起人绑定问题，需修数据或联系发起人）。
- 每用户每抽奖一份答卷是硬约束（DB 唯一索引），重复提交被拒；修改问卷题目不会让已提交者重新填写。
- mp 端与 admin 端的数据隔离靠 OwnerType/CreatorID 分流，后台共享池与个人题库互不可见；不要用后台接口去改小程序用户的问卷（属主校验会拒绝）。
- need_quiz 相关字段为历史死配置，任何新需求都应使用 cfg.need_questionnaire + 问卷绑定。
