feat: 完成现有小程序接口的全部迁移至新后端
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 小程序云接口迁移清单
|
||||
|
||||
2026-10-19:除微信数据预拉取外,本地开发中的小程序运行代码已切换到 doodle-store HTTP 接口;当前已发布的线上版仍使用旧云开发接口。以下路径均以 `/api/v1` 为前缀。新接口共用成功信封 `{code:0,message:"ok",data:...}`,由 Swagger 展示已注册的参数。
|
||||
|
||||
## 接口对照
|
||||
|
||||
| 旧调用或功能 | 新接口 | 权限 |
|
||||
| ---------------------------------------- | ----------------------------------------------------------------------- | -------------- |
|
||||
| `userLogin` | `POST /auth/wechat/login` | 微信 code 验证 |
|
||||
| 本人资料 | `GET /me`、`PUT /me/profile` | 本人 |
|
||||
| `userLogin.updateProfile` | `PUT /me/profile`,头像传 `avatarAssetId` | 本人 |
|
||||
| 头像云存储上传 | `POST /me/avatar-upload`,multipart:`file`、UUID `uploadId` | 本人 |
|
||||
| `userSettings.clearProfile` | `DELETE /me/profile` | 本人 |
|
||||
| `userSettings.deleteAccount` | `DELETE /me` | 本人 |
|
||||
| `userFavorites.add/remove` | `PUT /me/favorites/{worksheetId}`、`DELETE /me/favorites/{worksheetId}` | 本人 |
|
||||
| `userFavorites.list` | `GET /me/favorites?page=1&pageSize=20` | 本人 |
|
||||
| `userFavorites.check/batchCheck` | `POST /me/favorites/check`,`{worksheetIds:[]}` | 本人 |
|
||||
| 收藏/下载计数 | `GET /me/stats` | 本人 |
|
||||
| `userDownloadLogs.add` | `POST /me/downloads`,`{worksheetId,exportId}` | 本人 |
|
||||
| `userDownloadLogs.list` | `GET /me/downloads?page=1&pageSize=30` | 本人 |
|
||||
| `ageRecommendedQuery` | `GET /recommendations/age?ageBand=5-6` | 公开 |
|
||||
| `pageConfigFetch` 动态兜底 | `GET /page-config` | 公开 |
|
||||
| `categoriesQuery/Create/Update` 管理调用 | `GET/POST /admin/categories`、`PUT /admin/categories/{id}` | 管理员 |
|
||||
| 分类删除 | `DELETE /admin/categories/{id}` | 管理员 |
|
||||
| `worksheetsQuery` 管理调用 | `GET /admin/catalog/worksheets`,完整分页、真实计数与 seed 分开 | 管理员 |
|
||||
| `worksheetsUpdateStatus` | `PUT /admin/worksheets/{id}/status` | 管理员 |
|
||||
| `worksheetsBatchPatch` | `POST /admin/worksheets/batch`,`{patches,dryRun}`,最多 500 项 | 管理员 |
|
||||
| 预览图云存储上传 | `POST /admin/assets/preview-upload`,multipart:`file`、UUID `uploadId` | 管理员 |
|
||||
| `worksheetsPublish` | `POST /admin/worksheets/publish`,元数据与 `previewAssetId` | 管理员 |
|
||||
| 首页内容管理读取 | `GET /admin/site/config` | 管理员 |
|
||||
| `pageContentBuild` | `POST /admin/site/rebuild`,`page=home/category/all` | 管理员 |
|
||||
| 云数据库 `settings.homeAutoRefresh` 读取 | `GET /admin/site/timer` | 管理员 |
|
||||
| `homeTimerControl` | `PUT /admin/site/timer`,`{enabled}` | 管理员 |
|
||||
|
||||
公开分类、题型查询仍为既有 `/categories`、`/worksheets`、`/worksheets/{id}`。Web 原有 PATCH 分类/发布状态接口保留,同时提供 PUT 入口适配小程序请求方法。
|
||||
|
||||
## 预拉取保留范围
|
||||
|
||||
- 保留 `wx.getBackgroundFetchData` 及微信后台现有预拉取地址。
|
||||
- 预拉取/缓存先渲染首屏;随后后台读取新 `/page-config`,更新内存、缓存与首页。旧响应迟到不能覆盖更新的请求。
|
||||
- 动态接口每次按当前题型、分类祖先和资产状态重新过滤,不会借旧快照恢复下架内容。管理变更后主动刷新首页配置。
|
||||
- 新的首页刷新开关控制新后端快照任务。任务默认 UTC 22:00(北京时间次日 06:00)运行,可通过 `SITE_REFRESH_HOUR_UTC` 调整。任务使用 `job_runs` 唯一键和租约去重、有限重试,服务进程需要在线。
|
||||
- 旧云端预拉取文件的生成、发布链路和定时任务继续保留,不由新开关控制;新后端不改该文件、不修改预拉取地址。
|
||||
- 当前线上版仍依赖旧云开发。全部旧云函数源码、部署配置及线上服务均须保留,不将“本地开发版已无调用”作为删除、停用或修改旧接口契约的依据。未经另行明确安排,不迁移预拉取模式,不清理旧云函数、云数据库或云存储。
|
||||
|
||||
## 数据与权限
|
||||
|
||||
- 所有用户接口只使用本站 JWT 确认的用户 ID;运行代码已删除旧登录兼容层、通用云调用封装与无调用方的旧统计接口。
|
||||
- 收藏以用户+题型幂等;下载以用户+`exportId` 幂等。记录与真实计数同事务提交,下载只在保存图片成功后上报。原页面用户变化时停止补报。
|
||||
- 本人列表只查本人数据;已下架或删除的题型仅保留记录,附属 `worksheet` 返回 null。用户删除不破坏其他用户数据。
|
||||
- 管理接口使用当前数据库角色校验,写事务重新锁定核对身份;不会根据 develop 环境或“第一个用户”自动授予管理员。
|
||||
- 资料只允许昵称和本人 ready 头像资源 ID,不接受 OpenID、role、userId 或任意头像 URL。头像按用户确认放在同一七牛公开空间,仍要求本人拥有且用途正确。
|
||||
- 头像/预览只接收完整 PNG/JPEG,单文件不超过 2 MiB,并校验像素尺寸。服务端持有 AK/SK,以固定空间、明确对象 key 和仅新增策略上传,上传后核对远端 ETag/大小。相同 `uploadId` 和内容可恢复,冲突不覆盖。
|
||||
- 当前单实例限制每人每小时新增 30 个上传标识,同一标识重试不重复计数;多实例硬配额后续使用共享限流存储。
|
||||
- 替换或清除头像将旧资源标记 deleting;每分钟后台清理七牛对象,失败保留队列重试。注销与慢上传交错时再次安排清理。
|
||||
- 管理批次整批验证、原子提交,支持不写库预演。真实统计和展示种子分开,允许管理员显式重置统计;不会把合并展示值悄悄写回真实计数。
|
||||
|
||||
## 配置与迁移
|
||||
|
||||
服务端 `QINIU_PUBLIC_DOMAIN`、`QINIU_KEY_PREFIX`、`QINIU_BUCKET` 与 asset-sync 一致;新增上传需 `QINIU_AK`、`QINIU_SK` 成对配置,`QINIU_REGION` 可选。实际凭据只在被忽略的 `.env` 中,本地已沿用 asset-sync 的配置,不下发小程序。
|
||||
|
||||
迁移 `20261019000000_member_assets` 允许公开头像,并为本人变更加入 user 审计类型,已应用到本地 doodle_dev。它不搬运旧云端用户、收藏或下载历史;这些历史数据需要单独导出并导入、核对用户关联后再切换正式环境。旧云函数仍服务当前线上版,并承担旧预拉取链路维护;源码和线上服务均继续保留。
|
||||
|
||||
## 验证
|
||||
|
||||
- `pnpm typecheck`:后端类型检查。
|
||||
- `pnpm test`:后端 HTTP、校验、SDK 适配与资源工具回归。
|
||||
- `pnpm test:db`:自动创建并删除临时库;覆盖权限、并发幂等、归属、发布与回滚、清理和注销,不向真实七牛上传测试文件。
|
||||
- 小程序:`node --import tsx --test tests/auth-api.test.mjs tests/category-api.test.mjs tests/preview-assets.test.mjs`。
|
||||
|
||||
真实微信 code 兑换、微信后台白名单及真机上传仍需使用实际微信环境联调。生产域名当前尚未确定,体验/正式版不会回退到本机接口。
|
||||
@@ -311,7 +311,7 @@ UNIQUE source_ref_hash、UNIQUE object_ref_hash(可空);索引 `(owner_use
|
||||
|
||||
qiniu ready 必须具备 bucket、objectKey、objectRefHash、mimeType、sizeBytes 和 checksum;external ready 必须有经核实的 externalUrl,无法取得内容摘要时在报告注明。源文件下载、上传与校验完成后才 ready,再写业务外键。DB 存引用,访问域名从服务配置取;私有签名 URL 在响应时生成,不能写回数据库或页面配置。
|
||||
|
||||
SQL 检查 ready 必填字段、external 的 HTTPS 前缀、avatar 必须 private,以及摘要/hash 为 64 位小写十六进制。摘要计算和规范化由服务端完成;HTTPS 前缀检查不代替域名白名单或远程文件校验。
|
||||
SQL 检查 ready 必填字段、external 的 HTTPS 前缀、头像已按用户确认允许 public(仍核对本人归属),以及摘要/hash 为 64 位小写十六进制。摘要计算和规范化由服务端完成;HTTPS 前缀检查不代替域名白名单或远程文件校验。
|
||||
|
||||
上传和对象存储操作不放在长数据库事务中;先完成上传并登记资产,再短事务绑定业务记录。未绑定文件由清理流程回收,清理前检查 users、worksheets、worksheet_resources、worksheet_resource_assets、categories 和 page_configs 的所有引用。旧桥复制云文件后也必须遵循同一路径。
|
||||
|
||||
@@ -452,7 +452,7 @@ ORDER BY w.sort_order, w.updated_at DESC, w.id;
|
||||
|
||||
涉及用户的写事务先锁用户行并核对状态,再锁题型行、需要时锁内容行;多个题型及同层内容按 ID 固定顺序加锁。收藏、下载、资料修改和注销遵守相同锁顺序,避免注销与新写入交叉。
|
||||
|
||||
清除资料:昵称与头像引用置 NULL,保留用户、收藏和历史;解除引用后清理用户私有文件。注销:锁用户 → 只对功能收藏(worksheet_resource_id IS NULL)涉及的现存题型按 ID 加锁并减少 likes → 删除功能及内容两种收藏、下载日志 → 解除头像及其他私有资产关联,标记清理 → 清理身份审计关联 → 删除用户,全部数据库修改一事务。用户不存在时旧 token 立即无效。注销后再次微信登录创建全新 userId,不复用已删除账户,不恢复旧记录。
|
||||
清除资料:昵称与头像引用置 NULL,保留用户、收藏和历史;解除引用后清理被解除绑定的头像文件。注销:锁用户 → 只对功能收藏(worksheet_resource_id IS NULL)涉及的现存题型按 ID 加锁并减少 likes → 删除功能及内容两种收藏、下载日志 → 解除头像及其他私有资产关联,标记清理 → 清理身份审计关联 → 删除用户,全部数据库修改一事务。用户不存在时旧 token 立即无效。注销后再次微信登录创建全新 userId,不复用已删除账户,不恢复旧记录。
|
||||
|
||||
功能和资源下载计数都是累计成功保存次数,注销删除个人日志时不回减任一层 downloads。likes 仅表示当前功能收藏数量,注销需要扣除当前关系的贡献;若旧计数小于应扣数量,扣减保护为 0,并在同事务记录系统审计异常,不能得到负值,也不能因此阻止注销。
|
||||
|
||||
|
||||
+17
-3
@@ -186,7 +186,7 @@ flowchart LR
|
||||
|
||||
公开接口用 `@Public()` 明确声明,且不返回本人收藏状态;需要个人状态时单独请求 `/me/...`。现有 Public 装饰器会完全跳过守卫,不将它当作“有 token 就自动解析身份”的可选鉴权模式。
|
||||
|
||||
资产接口先验证 purpose、大小、格式、owner 和状态;头像只允许绑定本人拥有、用途为 avatar 且 ready 的私有资产。客户端传来的 assetId、worksheetId、resourceId 都必须做业务校验,不能仅因用户已登录便接受。
|
||||
资产接口先验证 purpose、大小、格式、owner 和状态;头像按用户确认使用公开空间,只允许绑定本人拥有、用途为 avatar 且 ready 的资产。客户端传来的 assetId、worksheetId、resourceId 都必须做业务校验,不能仅因用户已登录便接受。
|
||||
|
||||
### 5.3 管理权限与版本撤销
|
||||
|
||||
@@ -259,7 +259,7 @@ flowchart LR
|
||||
| `DELETE /me/profile` | 清昵称及头像引用,保留账户、凭证、收藏、历史;前端更新摘要,不清登录态 |
|
||||
| `DELETE /me` | 当前凭证 + 新微信 code + 用户确认,验证同一身份后按数据库规则完整注销 |
|
||||
|
||||
头像流程:`chooseAvatar` 得到临时本地文件 → 带 Bearer 上传 `POST /assets` → 得到本人 avatar assetId → PATCH 绑定。服务端校验归属,不能接受任意头像 URL、文件路径或他人 assetId。替换/清除后按既有文件清理机制处理旧私有对象,失败重试,不在数据库事务里请求对象存储。
|
||||
头像流程:`chooseAvatar` 得到临时本地文件 → 带 Bearer 上传 `POST /me/avatar-upload` → 得到本人 avatar assetId → `PUT /me/profile` 绑定。服务端校验归属,不能接受任意头像 URL、文件路径或他人 assetId。替换/清除后按既有文件清理机制处理旧头像对象,失败重试,不在数据库事务里请求对象存储。
|
||||
|
||||
昵称按数据库上限 100 个 Unicode 字符校验,首尾空白规范化,不把它作为唯一账号名。个人中心可分别保存昵称和头像,不强迫二者齐全;微信填写组件提供的临时路径不是永久头像地址。[微信头像昵称填写](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html)。
|
||||
|
||||
@@ -390,7 +390,7 @@ Cookie 写请求必须有 CSRF 防护,建议 Origin 校验 + 与会话绑定
|
||||
| 清缓存/注销与登录、列表请求并发 | 旧响应不复活状态;注销后后台 resume 不重新建号 |
|
||||
| 重新登录返回不同 userId | 原写请求不重发,原下载队列不代报 |
|
||||
| 保存成功后上报超时、重试 | 原 exportId 重用,不重复计数、不重复扣本地配额 |
|
||||
| 清资料 | 收藏/历史及有效凭证保留,头像私有对象进入清理流程 |
|
||||
| 清资料 | 收藏/历史及有效凭证保留,头像对象进入清理流程 |
|
||||
| 注销成功或响应丢失 | 事务完整,旧 token 失效;未知结果可确认,不自动重放或重新注册 |
|
||||
| 任意用户改他人头像、读他人历史 | 拒绝;管理员默认也不能读取全量私有数据 |
|
||||
| 小程序本地改角色或显示管理按钮 | 管理 API 仍拒绝,管理写事务重新核对操作者 |
|
||||
@@ -408,3 +408,17 @@ Cookie 写请求必须有 CSRF 防护,建议 Origin 校验 + 与会话绑定
|
||||
- [微信网站应用登录](https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html)、[OWASP 会话管理](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html):未来网站登录和浏览器会话的参考,不代表本站已接入。
|
||||
|
||||
本次建议默认按“小程序 24 小时 JWT、暂不增加表、Web 优先现有小程序扫码确认”推进。Web 的移动端覆盖、网站应用准入及实际域名拓扑留到 Web 启动时确定;若改选独立手机号/邮箱登录,需要补充验证、凭证、找回和解绑方案,不能直接复用昵称或客户端 UUID。
|
||||
|
||||
## 小程序首期接入进度(2026-10-19)
|
||||
|
||||
小程序已接入 `wx.login → POST /auth/wechat/login`,启动/回前台非阻塞预热;`utils/auth.ts` 提供 `getUser`、`ensureAuthenticated`、`getCachedUser`、`refreshUser` 和内部清理入口。`refreshUser` 调用受保护的 GET /me。当前阶段没有将资料更新伪装成已迁移:资料编辑、头像上传、收藏、下载记录及账户设置仍由独立的 `utils/legacy-auth.ts` 兼容旧云业务,后续逐项切换。
|
||||
|
||||
新凭证通过 AppID 与完整 API baseUrl 隔离,保存版本化的账户历史、凭证、本机预计到期时间及白名单用户摘要。公开分类/题型请求显式不鉴权;其余请求默认获取并携带本站 JWT。首次登录、提前 60 秒续期和 401 恢复共用 Promise;一次业务请求最多恢复与重发一次,迟到的旧 401 复用较新凭证,恢复后账户改变则停止原请求。无效微信 code 最多重新获取一次,网络错误不会复用 code 或自动循环。
|
||||
|
||||
账号不存在/禁用会清理凭证并暂停自动恢复,账户历史标记不随会话清理丢失;个人中心提供用户主动重新开始的入口,服务端仍负责拒绝禁用账户。新后端摘要使用 `globalData.user`,旧云端使用 `globalData.legacyUser`,不混用两种用户 ID 或用旧缓存冒充 JWT。
|
||||
|
||||
前端专项及分类/预览回归共 23 项通过(真实 8800 联调用例未启用);真实微信 code 兑换需在开发者工具或真机验证。正式部署前仍需配置线上 API 域名并先迁移旧用户及关联数据,避免在未迁移的生产库创建另一套业务身份。
|
||||
|
||||
## 其余接口切换进度(2026-10-19)
|
||||
|
||||
已完成微信预拉取之外的云接口替换,旧 `legacy-auth.ts` 兼容层已移除。用户资料、收藏、下载、头像、管理发布/批次与首页动态接口均使用本站用户身份,详见[小程序云接口迁移清单](小程序云接口迁移清单.md)。前文“首期接入进度”是分阶段记录,当前实现以该清单为准;历史云端用户/收藏/下载数据尚未自动搬迁。
|
||||
|
||||
Reference in New Issue
Block a user