错题四科入库:鸿蒙错题本种子数据与复习队列效果


实例:错题本(Mistake)|技术:10 道错题、四科覆盖、轮次梯度
一、种子数据的设计目标
实例 19 的种子数据要撑起「错题列表 + 复习队列 + 错因统计 + 总览」,设计目标:
- 10 道错题:列表内容充实——多屏可滚;
- 四科覆盖:数学 4 / 英语 3 / 物理 2 / 化学 1——学科筛选有结果;
- 轮次梯度:0~4 次——复习进度有层次(0 次急待复习、4 次快掌握);
- 掌握状态:已掌握 2 / 未掌握 8——「已掌握」tab 有内容;
- 错因覆盖:计算失误 3 / 概念不清 4 / 思路错误 2 / 粗心大意 1——错因统计条四色;
- 知识点多样:函数变换/绝对值不等式/数列求和/三角函数/被动语态/虚拟语气/倒装句/自由落体/圆周运动/氧化还原——知识点搜索有结果。
二、10 道错题种子数据
| # | 学科 | 题目 | 正确答案 | 我的答案 | 知识点 | 错因 | 轮次 | 掌握 | 录入 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 数学 | 已知 f(x)=x²+2x,求 f(x+1) 展开式 | x²+4x+3 | x²+2x+2 | 函数变换 | 计算失误 | 2 | 未掌握 | -20天 |
| 2 | 数学 | 解不等式 |x-3|>5 | x<-2 或 x>8 | x>8 | 绝对值不等式 | 思路错误 | 1 | 未掌握 | -18天 |
| 3 | 数学 | 等差数列 a₁=2,d=3,求 S₁₀ | 155 | 152 | 数列求和 | 计算失误 | 3 | 未掌握 | -15天 |
| 4 | 数学 | sin 75° 的值 | (√6+√2)/4 | (√6-√2)/4 | 三角函数 | 概念不清 | 4 | 已掌握 | -30天 |
| 5 | 英语 | The book ___ (write) by him | was written | written | 被动语态 | 概念不清 | 2 | 未掌握 | -12天 |
| 6 | 英语 | I suggest he ___ (go) home early | go | goes | 虚拟语气 | 概念不清 | 1 | 未掌握 | -10天 |
| 7 | 英语 | 翻译:他不仅学习好,还乐于助人 | Not only does he… | He not only study… | 倒装句 | 粗心大意 | 2 | 未掌握 | -8天 |
| 8 | 物理 | 自由落体 3s 内下落高度(g=10) | 45m | 30m | 自由落体 | 计算失误 | 1 | 未掌握 | -6天 |
| 9 | 物理 | 匀速圆周运动向心力方向 | 始终指向圆心 | 指向切线方向 | 圆周运动 | 概念不清 | 4 | 已掌握 | -25天 |
| 10 | 化学 | 配平:C+O₂→CO | 2C+O₂=2CO | C+O₂=CO | 氧化还原 | 思路错误 | 0 | 未掌握 | -3天 |
设计要点:
- 轮次梯度 0~4:化学配平 0 次(最急)、多数 1~3 次、三角函数/圆周运动 4 次且已掌握——复习进度有层次;
- 掌握分布 8/2:未掌握 8(复习主力)、已掌握 2(sin75°、向心力——复习满 5 次前手动标记的示例)——「已掌握」tab 有内容;
- 错因分布:概念不清 4(最多——「没学懂」是主要矛盾)、计算失误 3、思路错误 2、粗心大意 1——错因统计条「概念不清 4」排第一(cnt DESC);
- 录入时间 -30 天 ~ -3 天:错题时间跨度——列表「新录入在前」(created_time DESC 化学配平最新最顶);
- 复习队列:
mastered=0 AND rounds<5命中 8 道(未掌握全在队列),ORDER BY review_time ASC(=created_time)——化学配平(-3 天)最旧优先?不——review_time 小 = 录入早 = 排前:sin75° 和向心力已掌握不在队列,队列里 review_time 最小的是 -20 天的 f(x+1)(最久没复习)排最前。
三、代码实现
const now = Date.now();
const day = 86400000;
const seed: MistakeSeed[] = [ /* 10 道,见第二节表格 */ ];
for (const s of seed) {
const values: relationalStore.ValuesBucket = {
subject: s.subject, question: s.question, answer: s.answer, my_answer: s.myAnswer,
knowledge: s.knowledge, reason: s.reason, rounds: s.rounds,
mastered: s.mastered, created_time: s.createdTime, review_time: s.createdTime,
};
await store.insert(MistakeDao.TABLE, values);
}
hilog.info(DOMAIN, TAG, '已填充 10 道错题种子数据');
review_time: s.createdTime:上次复习时间初始化为录入时间(刚录入 = 刚看过)——复习队列的初始语义。幂等保护:查 mistake COUNT,非空即返回。
四、注入后的错题本效果预测
| 区块 | 预期效果 |
|---|---|
| 标题栏 | 「共 10 道 · 待复习 8」 |
| 错因统计条 | ●概念不清 4 / ●计算失误 3 / ●思路错误 2 / ●粗心大意 1 |
| 全部列表 | 未掌握 8 在前(轮次少在前):化学配平(0次) → 不等式(1) → 自由落体(1) → 虚拟语气(1) → … → 已掌握 2 在后 |
| 待复习 tab | 8 道未掌握,按 review_time 升序(f(x+1) 最久没复习排最前) |
| 已掌握 tab | 2 道(sin75°、向心力,✅ 已掌握) |
| 学科筛选 | 数学 4 / 英语 3 / 物理 2 / 化学 1 |
演示脚本:指着统计条说「我的错题主要集中在概念不清,4 道——说明不是粗心是没学懂」→ 切「待复习」→ 8 道,「f(x+1) 这个最久没复习,排第一」→ 点化学配平「复习」→ Toast「✅ 复习第 1 次」→ 复习标签变 1/5 → 连点 5 次(模拟多轮)→ 第 5 次 Toast「🎉 复习 5 次,已标记掌握!」→ 该题变「✅ 已掌握」、待复习 8→7。
五、验证方法
- hilog 日志:搜
MistakeDao,看到「已填充 10 道错题种子数据」; - 页面显示:标题栏「10 道 · 待复习 8」、统计条 4 色、列表 10 卡;
- 数据库直查:
SELECT COUNT(*) FROM mistake; -- 10 SELECT subject, COUNT(*) FROM mistake GROUP BY subject; -- 数学4 英语3 物理2 化学1 SELECT reason, COUNT(*) FROM mistake GROUP BY reason ORDER BY COUNT(*) DESC; -- 概念不清4 计算失误3 思路错误2 粗心大意1 SELECT COUNT(*) FROM mistake WHERE mastered=0 AND rounds<5; -- 8(待复习) SELECT question FROM mistake WHERE mastered=0 AND rounds<5 ORDER BY review_time ASC LIMIT 3; -- f(x+1)(-20天最旧)最前 - 交互验证:复习(轮次+1)、标记掌握/取消、学科筛选、已掌握 tab、删除。
六、FAQ
Q1:为什么已掌握 2 道是「复习 4 次」而不是 5 次?
A:种子里 sin75° 和向心力 rounds=4 且 mastered=1——模拟「手动标记掌握」的结果(4 次后手动标掌握,不必等满 5 次)——展示「手动干预」通道。**「轮次 4 + 手动掌握」**体现两种掌握路径。
Q2:错因为什么「概念不清」最多?
A:真实学情——「没学懂」是错题主因(计算失误是偶发、概念不清是系统性)。统计条「概念不清 4」排第一让诊断有说服力(演示「我的问题在哪」)。
Q3:复习队列里 f(x+1) 为什么排最前?
A:ORDER BY review_time ASC——f(x+1) 录入最早(-20 天),review_time=created_time 最小 → 排最前——「最久没复习的优先」。化学配平虽然轮次 0(最急),但录入最晚(-3 天)排最后——「队列按时间排序不按轮次」(轮次急迫性由 tab 切换体现)。
Q4:10 道题够演示复习闭环吗?
A:够——点化学配平复习 5 次(0→5 次完整演示「自动掌握」)+ 手动标记/取消掌握 + 队列变化(8→7)——复习闭环全路径可演示。
Q5:题目里的数学符号(√、²、|)能正常显示吗?
A:UTF-8 文本正常显示(√6、x²、|x-3|)——ArkUI Text 支持 Unicode。「题目是纯文本」(不渲染 LaTeX)——Demo 简化,真实错题应用可内嵌公式图片或富文本。
Q6:知识点搜索「函数」会命中什么?
A:LIKE '%函数%' 命中「函数变换」(第 1 题)——知识点标签的模糊搜索演示。「按知识点聚合复习」:搜同一知识点找出所有相关错题一起练。
七、文章小结
本篇文章展示了实例 19 的种子数据:10 道错题四科覆盖(数学 4/英语 3/物理 2/化学 1 + 轮次梯度 0~4 + 掌握 8/2 + 错因四类 + 知识点十种)。核心是「复习队列与诊断的种子设计」——轮次梯度让复习进度有层次、错因分布让统计条「概念不清 4」有诊断说服力、review_time=created_time 让队列「最久没复习优先」成立。错题本一开屏就有复习队列与错因画像。
下一篇(19-5)是实例 19 的收官文章,展示 MistakePage 全量代码与运行效果。
附录:initSeedData 完整代码与种子设计复盘
第三节的插入循环里写着 /* 10 道,见第二节表格 */——那是占位符,真正的 10 个 MistakeSeed 对象在这里完整亮相。对照第二节表格逐行核对,数据 100% 一致。
8.1 种子数组完整定义(10 道)
interface MistakeSeed {
subject: string; // 学科
question: string; // 题目
answer: string; // 正确答案
myAnswer: string; // 我的错误答案
knowledge: string; // 知识点
reason: string; // 错因
rounds: number; // 复习轮次
mastered: number; // 0 未掌握 / 1 已掌握
createdTime: number; // 录入时间(毫秒时间戳)
}
const day = 86400000; // 一天 = 24 * 60 * 60 * 1000 ms
const now = Date.now();
const seed: MistakeSeed[] = [
// 数学 4 道
{ subject: '数学', question: '已知 f(x)=x²+2x,求 f(x+1) 展开式', answer: 'x²+4x+3', myAnswer: 'x²+2x+2', knowledge: '函数变换', reason: '计算失误', rounds: 2, mastered: 0, createdTime: now - 20 * day },
{ subject: '数学', question: '解不等式 |x-3|>5', answer: 'x<-2 或 x>8', myAnswer: 'x>8', knowledge: '绝对值不等式', reason: '思路错误', rounds: 1, mastered: 0, createdTime: now - 18 * day },
{ subject: '数学', question: '等差数列 a₁=2,d=3,求 S₁₀', answer: '155', myAnswer: '152', knowledge: '数列求和', reason: '计算失误', rounds: 3, mastered: 0, createdTime: now - 15 * day },
{ subject: '数学', question: 'sin 75° 的值', answer: '(√6+√2)/4', myAnswer: '(√6-√2)/4', knowledge: '三角函数', reason: '概念不清', rounds: 4, mastered: 1, createdTime: now - 30 * day },
// 英语 3 道
{ subject: '英语', question: 'The book ___ (write) by him', answer: 'was written', myAnswer: 'written', knowledge: '被动语态', reason: '概念不清', rounds: 2, mastered: 0, createdTime: now - 12 * day },
{ subject: '英语', question: 'I suggest he ___ (go) home early', answer: 'go', myAnswer: 'goes', knowledge: '虚拟语气', reason: '概念不清', rounds: 1, mastered: 0, createdTime: now - 10 * day },
{ subject: '英语', question: '翻译:他不仅学习好,还乐于助人', answer: 'Not only does he…', myAnswer: 'He not only study…', knowledge: '倒装句', reason: '粗心大意', rounds: 2, mastered: 0, createdTime: now - 8 * day },
// 物理 2 道
{ subject: '物理', question: '自由落体 3s 内下落高度(g=10)', answer: '45m', myAnswer: '30m', knowledge: '自由落体', reason: '计算失误', rounds: 1, mastered: 0, createdTime: now - 6 * day },
{ subject: '物理', question: '匀速圆周运动向心力方向', answer: '始终指向圆心', myAnswer: '指向切线方向', knowledge: '圆周运动', reason: '概念不清', rounds: 4, mastered: 1, createdTime: now - 25 * day },
// 化学 1 道
{ subject: '化学', question: '配平:C+O₂→CO', answer: '2C+O₂=2CO', myAnswer: 'C+O₂=CO', knowledge: '氧化还原', reason: '思路错误', rounds: 0, mastered: 0, createdTime: now - 3 * day },
];
补全核对表(数组 ↔ 第二节表格):
| 维度 | 设计值 | 数组实际 |
|---|---|---|
| 总题数 | 10 | 10(数学 4 / 英语 3 / 物理 2 / 化学 1) |
| 轮次梯度 | 0~4 | 0 次×1、1 次×3、2 次×3、3 次×1、4 次×2 |
| 掌握分布 | 已掌握 2 | rounds=4 且 mastered=1 的 sin75° 与向心力 |
| 错因分布 | 概念不清 4 最多 | 计算失误 3 / 思路错误 2 / 粗心大意 1 |
| 录入时间 | -30 ~ -3 天 | now - N*day,跨度 27 天 |
为什么用 now - N*day 而不是写死时间戳:种子要在任何时间首次启动都呈现「30 天前到 3 天前」的错题跨度——写死时间戳会在几个月后变成「半年前的旧错题」,createdTime 相对 now 计算永远新鲜。
8.2 initSeedData 完整方法(含幂等保护)
static async initSeedData(context: common.Context): Promise<void> {
const store = await MistakeDao.getStore(context);
// 幂等保护:已有数据则直接返回,避免每次启动重复插入
const count = await store.count(MistakeDao.TABLE);
if (count > 0) {
hilog.info(DOMAIN, TAG, '错题本已有 %{public}d 条数据,跳过种子填充', count);
return;
}
for (const s of seed) {
const values: relationalStore.ValuesBucket = {
subject: s.subject, question: s.question, answer: s.answer, my_answer: s.myAnswer,
knowledge: s.knowledge, reason: s.reason, rounds: s.rounds,
mastered: s.mastered, created_time: s.createdTime, review_time: s.createdTime,
};
await store.insert(MistakeDao.TABLE, values);
}
hilog.info(DOMAIN, TAG, '已填充 10 道错题种子数据');
}
两个容易被忽略的细节:
review_time: s.createdTime:上次复习时间 = 录入时间——刚录入的题「刚看过」,复习队列ORDER BY review_time ASC才能把最久没复习的排最前(见第五节 SQL);- 幂等保护在 insert 之前:
store.count非 0 即 return——错题本支持用户删除/新增真实数据,绝不能在用户已有数据时再灌种子(否则用户删掉的题会「复活」)。
8.3 注入后的错题本效果预测(SQL 自检)
用四个 SQL 直查数据库,验证种子注入是否正确:
-- ① 总数与学科分布:10 / 数学4 英语3 物理2 化学1
SELECT COUNT(*) FROM mistake;
SELECT subject, COUNT(*) FROM mistake GROUP BY subject;
-- ② 轮次梯度:0 到 4 都有(复习进度有层次)
SELECT rounds, COUNT(*) FROM mistake GROUP BY rounds ORDER BY rounds;
-- ③ 错因分布:概念不清 4 排第一(统计条主色)
SELECT reason, COUNT(*) FROM mistake GROUP BY reason ORDER BY COUNT(*) DESC;
-- ④ 复习队列命中:mastered=0 AND rounds<5 → 8 道
SELECT question FROM mistake WHERE mastered=0 AND rounds<5 ORDER BY review_time ASC;
预期结果:① 10 条、② 轮次 0/1/2/3/4 各 1/3/3/1/2、③ 概念不清 4、④ 队列 8 条且 f(x+1)(-20 天)排最前。任何一项不符,先查 initSeedData 是否在入口被调用、表结构字段名是否与 ValuesBucket 键名一致。
8.4 FAQ(种子数据专项)
Q1:now - 20 * day 每次启动都在变,两天后 f(x+1) 变成 -22 天,会影响队列顺序吗?
A:不会——所有题目的 createdTime 同时右移,相对顺序不变(f(x+1) 永远比化学配平早 17 天)。复习队列按 review_time 相对排序,绝对时间戳漂移无影响。
Q2:已掌握 2 道(rounds=4)为什么还留在表里?
A:错题本不删题——掌握 ≠ 删除。「已掌握 tab」展示它们(✅ 标记),随时可「取消掌握」重新进入复习队列——掌握状态可逆,符合真实学习(旧错题可能再错)。
Q3:幂等保护只查 count,如果用户把题删到 0 条会怎样?
A:count=0 会再次灌种子——这是特性不是 bug:全删后回到「干净示例状态」,方便再次演示。生产环境可加 seed_done 标志表区分「从未初始化」与「用户清空」。
Q4:种子时间戳为什么用毫秒而不是秒?
A:Date.now() 返回毫秒(13 位),created_time/review_time 字段类型 INTEGER 直接存毫秒——与 reviewOnce 里 review_time = Date.now() 同一单位,排序/比较无需换算。
更多推荐




所有评论(0)