Files
doodle-store/docs/v3.5/题型查询接口说明.md
T

128 lines
9.5 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.
# 题型查询接口
更新日期:2026-10-16。实现范围:公开题型列表、详情和组合筛选,供小程序及未来 Web 共用。题型管理预览及状态写入已在独立管理员接口补齐;尚未实现题型配置编辑、分龄推荐 Top3 混排或首页构建,doodle-mini 分类页现已通过全局环境配置接入新接口。
## 接口与示例
统一前缀为 `/api/v1`,无需登录,固定要求自身 `status=active` 且所属分类及全部祖先已上线。客户端不能传 status、rawStats 等参数读取草稿、隐藏内容或管理统计;未识别参数返回 400。
| 用途 | 请求 |
| -------------------- | --------------------------------------------------------------------------- |
| 全部题型(分页) | `GET /worksheets?page=1&pageSize=20` |
| 本批全部 51 条 | `GET /worksheets?pageSize=100` |
| 按分类 | `GET /worksheets?categoryId=math` |
| 按单个年级 | `GET /worksheets?grade=1` |
| 按多个年级,任一匹配 | `GET /worksheets?grades=-1,0,1` |
| 按年龄段 | `GET /worksheets?ageBand=3-4` |
| 按单个年龄 | `GET /worksheets?age=5` |
| 按 Tag | `GET /worksheets?tag=描红` |
| 最新排序 | `GET /worksheets?sort=latest` |
| 热门排序 | `GET /worksheets?sort=hot` |
| 人工运营热门标记 | `GET /worksheets?isHot=true` |
| 新品标记 | `GET /worksheets?isNew=true` |
| 组合筛选 | `GET /worksheets?categoryId=math&ageBand=3-4&tag=数学&sort=hot&pageSize=20` |
| 详情 | `GET /worksheets/number-find` |
中文 Tag 由 HTTP 客户端正常 URL 编码。查询参数与实际路由已加入开发 Swagger:`http://127.0.0.1:8800/api-docs`。
## 筛选与排序规则
- 不同维度的条件按 AND 组合;多年级内部任一匹配。分类按原 ID 精确匹配,不自动包含子分类。
- 年级只使用审核后的 `worksheet_grades`,不读取旧 `worksheets.grade` 兜底。支持 -4~6,0 表示幼小衔接。`grade` 和逗号分隔的 `grades` 互斥;多重匹配不重复返回题型。
- `ageBand` 支持 3-4、4-5、5-6、6-7、7-8;题型适用范围须完整覆盖所选年龄段,即 ageMin ≤ 下限且 ageMax ≥ 上限。例如 3~8 岁题型可命中 3-4、4-5、5-6、6-7、7-8;3~5 岁题型不能命中 5-6,仅边界相交不足以匹配。公开列表与管理员列表共用此规则。
- `age` 是单年龄范围查询,要求 ageMin ≤ age ≤ ageMax,与 ageBand 互斥。年龄范围为非负 smallint。
- Tag 为字符串数组成员的精确匹配,区分大小写,不进行标题搜索、模糊匹配或推荐补齐。
- 默认排序:sortOrder 升序 → updatedAt 降序 → id 升序。
- 最新排序:firstPublishedAt 降序;旧数据未知时按 createdAt 降序 → id 升序。不使用会被计数变化刷新的 updatedAt 充当发布时间,也不改写原日期。
- 热门排序:downloads + downloadsSeed 降序 → likes + likesSeed 降序 → updatedAt 降序 → id 升序。数据库在完整筛选集排序后分页,不先取 100 条再排序,统计相加不会发生 bigint 溢出。
- `isHot` 独立筛选人工运营标记,不等同于下载热度排序,支持 true/false。
- `isNew` 依据真实 firstPublishedAt 及 `WORKSHEET_NEW_WINDOW_DAYS` 判断,默认 30 天;时间未知、未来或窗口外均为 false,旧 is_new 不作为新接口判断依据。最新排序与“仅新品”是两种查询,可分别使用。
## 分页及公开响应
page 默认 1,范围 1~1000000;pageSize 默认 20,范围 1~100。超过 100 条可继续翻页,没有旧云函数固定截断 100 条的问题。超过最后一页仍返回正确 total、空 items 和 hasNext=false。
```json
{
"code": 0,
"message": "ok",
"data": {
"items": [
{
"id": "number-find",
"title": "找数字,涂一涂",
"subtitle": "找出目标数字并涂色",
"category": "math",
"subcategory": "math-draw",
"path": "/mathPages/mathDraw/mathDraw?id=number-find",
"ageMin": 3,
"ageMax": 5,
"grades": [],
"difficulty": 1,
"previewImg": null,
"tags": ["数学", "数字认知", "涂色"],
"isNew": false,
"isHot": false,
"sortOrder": 101,
"downloads": "335",
"likes": "24",
"createdAt": "2026-04-29T05:06:22.720Z",
"updatedAt": "2026-10-15T07:17:05.132Z",
"contentUpdatedAt": null,
"firstPublishedAt": null
}
],
"total": 1,
"page": 1,
"pageSize": 20,
"hasNext": false
}
}
```
上述为响应结构示例,非默认第一页结果。downloads/likes 均为**含对应 seed 的展示值**,采用十进制字符串,避免 64 位计数在 JSON/JavaScript 中丢失精度。数据库原值不变;前端不要再次叠加 seed,不应把这些字段当作原始管理统计。
详情响应同样为 `{code:0,message:"ok",data:题型对象}`。不存在和未发布的题型均返回 404。公开白名单不含旧 grade、生成器配置、rendererKey、内部资产 ID、原 cloud:// 引用或对象存储 key。
列表与 total 在同一 Repeatable Read 事务、同一条件和时间点下读取,避免查询期间并发修改导致当次分页总量与内容使用不同快照。跨请求翻页仍可能受到新的业务写入影响,不承诺长时间固定快照。
## 预览图与配置
已按用户确认采用“返回题型文字/元数据,未就绪预览留空”的方式。预览返回 URL 前,必须同时满足资产 ready、public、purpose=preview、无私有用户归属、图片 MIME。
- 旧源引用、pending/failed/deleting/deleted、私有文件或非图片均返回 previewImg=null。
- 七牛公共资产:配置 `QINIU_PUBLIC_DOMAIN` 为 HTTPS origin,按对象 key 逐段编码;缺域名不猜地址、不返回 key。
- 经核实的 external 资产:仅输出无凭据、无查询参数或 fragment 的稳定 HTTPS URL;不输出带签名的临时地址。
`QINIU_PUBLIC_DOMAIN` 只用于只读地址解析,不代表上传、私有签名或文件迁移已经接入。新配置均在 `apps/server/.env.example`;实际密钥不属于查询接口范围。
## 当前 51 条数据的结果
2026-10-16 只读联调并逐项对照原附件:
| 查询 | total |
| ---------------------------------------- | ----- |
| 全部 | 51 |
| math | 22 |
| english | 7 |
| pinyin | 5 |
| chinese | 6 |
| puzzle | 11 |
| ageBand=3-4 | 10 |
| tag=字母 | 7 |
| categoryId=math & ageBand=3-4 & tag=数学 | 5 |
热门前 3:word-coloring-grid(774)、number-find(335)、number-decompose(325);最新前 3:clock-connect、clock-reading、word-test-sheet。
当前 51 张预览全部 pending,因此 previewImg=null;所有真实首次发布时间未知,isNew=false;审核后的年级关系尚为空,所以带 grade/grades 的查询暂时没有结果。这些条件不会用旧 grade/is_new 或临时图片地址自动补齐。年级筛选功能已经用独立样本验证,后续审核并写入关系即可命中。
已通过 `pnpm check`、`pnpm test:db`,临时库覆盖公开状态隔离、组合筛选、多年级去重、Tag 精确匹配及注入文本、false/0/重复参数校验、121 条完整分页、稳定并列顺序、bigint 展示/排序、真实新品、预览访问策略和 Swagger 参数声明。开发库实测只读,未修改题型、年级、资产或审计。
## 发布控制补充(2026-10-16)
公开列表、total、分页与详情统一过滤整条所属关系,不能通过热门、分类之外的查询或已知 ID 绕过。分类下架后其题型不再公开,恢复分类不改写题型自身状态。管理员通过 /admin/worksheets 查询草稿或下架内容;/admin/worksheet-resources 提供资源管理预览。状态更新、首发时间、页面快照失效与审计同事务。接口与客户端切换边界见 [内容发布与可见性](./内容发布与可见性.md)。
## 年龄段匹配调整(2026-10-16)
用户确认将普通题型查询从“最小年龄等于年龄段下限”改为“适用范围完整覆盖所选年龄段”。本次仅修改 ageBand 匹配语义,不修改 grade/grades、age/ageBand 参数格式,不改数据库或小程序云函数。旧分龄推荐接口尚未迁移,其匹配和榜单规则不属于本次普通列表变更。