earthworm 1.0.0 版本发布解读:从课程数据修复到游戏交互与排名系统的完整演进
【免费下载链接】earthwormLearning English through the method of constructing sentences with conjunctions项目地址: https://gitcode.com/GitHub_Trending/ea/earthworm
本文以 earthworm 开源仓库的 CHANGELOG.md 为线索,系统梳理 v1.0.0 版本在课程内容质量、游戏输入交互、快捷键/声音设置、定时排名任务以及 E2E 测试体系上的关键变更,并结合apps/api、apps/client等目录下的源码实现逐一印证,帮助读者理解该版本每一项改动背后的设计意图与落地点。读完本文,你将能够快速定位这些功能的实现文件,掌握本项目的排名、快捷键、答题设置与测试体系的组织方式。
版本背景:v1.0.0 双版本发布
CHANGELOG.md记录了两次1.0.0版本发布,时间分别为2024-03-02与2024-03-13,两条记录结构一致,均包含Bug Fixes(缺陷修复)与Features(新功能)两大分类。整个仓库的核心能力围绕"通过连词造句法学习英语"展开,客户端基于 Nuxt/Vue 构建,服务端基于 NestJS + Drizzle ORM,因此本版本的大量修复集中体现在两个维度:
- 课程数据本身的质量(course/statement 内容纠错);
- 游戏过程中的输入与交互体验(空格提交、快捷键、声音、单词宽度提示等)。
下文将按"新功能"与"缺陷修复"两条主线展开,并在每项变更后给出对应的源码路径,方便读者对照学习。
新功能深度解读:本版本引入了哪些能力
1. 定时任务模块与排行榜周期性重置
add scheduled task module and weekly reset ranking function (#176)
这是本版本最重量级的后端能力。排行榜不再只增不减,而是引入了周榜 / 月榜 / 年榜三个维度,并由定时任务自动清零。相关实现位于 apps/api/src/cron-job/cron-job.service.ts:
export const TIME_ZONE = "Asia/Shanghai"; @Injectable() export class CronJobService { private static readonly EVERY_MONDAY_AT_2AM = "0 2 * * 1"; private static readonly EVERY_FIRST_DAY_OF_MONTH_AT_2AM = "0 2 1 * *"; private static readonly EVERY_FIRST_DAY_OF_YEAR_AT_2AM = "0 2 1 1 *"; ... @Cron(CronJobService.EVERY_MONDAY_AT_2AM, { timeZone: TIME_ZONE }) async resetRankListWeekly() { this.rankService.resetRankList(RankPeriod.WEEKLY); } @Cron(CronJobService.EVERY_FIRST_DAY_OF_MONTH_AT_2AM, { timeZone: TIME_ZONE }) async resetRankListMonthly() { ... } @Cron(CronJobService.EVERY_FIRST_DAY_OF_YEAR_AT_2AM, { timeZone: TIME_ZONE }) async resetRankListYearly() { ... } }三个 Cron 表达式均为凌晨 2 点执行,时区显式指定为Asia/Shanghai:
| 周期 | Cron 表达式 | 触发时机 |
|---|---|---|
| 周榜 | 0 2 * * 1 | 每周一 02:00 |
| 月榜 | 0 2 1 * * | 每月 1 日 02:00 |
| 年榜 | 0 2 1 1 * | 每年 1 月 1 日 02:00 |
模块通过 apps/api/src/cron-job/cron-job.module.ts 注入RankService与UserModule。底层清理逻辑在 apps/api/src/rank/rank.service.ts 中实现:对对应周期的 Redis Key 执行DEL,成功后记录 verbose 日志,失败则记录 error 日志。
排名的计数逻辑同样值得关注:用户每完成一课,CourseService.completeCourse会调用rankService.userFinishCourse(userId)(见 apps/api/src/course/course.service.ts),该方法遍历周/月/年三个 Key,通过ZSCORE判断是否存在记录,不存在则ZADD初始化为 1,存在则ZINCRBY累加。榜单读取使用ZREVRANGE ... WITHSCORES取前 25 名,并附加当前用户自身的名次与完成数(ZREVRANK/ZSCORE),最终通过UserService.findUser批量补充用户名。可以看出:排行榜数据完全基于 Redis 有序集合(Sorted Set)实现,周期性 DEL 即完成了新一周期的清零。
2. 空格提交答案与可自定义的提交快捷键
submit with space与shortcut key settings for submit operations
这两项变更共同解决了"如何更高效地提交答案"的体验问题。空格提交功能由 apps/client/composables/user/submitKey.ts 提供:
export const SPACE_SUBMIT_ANSWER = "spaceSubmitAnswer"; export function useSpaceSubmitAnswer() { const { value: useSpace, toggle: toggleUseSpaceSubmitAnswer, ... } = useLocalStorageBoolean(SPACE_SUBMIT_ANSWER, false); return { useSpace, isUseSpaceSubmitAnswer, toggleUseSpaceSubmitAnswer, ... }; }该开关默认关闭,开启状态持久化在localStorage的spaceSubmitAnswer键中,可在设置页 apps/client/pages/User/Setting.vue 的"答题设置"板块通过"开启空格提交答案"开关切换。
快捷键方面,CHANGELOG还特别记录了**setting:** update cmd key display in the shortcut settings这条对快捷键设置 UI 的微调。完整的快捷键体系定义在 apps/client/composables/user/shortcutKey.ts,默认绑定如下:
| 功能类型 | 默认快捷键 | 说明 |
|---|---|---|
sound | Ctrl+' | 播放发音 |
answer | Ctrl+; | 显示/隐藏答案预览、再来一次 |
skip | Ctrl+. | 跳过当前问题 |
previous | Ctrl+, | 返回上个问题 |
mastered | Ctrl+m | 标记内容已经掌握 |
pause | Ctrl+p | 暂停/继续游戏 |
该实现参考了 VSCode 的快捷键设计,默认仅支持组合键形式,并提供了convertMacKey对 macOS 的修饰键做归一化处理(Control → Ctrl、Meta → Command),这正是cmd key display修复所涉及的部分。用户自定义的键位会以 JSON 形式写入localStorage的shortcutKeys键,编辑入口对应 apps/client/components/CustomShortcutDialog.vue,并带hasSameShortcutKey冲突检测,防止多个功能绑定同一组合键。
3. 移动端提示与完善的使用引导
add mobile tips (#144)与perfect use introduction (#161)
这两项属于易用性改进:为移动端访问场景补充提示信息,同时完善首次使用引导。客户端作为 Nuxt 应用,页面结构与提示组件主要集中在 apps/client/pages 与 apps/client/components/main 目录,例如游戏主页面 apps/client/pages/game/[coursePackId]/[id].vue 中便包含"你已经全部都掌握 自动帮你跳转到课程列表啦"等 toast 引导提示,可以看作是完善使用引导的产物之一。仓库还提供utils/detectDevice.ts(见 apps/client/utils/detectDevice.ts)用于设备能力判断,为移动端差异化提示提供了基础。
4. 每日朗读一个句子
read one sentence per day aloud (#171)
该功能围绕"每天一个句子"的英语学习理念展开。游戏过程中,句子数据由 apps/api/src/course/course.service.ts 的find方法从course与statement表按order升序联查得到,每句包含中文、英文与音标字段。朗读依赖的发音能力可在设置页 apps/client/pages/User/Setting.vue 的"声音设置"板块进行配置,包括"答案页面自动播放声音"(useAutoPronunciation)、"答题时自动播放声音"(useAutoPlayEnglish)以及"切换口音"(usePronunciation),相关实现位于 apps/client/composables/user/sound.ts 与 apps/client/composables/user/pronunciation.ts。
5. 错误单词的交互优化
added interaction to resolve wrong words、long sentence modification error、support to delete back to the previous incorrect word
这三条变更共同完善了"答错—纠正"的闭环体验:
- 返回上一个错误单词:允许在答题过程中回退,删除到前一个输错的位置重新输入;
- 长句修改错误:修复了长句子场景下修改输入时的异常行为;
- 与错误单词的交互:结合答案提示组件 apps/client/components/main/AnswerTip.vue 与"自动显示答案(输错三次)"设置(见 apps/client/composables/user/errorTip.ts),引导用户基于提示完成纠错。
配套的输入体验还有打字音效:正确/错误提示音与打字音效实现在 apps/client/components/main/QuestionInput/useTypingSound.ts 中,使用 Web Audio API 的AudioContext.decodeAudioData预解码typing.mp3,并通过PLAY_INTERVAL_TIME = 60毫秒做播放节流,防止高频按键导致声音重叠;对应音效资源位于 apps/client/assets/sounds(error.mp3、right.mp3、typing.mp3、error-feeble.mp3)。
6. 单词宽度提示与空格输入规则
the word is suggested by the width of the input box (#149)、Keep word width consistent (#181)、Prevents adding Spaces after the last word
这三条围绕"输入框宽度"与"空格规则"展开:输入框会根据内容宽度动态提示下一个单词的长度(对应设置页"显示每个单词长度"开关,见 apps/client/composables/user/words.ts),同时保证单词宽度在视觉上保持一致;"防止在最后一个单词后添加空格"则约束了答案提交前的输入格式,避免因尾部多余空格导致判定偏差。空格键本身也承担了"提交答案"的功能(前述submit with space),因此输入规则与提交逻辑之间需要严格配合。
7. 基于 Cypress 的 E2E 测试
add e2e test by cypress
本版本正式引入 Cypress E2E 测试体系,相关配置与用例位于 apps/client/cypress。以 apps/client/cypress/e2e/start-game.cy.ts 为例,它覆盖了两条核心链路:
- 游客进入游戏:拦截
GET /courses/try,点击"开启 Earthworm"后断言请求方法为GET,并验证 URL 跳转到/main/1; - 登录用户进入游戏:通过
cy.login登录后,拦截POST /game/start与GET /courses/2,断言最终 URL 包含/main/2。
配套的登录命令定义在 apps/client/cypress/support/commands.ts,测试数据样例位于 apps/client/cypress/fixtures(users.json、profile.json等)。这表明项目在单元测试(Vitest,见 apps/client/vitest.config.ts)之外,还建立了面向关键用户路径的端到端回归防线。
缺陷修复深度解读:课程数据与服务端稳定性
1. 课程语句内容纠错(占比最高)
Bug Fixes中超过一半的条目属于课程数据修复:
fix course 18 the ninety-first field is incorrect(出现两次,同一 commit4dc39ac与4436706)fix course issue, closes #118(对应 commit7408c2c)fix course-18、fix-course-18fix course-34 statement is incorrectupdate course 12-74、update course 15.5-28
这些修复的对象是packages/xingrong-courses/data/courses/目录下的课程 JSON(共 55 个课程,命名如18.json、34.json),而课程数据最初来自packages/xingrong-courses/data/pdf/下的 PDF 教材(含05.5.pdf、10.5.pdf、15.5.pdf等半课文件)。PDF 转结构化数据的解析逻辑在 packages/xingrong-courses/src/parsePDF/parser.ts:它以中文 英文 K.K.音标为起始标记、以中文 原形 第三人称单数 过去式 ing形式为结束标记切分文本,再将"中文"与"英文+音标"两两成组,最终生成{ chinese, english, soundmark }三元组。由于 PDF 排版分页、换页页码等噪声的存在(解析器专门过滤了纯数字的换页符),课程数据难免出现个别字段错位,因此 v1.0.0 集中修复了第 18 课第 91 条语句、第 34 课语句等历史数据问题,这类修复直接决定了学习内容的正确性。
2. 服务端稳定性:db:init环境变量读取失败
the env variable cannot be read, causing the db:init command to fail.
该修复解决的是初始化数据库命令因无法读取环境变量而失败的问题。数据库初始化相关的命令与配置位于 packages/db(Drizzle 迁移脚本src/migrate.ts、配置文件drizzle.config.ts、迁移记录drizzle/meta/_journal.json),服务端的数据库连接在 apps/api/src/common/db.ts 与 apps/api/src/global/providers/db.provider.ts 中建立。db:init类命令的失败往往源于环境变量加载时机早于 dotenv 初始化,本版本修复后确保了根目录 package.json 中定义的数据库脚本可以稳定执行。
3. 交互与测试相关的杂项修复
remove the horrible emoji:移除了游戏界面中不合适的 emoji 展示;tests problems:修复了测试用例本身存在的问题,保证 CI 中单元/集成测试(如 apps/api/src/course/tests/course.service.spec.ts、apps/api/src/rank/tests/rank.service.spec.ts)可稳定运行。
从 CHANGELOG 出发:如何验证与继续探索
如果你想在本地验证本版本的关键能力,可以按以下顺序操作:
- 跑通服务端:进入
apps/api,按 apps/api/README.md 的说明配置环境变量并启动;使用根目录 package.json 中提供的数据库脚本(依赖packages/db的迁移)初始化表结构; - 验证排行榜重置:查看 apps/api/src/cron-job/cron-job.service.ts 中的三个 Cron 表达式,结合 apps/api/src/rank/rank.service.ts 的
resetRankList方法理解 Redis Key 的清除机制; - 体验输入交互:启动
apps/client后进入游戏,在设置页 apps/client/pages/User/Setting.vue 中开启空格提交、打字音效、自动下一题等选项,观察localStorage中spaceSubmitAnswer、shortcutKeys等键值的变化; - 运行 E2E:在
apps/client下通过cypress.config.ts运行 apps/client/cypress/e2e/start-game.cy.ts,复现游客与登录用户两条进入游戏的路径; - 检查课程数据:对比 packages/xingrong-courses/data/courses/18.json 等文件与 packages/xingrong-courses/src/parsePDF/parser.ts 的解析规则,理解 CHANGELOG 中 course 18 / course 34 修复的由来。
总结
从CHANGELOG.md可以看出,earthworm 的 v1.0.0 是一次"内容质量 + 交互体验 + 工程基建"并重的发布:课程数据层面完成了第 18、34 课等语句纠错与 12-74、15.5-28 范围的内容更新;功能层面落地了空格提交、自定义快捷键、打字音效、单词宽度提示、每日朗读与错误单词回退等高频交互能力;后端则引入了基于 Redis 与 Cron 的周/月/年排行榜重置机制;工程层面补充了 Cypress E2E 测试并修复了db:init环境变量读取问题。对于希望深入该项目源码的读者,本文给出的文件路径即可作为一张"按功能索引源码"的导航图。
【免费下载链接】earthwormLearning English through the method of constructing sentences with conjunctions项目地址: https://gitcode.com/GitHub_Trending/ea/earthworm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考