feat: 完成分类、worksheet 接口开发,资源迁移到七牛

This commit is contained in:
R524809
2026-10-16 17:32:23 +08:00
parent e6ded00a4c
commit 8827110f2f
107 changed files with 12775 additions and 125 deletions
+15 -10
View File
@@ -2,15 +2,17 @@
> 数据库:PostgreSQL;ORM:Prisma。数据库选型已按用户确认更新。
>
> 状态:目标设计,本地开发数据库已配置;13 张业务表及初始配置已在本地创建;尚未创建 Prisma Schema 或接入 Prisma 迁移历史;线上集合、数据量和异常分布待 S0 盘点。
> 状态:目标设计,本地开发数据库已配置;13 张业务表及初始配置已在本地创建;已接入 Prisma Schema 和迁移基线;线上集合、数据量和异常分布待 S0 盘点。
>
> 更新日期:2026-10-14。
> 更新日期:2026-10-15(登录与 Prisma 实施状态更新,业务表结构不变)。
>
> 配套方案:[云服务端迁移方案](../云服务端迁移方案.md)。本文细化该方案的数据结构与一致性规则。
已生成 [数据库初始化 SQL](../../apps/server/prisma/sql/README.md)。2026-10-12 验证并创建最初 10 张表及两条配置;2026-10-14 执行 004/005/006/007/008 后,本地共有 13 张表。最新空库基线与对应增量升级结构已在临时库对照通过,多资源、多年级、资源计数及两种收藏约束、旧数据保留验证通过,临时库已清理;尚未创建 Prisma Schema 或导入线上数据。
登录与鉴权的后续实施设计见 [用户鉴权与登录设计](./用户鉴权与登录设计.md):优先小程序,细化微信登录、JWT、401 恢复及注销,并说明未来 Web 会话与跨端身份的演进;当前无需调整本文 13 张表。
本地开发使用 Docker PostgreSQL,图片等资源直接使用七牛;云服务器首期只常驻生产库,隔离测试库在真实联调时按需创建,可复用同一 PostgreSQL 实例。详见 [开发与部署环境方案](./开发与部署环境方案.md) 与 [本地数据库说明](../../infra/README.md)。本地数据库已配置,本文 13 张表已在本地创建,业务 API 尚未实现。
已生成 [数据库初始化 SQL](../../apps/server/prisma/sql/README.md)。2026-10-12 验证并创建最初 10 张表及两条配置;2026-10-14 执行 004/005/006/007/008 后,本地共有 13 张表。最新空库基线与对应增量升级结构已在临时库对照通过,多资源、多年级、资源计数及两种收藏约束、旧数据保留验证通过,临时库已清理;2026-10-15 已接入 Prisma Schema 和迁移基线,尚未导入线上数据。
本地开发使用 Docker PostgreSQL,图片等资源直接使用七牛;云服务器首期只常驻生产库,隔离测试库在真实联调时按需创建,可复用同一 PostgreSQL 实例。详见 [开发与部署环境方案](./开发与部署环境方案.md) 与 [本地数据库说明](../../infra/README.md)。本地数据库已配置,本文 13 张表已在本地创建,服务端微信登录、本人读取和账户鉴权已实现,其他业务 API 待接入。
## 1. 范围与依据
@@ -109,6 +111,7 @@ erDiagram
| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 |
| --------------------------- | -------------- | ----------- | ------------------------------------------------------------------ |
| status | varchar(16) | 否 / draft | CHECK:draft、active、hidden;公开可见还要求全部上级 active |
| id | varchar(128) | 否 | 原分类 ID,如 math、chinese;不重命名 |
| name | varchar(100) | 否 | 分类名 |
| icon | varchar(1024) | 否 | 保留图标名、字符等非文件内容;若是文件则转为稳定资产引用,接口解析 |
@@ -121,7 +124,9 @@ erDiagram
主键 id;索引 `(parent_id, sort_order, id)`;CHECK `parent_id IS NULL OR parent_id <> id`。父分类必须存在,导入按树层级排序;多层循环不能靠上述 CHECK 判断。删除被题型或子分类引用的分类返回冲突。
分类负责数学、语文、手工等内容方向,年级是独立适用维度,不把“一年级数学”“二年级数学”复制为分类,也不把年级放入 categories.parent_id。Web 的“年级 → 分类”菜单是组合查询,不代表数据库父子关系。年级名称及展示顺序先使用固定配置,不新增 grades/菜单表。categories 表结构保持不变。
分类负责数学、语文、手工等内容方向,年级是独立适用维度,不把“一年级数学”“二年级数学”复制为分类,也不把年级放入 categories.parent_id。Web 的“年级 → 分类”菜单是组合查询,不代表数据库父子关系。年级名称及展示顺序先使用固定配置,不新增 grades/菜单表。分类增加发布状态,不增加年级菜单字段。
分类、题型和资源统一使用三态发布规则;上级未上线时下级也不可公开读取,隐藏上级不改写下级自身状态。管理预览及快照失效规则见 [内容发布与可见性](./内容发布与可见性.md)。
### 3.3 worksheets — 题型
@@ -443,7 +448,7 @@ ORDER BY w.sort_order, w.updated_at DESC, w.id;
登录使用服务端配置的单一小程序 AppID,按微信返回的可信 `openid` 查找用户,沿用老 ID。首次并发登录以唯一约束兜底,冲突后读取已创建用户。首期 JWT 使用 userId + authVersion;每次鉴权核对用户存在、active 和版本,role 以当前服务端数据为准。
`auth_version` 是账户级登录凭证版本,默认 1。JWT 签发时写入相同的 `authVersion`,每次访问受保护接口时与数据库当前值比较;不一致返回 401,要求重新登录。需要强制全部设备退出或撤销已签发凭证时,由业务服务在事务中递增版本;普通登录和资料更新不递增,避免其他设备无故退出。它撤销的是该用户所有旧版本 token,不用于单设备退出。账户禁用及角色调整仍以数据库当前 status/role 校验,版本不能替代权限检查。现有守卫已实现版本比较,真实 AccountReader 与主动递增业务尚待接入。
`auth_version` 是账户级登录凭证版本,默认 1。JWT 签发时写入相同的 `authVersion`,每次访问受保护接口时与数据库当前值比较;不一致返回 401,要求重新登录。需要强制全部设备退出或撤销已签发凭证时,由业务服务在事务中递增版本;普通登录和资料更新不递增,避免其他设备无故退出。它撤销的是该用户所有旧版本 token,不用于单设备退出。账户禁用及角色调整仍以数据库当前 status/role 校验,版本不能替代权限检查。现有守卫与真实 AccountReader 已接入版本比较,主动递增版本的业务服务仍待接入。
涉及用户的写事务先锁用户行并核对状态,再锁题型行、需要时锁内容行;多个题型及同层内容按 ID 固定顺序加锁。收藏、下载、资料修改和注销遵守相同锁顺序,避免注销与新写入交叉。
@@ -494,7 +499,7 @@ DO NOTHING RETURNING id;
真实计数/seed 修订使用明确管理动作,锁题型行、记录前后值和 reason、同事务写审计。批量操作每项独立事务,保留逐项结果;dryRun 做同样校验且不写入。不将整批部分失败伪装为完全成功。
展示 likes/downloads = 原始值 + 对应 seed;管理 rawStats 不加 seed。保持现有首页 Top5、分龄 Top3 下载 + Top3 收藏再全量下载补齐的种子参与规则。分龄严格采用 `ageMin == 下限 AND ageMax >= 上限`;分龄并列沿用 updatedAt DESC,再以 id 稳定排序。首页并列新增稳定 id 排序,并在固定样本验收中记录差异。
展示 likes/downloads = 原始值 + 对应 seed;管理 rawStats 不加 seed。保持现有首页 Top5、分龄 Top3 下载 + Top3 收藏再全量下载补齐的种子参与规则。旧分龄推荐沿用 `ageMin == 下限 AND ageMax >= 上限`(尚未迁移);2026-10-16 用户确认普通 `/worksheets?ageBand=...` 筛选改为 `ageMin <= 下限 AND ageMax >= 上限`,只返回完整覆盖所选年龄段的题型;分龄并列沿用 updatedAt DESC,再以 id 稳定排序。首页并列新增稳定 id 排序,并在固定样本验收中记录差异。
### 5.4.1 新品、运营推荐与两层下载统计
@@ -516,7 +521,7 @@ worksheets 表示功能,worksheet_resources 表示可选内容。新增“小
首期题型量小,可在短事务中完成查询与 JSON 构建;若构建移出事务,提交前比较 expectedVersion,冲突必须重新读取重建,不能覆盖新配置。自动刷新只覆盖 featured/hot,保留 sections 等手动配置。home/all 构建清 category/age,category 分支只重建 category;原调用中空数组被当作“沿用现有配置”的行为由兼容层保留。
公共 API 直接读取 DB 配置。若兼容期仍发布静态 JSON,提交后用 `page_config_publish` 执行记录重试,文件采用按版本不可变对象 key;publishedVersion 只在对应版本上传成功后前移,防止旧发布覆盖新版本。签名 URL 不进入公共静态配置,私有头像也不进入公共页面 JSON。静态发布失败不回滚已经提交的业务配置,保留上一可用文件并明确发布状态。
公共 API 直接读取 DB 配置。若兼容期仍发布静态 JSON,提交后用 `page_config_publish` 执行记录重试,文件采用按版本不可变对象 key;publishedVersion 只在对应版本上传成功后前移,防止旧发布覆盖新版本。签名 URL 不进入公共静态配置,私有头像也不进入公共页面 JSON。静态发布失败不回滚已经提交的业务配置,保留上一可用文件并明确发布状态。内容下架撤销属于例外:必须立即取消旧展示快照的可用标记,不能以“上一可用文件”继续交付下架内容;新后端当前会清空数据库快照,外部分发及客户端缓存撤销在切换前补齐。
## 6. CloudBase 导入与审计
@@ -594,7 +599,7 @@ S0 仍需确认:全部线上集合及配置文档、来源 AppID、历史字
## 9. SQL 与实际结构最终核查
核查日期:2026-10-14。当前已确认范围为 13 张表、135 个字段;本次核查未新增表或业务字段。
核查日期:2026-10-14。当时已确认范围为 13 张表、135 个字段;该次核查未新增表或业务字段。2026-10-16 新增 categories.status 后为 136 个字段,升级及发布规则见 [内容发布与可见性](./内容发布与可见性.md)。
| 核查项目 | 结果 |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------- |
@@ -607,4 +612,4 @@ S0 仍需确认:全部线上集合及配置文档、来源 AppID、历史字
临时核查数据库全部清理;开发库只读比对,未写入样本。SQL 无结构差异需修复;已同步过时的 Prisma 目录说明、统一执行路径,修正关系图中的必填封面/可缺失历史关联及资产清理引用范围。
数据库约束通过不等于业务迁移完成。资源归属、当前发布状态/管理员权限、参数白名单、首次发布时间只写一次、动态新品判断、计数与审计事务、任务锁和租约仍由后续服务端实现;Prisma Schema 和迁移历史尚未接入。旧 grade/is_new/total_downloads 等字段继续按前述兼容规则保留,不作为新业务权威来源。
数据库约束通过不等于业务迁移完成。资源归属、当前发布状态/管理员权限、参数白名单、首次发布时间只写一次、动态新品判断、计数与审计事务、任务锁和租约仍由后续服务端实现;Prisma Schema 和迁移基线已接入。旧 grade/is_new/total_downloads 等字段继续按前述兼容规则保留,不作为新业务权威来源。