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

9.5 KiB
Raw Blame History

题型查询接口

更新日期: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。

{
    "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 提供资源管理预览。状态更新、首发时间、页面快照失效与审计同事务。接口与客户端切换边界见 内容发布与可见性。

年龄段匹配调整(2026-10-16)

用户确认将普通题型查询从“最小年龄等于年龄段下限”改为“适用范围完整覆盖所选年龄段”。本次仅修改 ageBand 匹配语义,不修改 grade/grades、age/ageBand 参数格式,不改数据库或小程序云函数。旧分龄推荐接口尚未迁移,其匹配和榜单规则不属于本次普通列表变更。