Files
doodle-store/docs/v3.5/Worksheet数据库设计说明.md
T
2026-10-23 16:52:47 +08:00

174 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Worksheet 数据库设计说明
更新日期:2026-10-14。配套 [数据库设计文档](./数据库设计文档.md) 和 [SQL 执行说明](../../apps/server/prisma/sql/README.md)。本文说明已经确认的模型及边界,不表示小程序或业务 API 已完成改造。
## 1. 需求与选择
现有小程序的 worksheet 对应一个可打印功能,多次生成可以得到不同结果。未来涂色、剪纸等功能复用同一种绘制方法,但需要选择不同底图;一份内容还可能包含多个 SVG 或图片,例如“小猫涂色”需要线框与彩色参考图,并有用于内容列表的效果预览。
只在 worksheets 放单个 asset_id 无法表达多份内容及每份内容的多个输入。固定 outline_asset_id/reference_asset_id 能满足一对文件,却无法容纳多个同用途文件。完整 template 模型又容易提前引入独立布局、组合引擎、版本编辑等尚无明确需求的能力。
因此采用 worksheet → 完整内容资源 → 文件关联 → assets。资源条目只管理标题、预览、发布状态和少量参数差异;文件关联只管理用途和顺序,不建设通用模板系统。四张表不要求四套后台页面,一个内容编辑表单即可维护全部关系。
## 2. 各层职责
| 层级 | 表 | 职责 |
| -------- | ------------------------- | ------------------------------------------------------------------------ |
| 功能 | worksheets | 功能身份、分类、收藏和统计;生成方式、绘制实现、共用参数、入口封面 |
| 内容 | worksheet_resources | 用户可选择的完整内容,例如“小猫涂色”;标题、内容预览、状态及轻量参数覆盖 |
| 文件输入 | worksheet_resource_assets | 内容需要哪些文件;outline/reference 用途及同用途处理顺序 |
| 文件 | assets | 七牛或已核实 HTTPS 文件的稳定引用、格式、大小、校验摘要及状态 |
```mermaid
erDiagram
worksheets ||--o{ worksheet_resources : "一个功能有多份内容"
worksheet_resources ||--o{ worksheet_resource_assets : "一份内容有多个文件"
assets ||--o{ worksheet_resource_assets : "文件可重复利用"
assets ||--o{ worksheet_resources : "内容预览"
assets ||--o{ worksheets : "功能封面"
```
基础 SVG、图片及较大的路径 JSON 放七牛,数据库不存临时签名 URL。参数 JSON 只保存小型、经过校验的配置,不存绘制代码。
## 3. worksheet ID 与 renderer 的分工
现有 [数学注册表](../../../doodle-mini/miniprogram/mathPages/mathDraw/registry.ts) 已将 addition-5、addition-10 等业务 ID 映射到同一 addition 实现及不同模式,说明功能身份与绘制实现并非一一对应。
| 标识或配置 | 作用 |
| --------------------- | --------------------------------------------------------------------- |
| worksheet.id | 稳定业务入口,用于路由、收藏、分享、统计及资源归属 |
| renderer_key | 从客户端已注册实现中选择绘制方法;多个 worksheet 可共用,不设唯一约束 |
| generation_mode | procedural 算法生成;resource 由完整内容资源驱动,不是“是否随机” |
| render_config | 同一功能共用参数和默认模式 |
| worksheet_resource.id | 用户本次选中的具体内容 |
| config_override | 内容与共用参数的少量差异,可空 |
调用顺序:通过 worksheetId 获取配置 → 选择 renderer → 根据需要选择内容 → 解析文件引用 → 绘制。仍由客户端执行绘制;增加新 renderer 需要发布客户端代码,数据库配置不能凭空增加算法。
保留原 path?id=worksheetId、旧 ID 和分享路径。renderer_key 允许 NULL 仅为了兼容已登记的旧映射;新功能发布必须设置已支持标识。未知标识或缺少必须文件时返回明确错误,不默认绘制为另一个功能。
## 4. 预览与多文件示例
```text
worksheet:动物涂色
renderer_key:coloring
generation_mode:resource
render_config:统一纸张尺寸、边距
preview_asset_id:功能入口封面
worksheet_resource:小猫
preview_asset_id:小猫列表效果图
config_override:无
文件关联:
outline / 0:小猫线框 SVG
outline / 1:补充线框 SVG(绘制实现支持时)
reference / 0:小猫彩色参考 PNG
```
- 功能封面用于首页和分类入口,保持原 previewImg 契约。
- 内容预览用于内容选择列表,每条内容各有 previewImg;通过分页接口返回,不把所有预览塞进 worksheet 详情。
- reference 用于绘制页面的对照展示;outline 是实际线框输入。参考图是否打印由 renderer 决定,不因关联就自动加入打印结果。
- 三种用途可以引用同一资产,也可以使用独立文件;资产去重不等于业务内容合并。
- 关联文件默认共同服务一份内容,不是随机候选池;随机选“小猫或恐龙”应选择内容条目。素材池抽取是另一种明确规则。
assets.purpose 描述文件管理用途,新增 worksheet_source;role 描述文件在该内容中的用法,仅支持 outline/reference。参考图可复用 preview 文件,SVG/PNG/JSON 格式由文件元数据和 renderer 校验,不作为 role。
## 5. 轻量配置、发布和权限
参数顺序是 renderer 默认值 → worksheet 共用参数 → 内容覆盖 → 允许的用户参数。首期采用按 renderer 定义的顶层参数覆盖,仅允许边距、缩放方式等明确字段;不支持任意 JSON 深度合并、脚本或文件级配置。
同用途文件按 sort_order、id 稳定排序;顺序不是通用图层 z-index。数量、排布及格式要求由 renderer 定义,不在数据库中新增绘制需求规则表。
管理员维护内容及关联;普通用户只能读取已发布功能下的已发布内容。发布需校验 renderer 支持、必须文件及其数量、资源 ready 状态、格式、参数范围和访问策略。SQL 外键只保证引用存在,不能替代发布状态或文件真实存在的检查。私有资源按策略生成访问地址,不暴露七牛密钥。
首期仅上下架,不硬删除功能或内容。关联采用 RESTRICT,删除共享资产前核对所有封面、内容预览、文件关联及其他引用;软删除同样须由服务检查。发布/修改/关联及管理审计使用同一事务,不在事务内等待七牛上传。内容及文件关联修改由服务同时维护资源 updated_at、父功能 content_updated_at/updated_at,不增加通用更新时间触发器。
## 6. 收藏、下载与旧数据
favorites 统一保存功能和具体内容收藏:worksheet_resource_id 为 NULL 表示功能,非 NULL 表示“小猫涂色”等完整内容,不直接收藏单个 asset。两种部分唯一索引分别去重;同用户可同时收藏功能及其中多个资源,不自动联动,不新增 resource_favorites 表。旧查询/数量/取消必须限定 NULL,内容查询和取消显式按资源目标。仅功能收藏增减 worksheets.likes,内容收藏不新增资源 likes。下载增加可空 worksheet_resource_id,记录选择的完整内容,而不是只记录其中一个文件。资源驱动的新下载要求资源 ID,校验归属及发布状态;算法生成和旧历史允许为空。相同 exportId 的重试同时核对功能和内容 ID,保持事件一致性。
下载同时维护功能与有明确归属的内容累计计数,只有新日志成功插入时同事务各加一次,失败全部回滚,重复 exportId 不加数。功能旧累计值不分摊资源,资源计数不与功能计数相加;个人注销删日志不回减任一层累计量。不增加资源收藏计数或 seed。资源 ID 是逻辑关联,缺失或隐藏的历史内容不导致整条记录丢失,也不泄露未发布详情。这个 ID 只表示选择过什么内容,不保证精确复现原打印结果;未来确需复现时另行设计选中素材、参数、随机种子及不可变版本快照。
增量升级保留旧 worksheet ID/path、预览、计数和历史,添加 procedural 默认值、NULL renderer_key、空共用配置;新内容表为空。导入按实际实现核对模式与映射,不从题型 ID 猜 renderer,不把旧预览图自动当作线框。新空库用最新 002/003;旧库依次使用未执行的 004/005/006/007/008,不重跑建表基线。Prisma 迁移历史接入时需登记对应结构版本。
## 7. 覆盖范围与后续扩展
| 场景 | 当前模型 |
| ------------------------------------- | ------------------------------------------------------ |
| 线框配参考图、独立列表预览 | 直接支持 |
| 多个同用途 SVG、资源文件共享 | 数据模型支持;客户端 renderer 须实现处理规则 |
| 同一绘制实现用于不同功能和内容 | 直接支持 |
| 部分内容调整少量绘制参数 | config_override 白名单覆盖 |
| 多页内容、每页不同文件布局 | 尚未表达页归属;真实需要时增加页结构 |
| 背景、装饰图层、单文件位置/透明度配置 | 首期不开放;同步扩展角色及 renderer 后再设计文件级配置 |
| 素材池随机组合 | 首期不把关联解释为候选池,后续明确选取协议 |
| 下载历史精确重现、版本编辑及回滚 | 当前不承诺;后续设计不可变内容版本与生成快照 |
当前只落实已经确定的身份、完整内容、文件用途、独立预览和少量配置差异,保留扩展点,但不预先建设模板引擎、通用图层系统或版本平台。
## 8. 内容分类与适用年级
categories 保持内容分类;worksheet_grades 用两列联合主键表达功能适用于哪些年级,支持多个及不连续年级。Web 年级菜单再按分类筛选,是两个维度的组合查询,不复制分类树。年级名称和排序暂用固定配置,不新增年级字典表。
旧 worksheets.grade 保留兼容,但其值可能由年龄区间推算,不能成为适用范围的唯一依据。006 仅建空关系表,不自动回填。经管理员审核后写入完整 grades,新筛选以关系表为准;空关系表示未配置,不表示全年级,0 表示幼小衔接。使用 EXISTS 查询防止多选年级导致重复结果。
资源条目暂继承功能适用年级;若同功能下内容难度跨年级明显不同,当前模型不提供资源级年级过滤,须先明确产品规则再扩展。年级与年龄独立维护,不根据年龄自动生成关系。详见数据库设计文档 4.6 节。
## 9. 功能与资源的新鲜度、热度和下载
功能早已固定不代表内容停止更新。功能和内容各增加可空 first_published_at,分别表达首次公开发布时间。新 isNew 按统一新品时间窗口派生,不新增资源布尔新品字段;草稿创建时间、参数更新时间、新增资源或重新上架时间不能替代首次发布时间。未知旧时间不回填为 now()/created_at,也不在重发时伪装新品。
worksheet.is_new 暂保留兼容:新接口以时间派生为准,旧接口仅在历史发布时间未知时可临时沿用原标记。worksheet.is_hot 定义为功能级人工运营推荐,与下载排行榜独立,不自动赋给资源。资源首期按真实 downloads 排名,不新增人工热门标记;若运营明确需要资源推荐,再扩展。
007 给内容条目增加 downloads,默认 0、非负,与 worksheet 总下载量分别维护。成功保存完整“小猫”内容一次:新增一条 download_logs,功能 +1,小猫 +1;关联两张 SVG 不代表下载两次。算法生成或旧事件无可信内容 ID,只更新功能;不能从资源文件访问次数推断打印成功。
事务按用户 → 功能 → 内容加锁,检查归属和发布状态,只有首次插入日志才加数;同一 exportId 的功能/内容 ID 不一致报冲突。失败回滚两层计数。正常下载仅更新业务时间,不更新内容/首次发布时间。功能历史基数和 seed 保留,不平均分配资源、不要求功能总量等于资源之和;删除个人日志不扣累计下载。
当前表结构支持这套规则,具体发布时间设置、isNew 派生、计数事务及兼容 DTO 后续由服务端实现;本次没有改变小程序现有显示逻辑。
## 10. 收藏具体底图的兼容调整
008 给统一 favorites 增加可空内容 ID,并将原 userId+worksheetId 全局唯一约束改为两个部分唯一索引。旧收藏保留原 ID、时间、归属且新列为 NULL,不猜测具体底图。新资源收藏的 worksheetId 从服务端读取,关系插入时重新核对父功能、内容发布状态及归属。
列表默认仅功能,资源/all 通过显式 target 选择;返回目标类型及资源独立预览。取消精确定位收藏记录或资源 ID,旧按功能取消不得删除内容收藏。资源缺失或下架只使详情不可用,内容 ID 不清空,仍可取消。注销清理两种收藏,仅功能关系贡献影响功能 likes。
数据库不强制资源外键以保留历史,发布内容不允许重新归属另一 worksheet。需要收藏某次算法生成结果、保存用户涂色进度或精确结果快照时另行设计;当前资源收藏只表示选中的完整底图内容。
## 发布可见性补充(2026-10-16)
沿用 worksheets 与 worksheet_resources 的 draft/active/hidden,不新增布尔上线字段。categories 增加同名 status,新建默认草稿;上级未上线会阻止下级公开读取,不覆盖下级状态。管理预览、上下架、首发时间及快照失效已实现,资源公开读取/生成/下载仍待后续接口接入相同策略。详见 [内容发布与可见性](./内容发布与可见性.md)。
## 独立纸张条目补充(2026-10-23)
worksheet 表示可以独立展示、分享、收藏、上下架和统计的业务条目,并不要求与绘制页面或绘制实现一一对应。田字格、米字格等需要独立运营时,各自占一条 worksheet,继续使用字符串业务 ID;不新增自增主键或含义重复的 type 字段。
| 条目 ID | 标题 | renderer_key | render_config |
| ---------------------- | ------------- | ------------ | -------------------------------- |
| paper-tian | 田字格 | paper-sheet | {"paperType":"tian"} |
| paper-mi | 米字格 | paper-sheet | {"paperType":"mi"} |
| paper-square | 方格 | paper-sheet | {"paperType":"square"} |
| paper-four-line | 四线三格 | paper-sheet | {"paperType":"four-line"} |
| paper-letter | 信纸 | paper-sheet | {"paperType":"letter"} |
| paper-horizontal | 横线 | paper-sheet | {"paperType":"horizontal"} |
| paper-vertical | 竖线 | paper-sheet | {"paperType":"vertical"} |
| paper-dictation-hanzi | 汉字听写 | paper-sheet | {"paperType":"dictation-hanzi"} |
| paper-dictation-pinyin | 拼音/英语听写 | paper-sheet | {"paperType":"dictation-pinyin"} |
| paper-homework-log | 作业登记表 | paper-sheet | {"paperType":"homework-log"} |
小程序纸张选项和本地条目元数据统一维护于 `config/worksheets/papers.ts`。服务端在 `paper-sheet-presets.ts` 显式登记支持的 ID、路由和参数;发布与旧格式导入复用此映射,不开放任意渲染配置写入。公开接口仅对已匹配的独立条目增加 `rendering: {rendererKey: "paper-sheet", paperType: "mi"}` 等白名单摘要,不透传原始 render_config。客户端同时校验条目 ID 和纸张类型,不认识的条目或错配配置不能默认生成田字格。
独立条目初始化使用工作区根目录命令:
```sh
pnpm paper-sheets:seed
pnpm paper-sheets:seed --apply
```
第一条仅预演;第二条只允许本机 `doodle_dev`,事务创建缺少的十种纸张草稿并记录审计、撤销展示快照。已有相同配置条目不修改,ID 被其他配置占用时整批回滚。初始化需要原 `paper-sheet` 已存在,用它的分类和封面建立草稿,年龄和标题来自独立条目定义;仅复制明确配置过的年级关系,不从年龄推断。
草稿暂借综合入口预览,不能把该封面当作各纸张真实预览。开发版进入相应独立 ID 后,直接使用客户端已注册配置进行本地预览,不请求公开详情或通过 404 判断草稿;可通过调试发布工具导出当前纸张封面并发布。体验版和正式版必须读取到 active 详情及匹配绘制摘要才允许生成,404 或网络失败均不能降级为本地预览。新增条目首次发布时间为空,计数及 seed 从零开始,发布流程负责首次发布时间。不存在原综合入口时可直接通过开发版逐张创建条目。
旧 `paper-sheet` 的 ID、记录、收藏、计数和发布时间保留,不向新条目分摊。旧链接的 `type` 参数继续生效,综合入口的新分享通过 `mode` 保留纸张选择;进入综合入口时仍以综合条目为收藏和下载目标,主动切换纸张则转为对应独立条目,之后分享、发布、收藏和下载都指向它。颜色变化继续作为用户参数,不改变业务身份。
本次复用既有字段,不需要 DDL 迁移。worksheet_resources 仍管理“小猫涂色”等功能下面的具体内容,不将这十种独立纸张重复建为资源。