|
|
@@ -1,6 +1,6 @@
|
|
|
# 小程序词汇摸底后端能力设计
|
|
|
|
|
|
-> Status: 用户已确认设计方向,设计文档已写入,待用户审阅后进入实施计划。
|
|
|
+> Status: 设计已按用户采访结论修订,正在完成自审;自审后进入实施计划。
|
|
|
|
|
|
## 目标
|
|
|
|
|
|
@@ -12,10 +12,10 @@
|
|
|
2. 后端返回第 1 关词列表。
|
|
|
3. 每关展示 12 个英文单词,学生只勾选“不认识”的词。
|
|
|
4. 小程序提交本关不认识词。
|
|
|
-5. 后端根据本关 unknownRate 调整下一关中心词频和取词宽度。
|
|
|
+5. 后端根据所有已提交关卡估算能力边界 K,并结合当前关答题倒挂情况调整下一关中心词频;取词宽度逐关收敛。
|
|
|
6. 达到停止条件后生成报告,返回词汇量、K 值、能力等级和学习区间。
|
|
|
|
|
|
-初版运行环境假设:单机部署。session、level、word 明细使用内存保存;同一进程内支持断点恢复和重复提交幂等,服务重启后 session 丢失可接受。
|
|
|
+初版运行环境假设:单机部署。session、level、word 明细使用内存保存;同一进程内支持断点恢复;进行中的已提交关卡支持弱网重复提交幂等。session finished 后不再接受 submit,返回 `SURVEY_SESSION_FINISHED`。服务重启后 session/report 丢失可接受;报告分享传播和持久化存储另起设计。
|
|
|
|
|
|
## 当前项目上下文
|
|
|
|
|
|
@@ -40,7 +40,7 @@
|
|
|
|
|
|
## 词库来源
|
|
|
|
|
|
-词库不通过 RDS 表查询。
|
|
|
+词库不通过数据库表查询。
|
|
|
|
|
|
真实词库来自 `Qingti.Teaching.Content.Dictionary.EnglishDictionary` 加载的 MessagePack 二进制文件,现有 .NET 业务通过 `IEnglishDictionary` 查询:
|
|
|
|
|
|
@@ -51,25 +51,33 @@
|
|
|
本次 Java 微服务初版直接加载 MessagePack `dict.data`。用户授权从本机路径复制文件到当前项目:
|
|
|
|
|
|
```text
|
|
|
-源文件:F:\Codes\Projects\qingti.teaching.api\Qingti.Teaching.Dictionary\Resources\dict.data
|
|
|
+源文件:/Users/exiao/Codes/qingti.teaching.api/Qingti.Teaching.Dictionary/Resources/dict.data
|
|
|
目标文件:abilities/vocabulary-survey/infrastructure/src/main/resources/data/dict.data
|
|
|
```
|
|
|
|
|
|
-顶层 MessagePack 对象:
|
|
|
+已用真实 `dict.data` 验证顶层 MessagePack 对象结构:
|
|
|
|
|
|
```text
|
|
|
DictionaryObject
|
|
|
-├── words: DictionaryWord[]
|
|
|
-├── exchanges: DictionaryExchange[]
|
|
|
-└── meanings: DictionaryMeaning[]
|
|
|
+├── words: DictionaryWord[] 11992 条
|
|
|
+├── exchanges: DictionaryExchange[] 35980 条
|
|
|
+├── meanings: DictionaryMeaning[] 17082 条
|
|
|
+└── phrases: DictionaryPhrase[] 404461 条
|
|
|
```
|
|
|
|
|
|
-小程序摸底选词优先基于 `wordFrequency`,核心字段为:
|
|
|
+小程序摸底选词优先基于 `wordFrequency`。真实词库中 `wordFrequency` 是近似 rank/难度序号:越小越高频、越基础;越大越低频、越难。真实范围为 `1..11993`,其中 `5212` 缺失,因此实现必须支持“按词频附近查找”,不能假设每个词频值都有单词。
|
|
|
+
|
|
|
+`words` 核心字段为:
|
|
|
|
|
|
- `wordId`
|
|
|
- `spell`
|
|
|
- `wordFrequency`
|
|
|
-- `meaningId`
|
|
|
+
|
|
|
+`words` 不直接包含 `meaningId`。`meaningId` 来自 `meanings`,通过 `meanings.wordId` 与 `words.wordId` 关联。一个单词可能有多个释义;初版接口不返回 `meaningId`,但加载索引时仍使用 `meanings` 过滤掉没有任何释义的 29 个词,避免返回无释义词。本轮不直接修改二进制 `dict.data`;如果后续要清理源词库文件,应作为词库发布/校验流程单独处理。
|
|
|
+
|
|
|
+词汇摸底是必需能力。`dict.data` 缺失、损坏或 MessagePack 结构无法解析时,应让 Spring context 启动失败,以便发布或运维阶段尽早暴露配置/资源问题,而不是在运行时静默降级。
|
|
|
+
|
|
|
+`phrases` 初版摸底不使用,不需要为 40 万条 phrase 建业务索引。Java loader 初版优先使用 Jackson MessagePack 完整绑定 `DictionaryObject`,以降低实现风险;即使完整绑定读取了 `phrases`,业务索引仍只建立 `words` 和 `meanings` 所需数据。若验证发现启动慢或内存占用过高,再改用 `msgpack-core` 流式/低层解析并跳过 `phrases`。
|
|
|
|
|
|
实现时允许新增 MessagePack 解析依赖,优先尝试:
|
|
|
|
|
|
@@ -80,7 +88,7 @@ DictionaryObject
|
|
|
</dependency>
|
|
|
```
|
|
|
|
|
|
-如果真实文件结构不适合 Jackson 绑定,再改用 `org.msgpack:msgpack-core` 做低层解析。
|
|
|
+如果真实文件结构不适合 Jackson 绑定,或完整绑定带来不可接受的启动耗时/内存占用,再改用 `org.msgpack:msgpack-core` 做低层解析。
|
|
|
|
|
|
## 方案选择
|
|
|
|
|
|
@@ -170,9 +178,7 @@ DTO 使用小程序期望的 JSON 字段:
|
|
|
- `level`
|
|
|
- `currentLevel`
|
|
|
- `report`
|
|
|
-- `surveyWordId`
|
|
|
- `wordId`
|
|
|
-- `meaningId`
|
|
|
- `spell`
|
|
|
- `wordFrequency`
|
|
|
|
|
|
@@ -203,7 +209,7 @@ domain 不依赖 contracts、Spring、Jackson、MessagePack。
|
|
|
- `StartVocabularySurveyCommand`
|
|
|
- `SubmitVocabularySurveyLevelCommand`
|
|
|
- `VocabularySurveyResultMapper`
|
|
|
-- session 过期和 restart 编排
|
|
|
+- session 过期编排
|
|
|
|
|
|
application 依赖 domain,返回 contracts DTO 或 application result。为了贴合现有项目风格,初版可由 application service 直接返回 contracts response,但 domain 仍不依赖 contracts。
|
|
|
|
|
|
@@ -217,15 +223,17 @@ application 依赖 domain,返回 contracts DTO 或 application result。为了
|
|
|
- `DictionaryWord`
|
|
|
- `DictionaryMeaning`
|
|
|
- `DictionaryExchange`
|
|
|
+- `DictionaryPhrase`(如解析库要求完整绑定;业务上可忽略)
|
|
|
- `InMemoryVocabularySurveySessionRepository`
|
|
|
|
|
|
`MessagePackVocabularyWordProvider` 启动时加载 `classpath:data/dict.data`,建立索引:
|
|
|
|
|
|
- `wordId -> VocabularyWord`
|
|
|
-- `meaningId -> VocabularyWord` 或 `meaningId -> DictionaryMeaning + word`
|
|
|
-- `wordFrequency -> List<VocabularyWord>`
|
|
|
+- `wordFrequency -> VocabularyWord`
|
|
|
- `sortedByFrequency`
|
|
|
|
|
|
+加载索引时根据 `meanings.wordId` 过滤没有任何 meaning 的单词;初版 API 不返回 `meaningId`,也不需要建立 `meaningId -> VocabularyWord` 业务查询索引。
|
|
|
+
|
|
|
## 词库 port 设计
|
|
|
|
|
|
业务算法不直接读取 `dict.data`,也不关心 MessagePack 结构。domain 定义一个词库查询接口,例如:
|
|
|
@@ -241,8 +249,6 @@ public interface VocabularyWordProvider {
|
|
|
|
|
|
Optional<VocabularyWord> findByWordId(int wordId);
|
|
|
|
|
|
- Optional<VocabularyWord> findByMeaningId(int meaningId);
|
|
|
-
|
|
|
int maxWordFrequency();
|
|
|
}
|
|
|
```
|
|
|
@@ -252,7 +258,6 @@ domain 使用的单词模型:
|
|
|
```java
|
|
|
public record VocabularyWord(
|
|
|
int wordId,
|
|
|
- int meaningId,
|
|
|
String spell,
|
|
|
int wordFrequency) {
|
|
|
}
|
|
|
@@ -267,29 +272,27 @@ public record VocabularyWord(
|
|
|
|
|
|
## HTTP 接口
|
|
|
|
|
|
-接口路径按需求实现,不额外加 `/api` 前缀:
|
|
|
+接口路径统一使用 `/api/vocabulary-survey` 前缀,与现有后端 controller 风格保持一致:
|
|
|
|
|
|
### 开始摸底
|
|
|
|
|
|
```text
|
|
|
-POST /mini-program/vocabulary-survey/start
|
|
|
+POST /api/vocabulary-survey/start
|
|
|
```
|
|
|
|
|
|
请求:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
- "studentId": 123,
|
|
|
- "grade": 7,
|
|
|
- "restart": false
|
|
|
+ "grade": 7
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-`studentId` 在当前无登录态时用于幂等恢复;如果缺失,可用匿名 session 策略,但推荐小程序传入。
|
|
|
+初版不传 `studentId`,也不提供 `restart`/`forceNew` 机制。每次开始摸底都会创建一个新的匿名 session。小程序端负责可靠保存未完成摸底的 `sessionId`;如果本地已有未完成 `sessionId`,前端不应再次调用 start,而应调用查询接口恢复当前关。
|
|
|
|
|
|
响应:
|
|
|
|
|
|
-以下响应示例中的 `surv_xxx`、`lvl_xxx`、`sw_xxx` 是示例 ID,不是未决内容;实现时使用对应前缀生成真实 ID。
|
|
|
+以下响应示例中的 `surv_xxx`、`lvl_xxx` 是示例 ID,不是未决内容;实现时使用对应前缀生成真实 ID。
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
@@ -303,9 +306,7 @@ POST /mini-program/vocabulary-survey/start
|
|
|
"feedback": "先来热个身,选出你不认识的单词吧",
|
|
|
"words": [
|
|
|
{
|
|
|
- "surveyWordId": "sw_xxx",
|
|
|
"wordId": 101,
|
|
|
- "meaningId": 2001,
|
|
|
"spell": "happy",
|
|
|
"wordFrequency": 403
|
|
|
}
|
|
|
@@ -317,15 +318,14 @@ POST /mini-program/vocabulary-survey/start
|
|
|
### 提交某一关
|
|
|
|
|
|
```text
|
|
|
-POST /mini-program/vocabulary-survey/levels/{levelId}/submit
|
|
|
+POST /api/vocabulary-survey/sessions/{sessionId}/levels/{levelId}/submit
|
|
|
```
|
|
|
|
|
|
请求:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
- "sessionId": "surv_xxx",
|
|
|
- "unknownSurveyWordIds": ["sw_001", "sw_002"]
|
|
|
+ "unknownWordIds": [101, 102]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -373,7 +373,7 @@ POST /mini-program/vocabulary-survey/levels/{levelId}/submit
|
|
|
### 查询 session 状态
|
|
|
|
|
|
```text
|
|
|
-GET /mini-program/vocabulary-survey/sessions/{sessionId}
|
|
|
+GET /api/vocabulary-survey/sessions/{sessionId}
|
|
|
```
|
|
|
|
|
|
进行中返回当前关,已完成返回报告。过期 session 初版返回业务错误码 `SURVEY_SESSION_EXPIRED`,避免前端继续提交旧 session。
|
|
|
@@ -402,33 +402,38 @@ GET /mini-program/vocabulary-survey/sessions/{sessionId}
|
|
|
```text
|
|
|
gradeMaxFrequency = 根据年级映射得到
|
|
|
challengeMaxFrequency = 下一年级上限;最高不超过词库最大 wordFrequency
|
|
|
-currentCenterFrequency = 0.35 * gradeMaxFrequency
|
|
|
+currentCenterFrequency = 0.25 * gradeMaxFrequency
|
|
|
currentWordWidth = gradeMaxFrequency
|
|
|
minWordWidth = 100
|
|
|
```
|
|
|
|
|
|
+第一关有意偏简单,降低学生挫败感;后续根据每关 `unknownCount` 自适应向更难或更简单的词频中心移动。
|
|
|
+
|
|
|
## session 流程
|
|
|
|
|
|
+初版 session 使用内存存储,过期时间为 7 天。repository 保存 `createdAt`、`updatedAt`、`expiresAt`;每次 start、submit、get session 时都需要判断是否过期。当前内存 repository 提供 `deleteExpired(now)` 能力,但初版 runtime 不引入持久化、分享链接或跨进程恢复;面向报告裂变传播的持久化 report/shareId 设计后续单独展开。
|
|
|
+
|
|
|
### start
|
|
|
|
|
|
1. 校验年级。
|
|
|
-2. 如果同一 `studentId + grade` 已有未完成且未过期 session:
|
|
|
- - `restart != true`:返回已有 session 当前关。
|
|
|
- - `restart == true`:旧 session 标记 `cancelled`,创建新 session。
|
|
|
-3. 初始化算法状态。
|
|
|
-4. 生成第 1 关 12 个词。
|
|
|
-5. 保存到内存 repository。
|
|
|
+2. 初始化算法状态。
|
|
|
+3. 生成第 1 关 12 个词。
|
|
|
+4. 保存到内存 repository。
|
|
|
+
|
|
|
+初版没有学生身份,也没有 restart 机制;未完成 session 的继续能力由前端可靠保存 `sessionId` 后调用 `GET /api/vocabulary-survey/sessions/{sessionId}` 或提交当前关实现。如果前端丢失 `sessionId`,后端无法恢复旧匿名 session,只能重新开始。
|
|
|
|
|
|
### submit
|
|
|
|
|
|
-1. 校验 session 存在、未过期、未 finished。
|
|
|
-2. 校验 level 存在且是当前关。
|
|
|
-3. 校验所有 `unknownSurveyWordIds` 属于当前 session 和当前 level。
|
|
|
-4. 如果本关已经提交过,直接返回 session 最新状态,不重复计算。
|
|
|
-5. 未提交的本关词默认认识。
|
|
|
-6. 计算 unknownRate 并更新 level。
|
|
|
-7. 判断是否结束;未结束则生成下一关。
|
|
|
-8. 保存并返回最新状态。
|
|
|
+1. 校验 session 存在、未过期。
|
|
|
+2. 如果 session 已 finished,返回 `SURVEY_SESSION_FINISHED`,不再允许提交任何历史关卡。
|
|
|
+3. 校验 level 属于当前 session。
|
|
|
+4. 如果 session 仍 inProgress 且该 level 已提交过,直接返回 session 最新状态,不比较第二次提交内容,不重复计算,也不重复推进下一关。该策略用于处理弱网重试、响应丢失、用户连点等重复提交场景。
|
|
|
+5. 如果该 level 未提交,校验 level 是当前关。
|
|
|
+6. 校验所有 `unknownWordIds` 都属于当前 session 的当前 level。即使提交的是词库真实 `wordId`,也不能接受未在本关展示过的词,避免污染答题分析和 K 值估算。
|
|
|
+7. 未提交的本关词默认认识。
|
|
|
+8. 更新 level,并基于已提交关卡估算下一关中心或生成报告。
|
|
|
+9. 判断是否结束;未结束则生成下一关。
|
|
|
+10. 保存并返回最新状态。
|
|
|
|
|
|
### get session
|
|
|
|
|
|
@@ -454,31 +459,34 @@ right = center + width / 2
|
|
|
5. 局部找不到时扩大查找半径。
|
|
|
6. 仍不足 12 个则抛 `SURVEY_WORD_GENERATION_FAILED`。
|
|
|
|
|
|
-`surveyWordId` 使用 `sw_` 前缀加随机或序列化 ID;`levelId` 使用 `lvl_`;`sessionId` 使用 `surv_`。
|
|
|
+`levelId` 使用 `lvl_` 前缀加随机或序列化 ID;`sessionId` 使用 `surv_` 前缀加随机或序列化 ID。单词提交使用真实词库 `wordId`,不额外生成 `surveyWordId`。
|
|
|
|
|
|
## 收敛算法
|
|
|
|
|
|
提交后:
|
|
|
|
|
|
```text
|
|
|
-unknownRate = unknownCount / wordCount
|
|
|
nextWordWidth = max(currentWordWidth * 0.75, 100)
|
|
|
offset = nextWordWidth / 6
|
|
|
+analysis = analyze(allSubmittedLevels)
|
|
|
```
|
|
|
|
|
|
-规则:
|
|
|
+当前实现以 K 估算为主,而不是只根据本关 `unknownCount` 阈值移动:
|
|
|
|
|
|
```text
|
|
|
-unknownRate <= 0.4:
|
|
|
- nextCenter = currentCenterFrequency + offset
|
|
|
-
|
|
|
-unknownRate >= 0.6:
|
|
|
+if currentSubmittedLevel has inversion:
|
|
|
nextCenter = currentCenterFrequency - offset
|
|
|
+ feedback = 本关结果不稳定,我们再确认一下基础词。
|
|
|
|
|
|
-0.4 < unknownRate < 0.6:
|
|
|
- nextCenter = currentCenterFrequency
|
|
|
+else:
|
|
|
+ nextCenter = analysis.estimatedK
|
|
|
+ feedback 根据 nextCenter 与 currentCenterFrequency 的相对关系提示“挑战稍高一点 / 巩固更基础 / 继续保持”。
|
|
|
```
|
|
|
|
|
|
+`inversion` 仅看当前已提交关:如果当前关同时存在认识词和不认识词,且最高频认识词比最低频不认识词更难,则认为本关答题倒挂,下一关保守下探基础词。历史倒挂不会永久惩罚后续关卡。
|
|
|
+
|
|
|
+`analysis.estimatedK` 使用所有已提交关卡的实测词计算,估算规则见“K 值估算”。这使下一关围绕当前估计能力边界取词,而不是只按本关未知词数量做固定步长移动。
|
|
|
+
|
|
|
边界:
|
|
|
|
|
|
```text
|
|
|
@@ -491,19 +499,19 @@ unknownRate >= 0.6:
|
|
|
|
|
|
```text
|
|
|
if completedLevelCount >= 6: end
|
|
|
-else if completedLevelCount >= 4 && recent two levels boundary: end
|
|
|
+else if completedLevelCount >= 4 && recent two levels in convergenceBand: end
|
|
|
else if completedLevelCount >= 5: end
|
|
|
else continue
|
|
|
```
|
|
|
|
|
|
-“接近边界”定义为 `0.4 < unknownRate < 0.6`。
|
|
|
+`convergenceBand` 定义为 `4 <= unknownCount <= 8`,表示最近关卡的未知数处于中间区间,当前中心词频已接近学生能力分界区间。该阈值对应每关 12 词下的保守版 `0.33/0.67` 策略。
|
|
|
|
|
|
该策略满足:
|
|
|
|
|
|
- 最少 4 关。
|
|
|
- 默认 5 关。
|
|
|
- 最多 6 关。
|
|
|
-- 最近两关均接近边界可提前结束。
|
|
|
+- 最近两关均落入 `convergenceBand` 可提前结束。
|
|
|
|
|
|
## K 值估算
|
|
|
|
|
|
@@ -521,7 +529,7 @@ error(K) = K 左侧不认识词数量 + K 右侧认识词数量
|
|
|
- `wordFrequency > K` 属于 K 右侧。
|
|
|
|
|
|
4. 选择 error 最小的 K。
|
|
|
-5. 如果多个 K 相同,优先选择最接近最近边界关卡中心频率的 K;如果没有边界关,则选择最接近最后一关 center 的 K。
|
|
|
+5. 如果多个 K 相同,优先选择最接近最近落入 `convergenceBand` 关卡中心频率的 K;如果没有此类关卡,则选择最接近最后一关 center 的 K。
|
|
|
|
|
|
## 词汇量估算
|
|
|
|
|
|
@@ -531,7 +539,7 @@ error(K) = K 左侧不认识词数量 + K 右侧认识词数量
|
|
|
vocabularySize = K
|
|
|
```
|
|
|
|
|
|
-原因:`wordFrequency` 在当前词库中接近 rank/频段位置,用 K 作为初版词汇量估算最稳定、最可解释。后续如引入完整掌握度模型,可升级为基于实测词掌握度和未实测词估计掌握度的聚合模型。
|
|
|
+原因:`wordFrequency` 在当前词库中接近 rank/频段位置,用 K 作为初版词汇量估算最稳定、最可解释。API 字段名保持 `vocabularySize`,但业务语义是估算值;前端展示时建议使用“约”“估算”等措辞。后续如引入完整掌握度模型,可升级为基于实测词掌握度和未实测词估计掌握度的聚合模型。
|
|
|
|
|
|
## 能力等级
|
|
|
|
|
|
@@ -598,36 +606,6 @@ ability:
|
|
|
location: classpath:data/dict.data
|
|
|
```
|
|
|
|
|
|
-### 数据库配置
|
|
|
-
|
|
|
-按用户要求直接创建实际配置文本,用户名和密码留空,由用户本地填写。不使用 `.example` 模板。
|
|
|
-
|
|
|
-实现时需要合并到现有配置文件,不能覆盖 `ability.exam-sprint.report` 等已有配置;尤其是 `application-test.yml` 中已有的 report 测试配置必须保留,只新增或更新 `spring.datasource` 和 `ability.vocabulary-survey` 相关配置。
|
|
|
-
|
|
|
-`ability-center-runtime/src/main/resources/application-dev.yml` 指向 `qingti_db_dev`:
|
|
|
-
|
|
|
-```yaml
|
|
|
-spring:
|
|
|
- datasource:
|
|
|
- url: jdbc:mysql://rm-uf6881jgyy065rdxdoo.rwlb.rds.aliyuncs.com:3306/qingti_db_dev?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
|
|
- username:
|
|
|
- password:
|
|
|
- driver-class-name: com.mysql.cj.jdbc.Driver
|
|
|
-```
|
|
|
-
|
|
|
-`ability-center-runtime/src/main/resources/application-test.yml` 指向 `qingti_test`:
|
|
|
-
|
|
|
-```yaml
|
|
|
-spring:
|
|
|
- datasource:
|
|
|
- url: jdbc:mysql://rm-uf6881jgyy065rdxdoo.rwlb.rds.aliyuncs.com:3306/qingti_test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
|
|
- username:
|
|
|
- password:
|
|
|
- driver-class-name: com.mysql.cj.jdbc.Driver
|
|
|
-```
|
|
|
-
|
|
|
-初版词库不依赖 datasource;该配置只为当前项目环境后续数据库接入预留。
|
|
|
-
|
|
|
## 错误码
|
|
|
|
|
|
在 `ability-center-kernel` 的 `ErrorCode` 增加:
|
|
|
@@ -654,7 +632,7 @@ HTTP 层继续通过 `BusinessException + GlobalExceptionHandler` 返回统一 `
|
|
|
2. 第一关生成。
|
|
|
3. 每关返回 12 个词。
|
|
|
4. 同一 session 不重复出词。
|
|
|
-5. `unknownRate` 影响下一关中心词频。
|
|
|
+5. `unknownCount` 影响下一关中心词频。
|
|
|
6. `currentWordWidth` 每关按 0.75 收敛。
|
|
|
7. 最少 4 关、默认 5 关、最多 6 关结束规则。
|
|
|
8. K 值估算。
|
|
|
@@ -672,7 +650,7 @@ HTTP 层继续通过 `BusinessException + GlobalExceptionHandler` 返回统一 `
|
|
|
- MessagePack 字典文件可以加载。
|
|
|
- 能按词频附近取词。
|
|
|
- 能通过 `wordId` 查词。
|
|
|
-- 能通过 `meaningId` 查词。
|
|
|
+- 能过滤没有释义的词。
|
|
|
|
|
|
真实 `dict.data` 加载测试可单独隔离,避免影响核心算法测试速度。核心测试使用 fake provider。
|
|
|
|
|
|
@@ -680,10 +658,10 @@ HTTP 层继续通过 `BusinessException + GlobalExceptionHandler` 返回统一 `
|
|
|
|
|
|
使用 MockMvc/WebMvcTest 风格覆盖:
|
|
|
|
|
|
-- `POST /mini-program/vocabulary-survey/start`
|
|
|
-- `POST /mini-program/vocabulary-survey/levels/{levelId}/submit` 返回下一关。
|
|
|
-- `POST /mini-program/vocabulary-survey/levels/{levelId}/submit` 返回最终报告。
|
|
|
-- `GET /mini-program/vocabulary-survey/sessions/{sessionId}` 恢复当前关或返回报告。
|
|
|
+- `POST /api/vocabulary-survey/start`
|
|
|
+- `POST /api/vocabulary-survey/sessions/{sessionId}/levels/{levelId}/submit` 返回下一关。
|
|
|
+- `POST /api/vocabulary-survey/sessions/{sessionId}/levels/{levelId}/submit` 返回最终报告。
|
|
|
+- `GET /api/vocabulary-survey/sessions/{sessionId}` 恢复当前关或返回报告。
|
|
|
- 错误码映射。
|
|
|
|
|
|
### 架构测试
|
|
|
@@ -698,17 +676,17 @@ HTTP 层继续通过 `BusinessException + GlobalExceptionHandler` 返回统一 `
|
|
|
|
|
|
实现完成后至少运行:
|
|
|
|
|
|
-```powershell
|
|
|
+```bash
|
|
|
mvn test
|
|
|
```
|
|
|
|
|
|
如果全量测试受现有 Playwright/PDF 测试影响,可补充运行目标模块:
|
|
|
|
|
|
-```powershell
|
|
|
-mvn -pl abilities/vocabulary-survey/domain test
|
|
|
-mvn -pl abilities/vocabulary-survey/application test
|
|
|
-mvn -pl abilities/vocabulary-survey/infrastructure test
|
|
|
-mvn -pl ability-center-runtime test
|
|
|
+```bash
|
|
|
+mvn -pl abilities/vocabulary-survey/domain -am test
|
|
|
+mvn -pl abilities/vocabulary-survey/application -am test
|
|
|
+mvn -pl abilities/vocabulary-survey/infrastructure -am test
|
|
|
+mvn -pl ability-center-runtime -am test
|
|
|
```
|
|
|
|
|
|
最终汇报必须说明真实验证结果;未跑或失败不得声称通过。
|
|
|
@@ -718,23 +696,26 @@ mvn -pl ability-center-runtime test
|
|
|
### 初版风险
|
|
|
|
|
|
- session 为内存存储,服务重启后丢失。
|
|
|
-- `dict.data` MessagePack 结构需要通过真实文件验证 Java 反序列化字段名和类型。
|
|
|
+- `dict.data` 已验证真实结构,并通过 Java MessagePack loader 加载;词汇摸底是必需能力,因此资源缺失或解析失败应导致启动失败。
|
|
|
- 词汇量初版采用 `vocabularySize = K`,不是完整掌握度模型。
|
|
|
-- 当前项目未接登录态,`studentId` 暂由请求体传入。
|
|
|
+- 当前项目未接登录态,初版使用匿名 session;前端确认会可靠保存 `sessionId`,后端按 `sessionId` 支持恢复未完成摸底。
|
|
|
- 复制二进制词库到仓库后,仓库体积可能增加;词库更新需要替换文件并重新发版。
|
|
|
+- 当前无释义词由 Java loader 过滤,本轮不直接清理二进制词库文件;如果需要源头清理,应建立独立词库清洗、版本校验和发布流程。
|
|
|
+- 当前 report 随内存 session 保存,不支持服务重启后恢复、跨实例访问或长期分享传播;报告持久化和分享 ID 后续单独设计。
|
|
|
|
|
|
### 后续演进
|
|
|
|
|
|
- session、level、word 明细落库,支持服务重启恢复和多实例部署。
|
|
|
- 接入真实登录态,从认证上下文获取学生 ID。
|
|
|
-- 将词库更新流程从复制文件升级为 OSS 下载、版本校验或配置化挂载。
|
|
|
+- 引入 report 持久化和 shareId/share link,支持报告裂变传播、跨重启访问和有效期管理。
|
|
|
+- 将词库更新流程从复制文件升级为 OSS 下载、版本校验、配置化挂载或源文件清洗。
|
|
|
- 引入更完整的词汇量估算模型,使用实测词掌握度和未实测词估算掌握度聚合。
|
|
|
- 根据真实小程序体验调整停止条件、反馈文案和报告文案。
|
|
|
|
|
|
## 实施顺序
|
|
|
|
|
|
1. 新增 Maven 模块和基础 package。
|
|
|
-2. 复制 `dict.data` 到 infrastructure resources。
|
|
|
+2. 确认已复制的 `dict.data` 位于 infrastructure resources,并纳入模块资源。
|
|
|
3. 新增 MessagePack 依赖和词库加载 adapter。
|
|
|
4. 实现 domain 模型、年级策略、选词规划、K 估算和报告生成。
|
|
|
5. 实现内存 session repository。
|