[M-QUIZ-01] 冻结 choice、fill、judge 跨端契约并完善 fill 题型 #339

Closed
opened 2026-06-23 20:28:12 +08:00 by wangdl · 1 comment
Owner

类型 / 标签

backend ios contract quiz p1

依赖

M-QUIZ-01-01(Attempt 创建后才能验证契约)

目标

为当前真实存在的三种题型建立唯一、明确的跨端格式,并补齐 fill 的判分和结果展示。

真实题型

choice
fill
judge

不得新增其他题型。

契约

为每种题型冻结:

模型输出格式
数据库格式
API 格式
Swift Codable 格式
用户答案格式
正确答案格式
判分规则
结果展示规则

choice

确认:

  • 单选交互
  • 选项稳定标识
  • 只能选择一个答案
  • 服务端判分
  • 结果显示用户选择、正确选择和解析

judge

确认统一使用一种规范值:"true" / "false"(字符串)

不得在不同层混用:

Boolean
"true"/"false"
"正确"/"错误"

允许展示层本地化,但传输和存储必须统一。

fill

冻结规范化规则,至少评估:

  • 去除首尾空白
  • 连续空白规范化
  • 是否忽略大小写
  • Unicode 规范化
  • 中文标点与英文标点
  • 是否允许多个可接受答案

不要实现复杂语义相似度或 AI 判分。

结果页必须显示:

用户答案
正确答案
是否正确
解析

类型安全

优先增加应用层类型:

TypeScript union / enum
Swift enum
Validator allowlist

本 Issue 原则上不修改 Prisma enum、不新增 Migration。

数据库历史非法 type 必须 fail-closed,不得自动按 choice 处理。

验收标准

  • 三种题型契约文档完成
  • 后端 Validator 使用相同枚举
  • Swift 使用相同 Codable 枚举
  • fill 判分规则确定并测试
  • fill 结果页展示完整
  • judge 格式跨端一致(传输/存储统一为 "true"/"false"
  • 非法 type 被拒绝(fail-closed)
  • 相关测试 0 failed

建议执行顺序

#4 — 依赖 Issue 01。可与 Issue 03 并行推进。

## 类型 / 标签 `backend` `ios` `contract` `quiz` `p1` ## 依赖 M-QUIZ-01-01(Attempt 创建后才能验证契约) ## 目标 为当前真实存在的三种题型建立唯一、明确的跨端格式,并补齐 fill 的判分和结果展示。 ## 真实题型 ```text choice fill judge ``` 不得新增其他题型。 ## 契约 为每种题型冻结: ```text 模型输出格式 数据库格式 API 格式 Swift Codable 格式 用户答案格式 正确答案格式 判分规则 结果展示规则 ``` ## choice 确认: - 单选交互 - 选项稳定标识 - 只能选择一个答案 - 服务端判分 - 结果显示用户选择、正确选择和解析 ## judge 确认统一使用一种规范值:`"true"` / `"false"`(字符串) 不得在不同层混用: ```text Boolean "true"/"false" "正确"/"错误" ``` 允许展示层本地化,但传输和存储必须统一。 ## fill 冻结规范化规则,至少评估: - 去除首尾空白 - 连续空白规范化 - 是否忽略大小写 - Unicode 规范化 - 中文标点与英文标点 - 是否允许多个可接受答案 **不要实现复杂语义相似度或 AI 判分。** 结果页必须显示: ```text 用户答案 正确答案 是否正确 解析 ``` ## 类型安全 优先增加应用层类型: ```text TypeScript union / enum Swift enum Validator allowlist ``` 本 Issue 原则上不修改 Prisma enum、不新增 Migration。 数据库历史非法 type 必须 fail-closed,不得自动按 choice 处理。 ## 验收标准 - [ ] 三种题型契约文档完成 - [ ] 后端 Validator 使用相同枚举 - [ ] Swift 使用相同 Codable 枚举 - [ ] fill 判分规则确定并测试 - [ ] fill 结果页展示完整 - [ ] judge 格式跨端一致(传输/存储统一为 `"true"/"false"`) - [ ] 非法 type 被拒绝(fail-closed) - [ ] 相关测试 0 failed ## 建议执行顺序 #4 — 依赖 Issue 01。可与 Issue 03 并行推进。
wangdl added this to the M-QUIZ-01 Quiz 答题闭环与跨端契约收口 milestone 2026-06-23 20:28:12 +08:00
wangdl added the
priority:p1
type:feature
area:quiz
work:contract
backend
labels 2026-06-23 20:28:12 +08:00
Author
Owner

开发完成评论

完成内容

1. 创建共享契约模块 quiz-type.contract.ts

  • TypeScript QuestionType union 类型("choice" | "fill" | "judge"
  • isValidQuestionType() / assertValidQuestionType() — Validator allowlist
  • isValidJudgeValue() — judge 值校验(仅接受字符串 "true" / "false"
  • normalizeFillAnswer() — fill 规范化规则:trim、空白归一、Unicode NFC、中文标点→英文、标点邻接空格剥离、ASCII 大小写 insensitive
  • gradeFillAnswer() / gradeChoiceAnswer() / gradeJudgeAnswer() — 按题型判分
  • gradeAnswer() — 通用分发入口(非法 type → fail-closed 抛错)

2. 集成到 submit 判分

  • quiz.service.ts:submit() 使用 gradeAnswer() 替代直接 === 比较
  • fill 题型享受规范化判分(空白/大小写/标点差异不判错)
  • 非法 question type → BadRequestException

3. 修复非法 type 静默降级

  • quiz-generation-projector.ts:107 — 非法 type 记录 error 日志(fail-closed)
  • runtime-internal.service.ts:486 — 非法 type → 直接抛 BadRequestException
  • quiz-generation-validator.ts — 使用共享契约 VALID_QUESTION_TYPES + isValidJudgeValue

4. 创建 Swift 契约枚举

  • QuizQuestionType.swiftenum QuizQuestionType: String, Codable
  • displayAnswer(raw:options:) — choice 索引→选项文本
  • displayName — 本地化展示名

5. 测试

  • quiz-type.contract.spec.ts — 30 tests(类型校验 + fill 规范化 + 判分分发)
  • quiz.service.spec.ts — 新增 fill 规范化判分 + 非法 type fail-closed 测试

修改文件

  • src/modules/quiz/quiz-type.contract.ts(新建)
  • src/modules/quiz/quiz-type.contract.spec.ts(新建)
  • src/modules/quiz/quiz.service.ts — 集成 gradeAnswer
  • src/modules/quiz/quiz.service.spec.ts — +3 tests
  • src/modules/ai-job/quiz-generation-validator.ts — 使用共享契约
  • src/modules/ai-job/quiz-generation-projector.ts — fail-closed
  • src/modules/ai-runtime/internal/runtime-internal.service.ts — fail-closed
  • ios-projects/.../QuizQuestionType.swift(新建)

测试情况

  • API: 9 suites / 178 tests / 0 failed
  • iOS: xcodebuild ... buildBUILD SUCCEEDED

契约摘要

维度 choice fill judge
答案格式 选项索引字符串 自由文本 "true" / "false"
判分规则 精确匹配 规范化后匹配 精确匹配
非法 type fail-closed fail-closed fail-closed
跨端枚举 TS union + Swift enum TS union + Swift enum TS union + Swift enum

是否建议进入 Review

## 开发完成评论 ### 完成内容 #### 1. 创建共享契约模块 `quiz-type.contract.ts` - TypeScript `QuestionType` union 类型(`"choice" | "fill" | "judge"`) - `isValidQuestionType()` / `assertValidQuestionType()` — Validator allowlist - `isValidJudgeValue()` — judge 值校验(仅接受字符串 `"true"` / `"false"`) - `normalizeFillAnswer()` — fill 规范化规则:trim、空白归一、Unicode NFC、中文标点→英文、标点邻接空格剥离、ASCII 大小写 insensitive - `gradeFillAnswer()` / `gradeChoiceAnswer()` / `gradeJudgeAnswer()` — 按题型判分 - `gradeAnswer()` — 通用分发入口(非法 type → fail-closed 抛错) #### 2. 集成到 submit 判分 - `quiz.service.ts:submit()` 使用 `gradeAnswer()` 替代直接 `===` 比较 - fill 题型享受规范化判分(空白/大小写/标点差异不判错) - 非法 question type → `BadRequestException` #### 3. 修复非法 type 静默降级 - `quiz-generation-projector.ts:107` — 非法 type 记录 error 日志(fail-closed) - `runtime-internal.service.ts:486` — 非法 type → 直接抛 `BadRequestException` - `quiz-generation-validator.ts` — 使用共享契约 `VALID_QUESTION_TYPES` + `isValidJudgeValue` #### 4. 创建 Swift 契约枚举 - `QuizQuestionType.swift` — `enum QuizQuestionType: String, Codable` - `displayAnswer(raw:options:)` — choice 索引→选项文本 - `displayName` — 本地化展示名 #### 5. 测试 - `quiz-type.contract.spec.ts` — 30 tests(类型校验 + fill 规范化 + 判分分发) - `quiz.service.spec.ts` — 新增 fill 规范化判分 + 非法 type fail-closed 测试 ### 修改文件 - `src/modules/quiz/quiz-type.contract.ts`(新建) - `src/modules/quiz/quiz-type.contract.spec.ts`(新建) - `src/modules/quiz/quiz.service.ts` — 集成 `gradeAnswer` - `src/modules/quiz/quiz.service.spec.ts` — +3 tests - `src/modules/ai-job/quiz-generation-validator.ts` — 使用共享契约 - `src/modules/ai-job/quiz-generation-projector.ts` — fail-closed - `src/modules/ai-runtime/internal/runtime-internal.service.ts` — fail-closed - `ios-projects/.../QuizQuestionType.swift`(新建) ### 测试情况 - API: **9 suites / 178 tests / 0 failed** ✅ - iOS: `xcodebuild ... build` → **BUILD SUCCEEDED** ✅ ### 契约摘要 | 维度 | choice | fill | judge | |------|--------|------|-------| | 答案格式 | 选项索引字符串 | 自由文本 | `"true"` / `"false"` | | 判分规则 | 精确匹配 | 规范化后匹配 | 精确匹配 | | 非法 type | fail-closed | fail-closed | fail-closed | | 跨端枚举 | TS union + Swift enum | TS union + Swift enum | TS union + Swift enum | ### 是否建议进入 Review - **是**
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#339
No description provided.