重构后台管理服务并推进渐进式类型化
This commit is contained in:
@@ -1,255 +0,0 @@
|
||||
# Backend Refactor Status
|
||||
|
||||
最后更新:2026-04-09(已切换到“测试数据可丢弃,不做历史迁移兼容”前提)
|
||||
|
||||
## 当前结论
|
||||
|
||||
后端正在从“单一腾讯领取兑换工具”迁移到“通用订单履约系统”。
|
||||
|
||||
当前代码已经完成了下面两件大事:
|
||||
|
||||
1. 数据层已经切到 PostgreSQL,并引入了新的通用履约模型。
|
||||
2. 仓库结构已经分出 `db / repositories / services / routes / frontend / deploy` 等清晰边界。
|
||||
|
||||
当前仍处于新旧架构并存阶段,但已经明确采用下面的推进原则:
|
||||
|
||||
- 现有数据库数据视为测试数据,可直接清空
|
||||
- 不再为了旧测试数据保留历史迁移修复逻辑
|
||||
- 新配置只认当前履约模型,不再从旧映射自动推导
|
||||
|
||||
当前主要表现为:
|
||||
|
||||
- 新 schema 已经使用 `fulfillment_* / inventory_items / task_inventory_bindings`
|
||||
- 核心 service 主链路已经开始直接使用“主 token / 主库存绑定”语义
|
||||
- admin/public 资源名已经切到 `inventory`
|
||||
- 剩余工作主要集中在“多库存绑定能力上浮”和“测试/种子数据体系化”
|
||||
|
||||
## 当前真实架构
|
||||
|
||||
从工程实现视角,当前后端可以按 5 层理解:
|
||||
|
||||
1. 接口层
|
||||
- `src/routes/`
|
||||
- 负责接 HTTP 请求、鉴权、组装响应
|
||||
2. 应用编排层
|
||||
- `src/services/order/`
|
||||
- `src/services/claim/`
|
||||
- `src/services/admin/`
|
||||
- 负责订单、任务、库存、领取、后台操作编排
|
||||
3. 执行能力层
|
||||
- `src/services/session/`
|
||||
- `src/services/platforms/`
|
||||
- 负责浏览器自动化、平台消息发送、OCR 调用
|
||||
4. 持久化层
|
||||
- `src/repositories/`
|
||||
- 负责 orders、tasks、inventory、claim_tokens、webhook_events 的读写
|
||||
5. 基础设施层
|
||||
- `src/db/`
|
||||
- `src/config/`
|
||||
- 负责数据库连接、迁移、运行时配置、bootstrap
|
||||
|
||||
## 已完成部分
|
||||
|
||||
### 1. 通用履约模型已经落库
|
||||
|
||||
当前迁移文件已经不再围绕旧式单库存码模型设计,而是引入了:
|
||||
|
||||
- `fulfillment_profiles`
|
||||
- `fulfillment_profile_requirements`
|
||||
- `sku_fulfillment_bindings`
|
||||
- `inventory_items`
|
||||
- `fulfillment_tasks`
|
||||
- `task_inventory_bindings`
|
||||
- `tencent_browser_contexts`
|
||||
- `message_deliveries`
|
||||
|
||||
这说明系统底层已经支持:
|
||||
|
||||
- 同一个 SKU 绑定不同履约方式
|
||||
- 不同 provider / platform / shop 的差异化履约策略
|
||||
- 一个任务绑定多个库存项
|
||||
- 不同 `credential_type` 的库存凭据
|
||||
- 手动发货与腾讯领取兑换并存
|
||||
|
||||
### 2. 履约目录 bootstrap 已经具备基础抽象
|
||||
|
||||
当前 bootstrap 中已经定义了两个核心 profile:
|
||||
|
||||
- `manual_review`
|
||||
- `tencent_claim_redeem`
|
||||
|
||||
说明“手动发货”和“腾讯领取兑换”已经从业务概念上进入统一履约目录,而不是散落在业务代码里。
|
||||
|
||||
### 3. Admin 路由已经完成按领域拆分
|
||||
|
||||
后台路由不再全部堆在一个文件里,而是按:
|
||||
|
||||
- auth
|
||||
- dashboard
|
||||
- users
|
||||
- orders
|
||||
- tasks
|
||||
- inventory
|
||||
- webhook-events
|
||||
- platform-config
|
||||
|
||||
拆成独立模块,便于继续演进。
|
||||
|
||||
### 4. 核心任务读写已经开始切回新 schema 语义
|
||||
|
||||
当前仓库层已经明确暴露:
|
||||
|
||||
- `primary_claim_token_*`
|
||||
- `primary_inventory_item_*`
|
||||
|
||||
应用层也开始改为基于这些字段处理“主领取 token / 主库存绑定”,而不是继续把它们伪装成旧 `claim_token_id / reserved_cdk_id`。
|
||||
|
||||
另外,`claimed_at / role_confirmed_at / redeemed_at` 这些任务时间字段现在也已真正通过 repository 落库,不再只是 service 层表面传值。
|
||||
|
||||
### 5. 本地 Docker 开发栈已经重新打通
|
||||
|
||||
当前开发 Compose 已改为:
|
||||
|
||||
- `postgres` 提供开发数据库
|
||||
- backend 通过 `DATABASE_URL` 连接 PostgreSQL
|
||||
- 启动时自动执行 migration 与 bootstrap
|
||||
|
||||
这轮还顺手修掉了一个启动顺序问题:
|
||||
|
||||
- 之前 backend 启动时没有等待 migration 完成,就先执行 fulfillment catalog bootstrap
|
||||
- 现已改为先 `await runDatabaseMigrations()`,再继续后续初始化
|
||||
|
||||
本地验证结果:
|
||||
|
||||
- `docker compose -f docker-compose.dev.yml` 可拉起 `postgres / backend / frontend / web`
|
||||
- `/health` 可正常返回
|
||||
- admin 登录可用
|
||||
- `GET /api/v1/admin/tasks` 已能正常鉴权并返回结果
|
||||
|
||||
### 6. `manual_dispatch` 已具备完整人工履约闭环
|
||||
|
||||
当前 `manual_dispatch` 不再只是把任务停在 `manual_review`,而是已经补齐成新的正式履约路径:
|
||||
|
||||
- paid 后的人工履约任务会进入 `manual_review`
|
||||
- admin 新增 `POST /api/v1/admin/tasks/:taskId/complete-manual-dispatch`
|
||||
- 支持把人工履约结果明确回写为 `delivered` 或 `failed`
|
||||
- 结果会写入 `task_status / delivery_status / result_code / result_message / context_json`
|
||||
- 如果任务绑定了库存凭据,人工完成时也会同步把库存置为 consumed
|
||||
- `task_events` 已开始记录人工履约完成事件
|
||||
|
||||
同时,人工履约任务已经不再允许误走旧自动链路:
|
||||
|
||||
- 不再允许对 `manual_dispatch` 执行自动重试
|
||||
- 不再允许为 `manual_dispatch` 重新生成领取链接
|
||||
- admin 详情页已补上人工履约回写表单与结果展示
|
||||
|
||||
### 7. admin 任务接口开始去除旧 `CDK` 过渡命名
|
||||
|
||||
这一轮继续把任务侧接口往新模型对齐:
|
||||
|
||||
- 任务释放入口已改为 `release-inventory`
|
||||
- 任务 action payload 不再返回 `reservedCdkId`
|
||||
- 主领取 token 字段统一为 `primaryClaimTokenId`
|
||||
- 前端任务详情/列表不再依赖 `reservedCdkCodeMasked` 和 `cdk` 兼容对象
|
||||
|
||||
当前任务管理界面已经优先使用:
|
||||
|
||||
- `inventoryItemId`
|
||||
- `inventory.displayValue`
|
||||
- `inventory.credentialType`
|
||||
- `primaryClaimTokenId`
|
||||
|
||||
### 8. admin 库存管理域已经切到 `inventory` 资源名
|
||||
|
||||
这一轮把库存管理域的外层入口也继续切到新模型:
|
||||
|
||||
- 后端 admin API 已从 `/api/v1/admin/cdks` 切到 `/api/v1/admin/inventory`
|
||||
- 前端后台路由已从 `/admin/cdks` 切到 `/admin/inventory`
|
||||
- 库存管理页面、导航、审计 action、targetType 已统一改为库存项语义
|
||||
- 库存列表返回结构已改为 `inventoryItemId / displayValue / credentialType`
|
||||
|
||||
现在从 URL、页面和 admin payload 角度,库存域已经不再把资源本身暴露成旧 `cdk` 名称。
|
||||
|
||||
### 9. 后端内部 `cdk` 命名主链路已经收口到 `inventory`
|
||||
|
||||
这轮已经把后端主链路里的历史命名继续清理掉:
|
||||
|
||||
- `repositories/cdk-repo.js` 已收口为 `repositories/inventory-repo.js`
|
||||
- `services/order/cdk-service.js` 已收口为 `services/order/inventory-service.js`
|
||||
- admin / claim / delivery 主链路已改用 `getInventoryItemById / reserveInventoryForTask / releaseReservedInventoryItem` 等命名
|
||||
- 任务错误文案已统一改成“库存项 / 库存凭据”语义
|
||||
|
||||
现在 `apps/backend/src` 主干里已经不再保留旧 `cdk-*` 仓储与服务文件名。
|
||||
|
||||
### 10. 开发种子数据入口已经补上
|
||||
|
||||
这轮已经补上新的开发数据入口,不再只能靠手工 webhook 或临时 SQL 验证:
|
||||
|
||||
- 新增 `apps/backend/scripts/seed-dev-data.js`
|
||||
- 新增 `npm run seed:dev-data`
|
||||
- 支持 `--apply --reset` 先清库再重建固定场景
|
||||
- 当前已覆盖:
|
||||
- `manual_pending`
|
||||
- `manual_completed`
|
||||
- `claim_ready`
|
||||
- `claim_claimed`
|
||||
- `claim_role_confirmed`
|
||||
- `claim_retry_pending`
|
||||
- `claim_expired`
|
||||
- `claim_waiting_inventory`
|
||||
|
||||
这意味着后续继续改任务列表、订单详情、履约编排时,已经有一套可重复验证的最小场景。
|
||||
|
||||
## 当前未完成部分
|
||||
|
||||
### 1. 多凭据任务能力还没有完全上浮到应用层
|
||||
|
||||
底层已经支持一个任务绑定多个库存项,但上层展示和操作仍偏向:
|
||||
|
||||
- 虽然 admin 任务详情已经开始返回并展示 `inventoryBindings`
|
||||
- 任务列表和订单详情已经开始展示绑定摘要计数
|
||||
- 但列表操作入口和大部分应用编排仍主要围绕一个主库存项
|
||||
- 一个任务只暴露一个领取 token
|
||||
- 后台操作还没有按“绑定集合”粒度设计
|
||||
|
||||
### 2. 测试数据初始化还不够体系化
|
||||
|
||||
虽然已经明确“测试数据可丢弃”,也已经有 PostgreSQL 清库脚本和基础 seed 脚本,但当前场景库仍然偏薄。
|
||||
|
||||
还缺:
|
||||
|
||||
- `redeeming / redeemed with screenshot / claim token revoked` 等更靠近真实自动化结果的场景
|
||||
- 更接近真实订单组合的多任务、多数量、多库存绑定样本
|
||||
|
||||
### 3. 任务状态与库存绑定状态仍可进一步解耦
|
||||
|
||||
当前虽然已经有 `task_status / delivery_status / inventory_status / task_inventory_bindings` 四套状态,但 admin 视角仍以“任务主状态”为中心:
|
||||
|
||||
- 详情页已经开始展示库存绑定集合
|
||||
- 任务列表和订单详情已经开始展示绑定摘要
|
||||
- 但订单列表、任务操作入口还没有把多库存绑定事件完整展开
|
||||
- `manual_dispatch` 与自动领取任务共用的列表状态摘要还比较粗
|
||||
- 订单编排层还没有把“多库存、多步骤”履约任务展开成更清晰的应用层对象
|
||||
|
||||
## 现在最该继续做的事
|
||||
|
||||
1. 把“单个主库存项”心智继续升级为“任务库存绑定集合”
|
||||
2. 让 admin 层逐步暴露多库存绑定、多履约步骤的真实状态
|
||||
3. 在现有 seed 基础上继续扩充更多任务状态和编排场景
|
||||
4. 继续把订单列表和任务操作入口也切到绑定集合视角
|
||||
|
||||
当前这件事已经继续往前走了一步:
|
||||
|
||||
- 旧 SQLite 思路的 `scripts/cleanup-dev-data.js` 已经替换为基于 PostgreSQL 新 schema 的清库脚本
|
||||
- admin 任务详情已开始暴露并展示 `task_inventory_bindings` 集合
|
||||
- admin 任务列表和订单详情已开始消费绑定摘要计数
|
||||
- `scripts/seed-dev-data.js` 已扩展到 claim 主流程多状态样本,bootstrap 仍然只负责 profile/catalog,不负责完整业务测试数据生成
|
||||
|
||||
## 文档说明
|
||||
|
||||
以下旧文档已删除,不再作为事实来源:
|
||||
|
||||
- `apps/backend/方案A-订单编排与自动兑换设计.md`
|
||||
- `apps/backend/阶段1-4实施规格说明.md`
|
||||
|
||||
后续请以代码、迁移文件和本状态文档为准。
|
||||
@@ -0,0 +1,197 @@
|
||||
# Backend TypeScript 迁移计划
|
||||
|
||||
最后更新:2026-04-14
|
||||
|
||||
## 结论
|
||||
|
||||
当前后端不建议整体重写为 Go。
|
||||
|
||||
更合适的路线是:
|
||||
|
||||
1. 保留 Node.js + Express + Playwright + PostgreSQL 主栈
|
||||
2. 先把后端渐进迁移到 TypeScript
|
||||
3. 同时拆分超大 service 文件
|
||||
4. 补足核心业务链路测试
|
||||
|
||||
这样可以先解决“可维护性”和“改动风险”问题,而不会额外引入一次高成本的跨语言重写。
|
||||
|
||||
## 为什么现在不直接重写 Go
|
||||
|
||||
当前后端的主要复杂度并不来自语言本身,而是来自:
|
||||
|
||||
- 订单、任务、库存、领取、自动发货、webhook 多条链路同时演进
|
||||
- Playwright 浏览器自动化本身就天然偏 Node 生态
|
||||
- 若整体换成 Go,浏览器自动化大概率仍需保留 Node worker
|
||||
- 这样会把系统变成 Go + Node + Python 三段式,边界更多,联调更难
|
||||
|
||||
所以现阶段最有效的动作,不是换语言,而是先把现有 Node 后端“类型化、分层化、可测试化”。
|
||||
|
||||
## 当前主要维护痛点
|
||||
|
||||
从当前仓库状态看,风险主要集中在:
|
||||
|
||||
- 少数超大编排文件已经承担过多职责
|
||||
- `apps/backend/src/services/admin/admin-service.js`
|
||||
- `apps/backend/src/services/claim/claim-session-service.js`
|
||||
- `apps/backend/src/services/order/webhook-service.js`
|
||||
- `apps/backend/src/services/session/session.js`
|
||||
- 领域对象靠运行时约定在 routes / services / repositories 之间传递
|
||||
- 当前自动化测试覆盖还偏薄,跨链路改动缺少回归保护
|
||||
|
||||
## 迁移目标
|
||||
|
||||
本次迁移不是为了“全部改成 .ts 才算完成”,而是为了达到下面几个目标:
|
||||
|
||||
1. 让核心领域对象具备稳定的静态类型
|
||||
2. 让新增改动优先进入类型系统,而不是继续扩大纯 JS 面积
|
||||
3. 让超大 service 文件拆分时有类型边界可依赖
|
||||
4. 让 typecheck 和测试一起成为后端的标准校验步骤
|
||||
|
||||
## 分阶段计划
|
||||
|
||||
### 阶段 1:建立类型基础设施
|
||||
|
||||
目标:
|
||||
|
||||
- 引入后端 `tsconfig`
|
||||
- 增加 `npm run typecheck`
|
||||
- 先从最核心的共享配置和基础领域对象开始建类型
|
||||
- 暂不改变现有运行方式,不引入编译产物,不影响 Docker 启动
|
||||
|
||||
范围:
|
||||
|
||||
- `runtimeConfig`
|
||||
- 公共配置对象
|
||||
- 后续会继续补:
|
||||
- admin DTO
|
||||
- order / task / inventory 基础类型
|
||||
- webhook 解析结果类型
|
||||
|
||||
验收标准:
|
||||
|
||||
- 后端可以执行 `npm run typecheck`
|
||||
- 现有 `npm run dev` / `npm run start` / Docker 启动流程不受影响
|
||||
|
||||
### 阶段 2:按领域拆分超大 service
|
||||
|
||||
目标:
|
||||
|
||||
- 保持运行逻辑不变
|
||||
- 先把“巨石编排文件”拆成更小的用例编排模块
|
||||
|
||||
建议拆分顺序:
|
||||
|
||||
1. `admin-service.js`
|
||||
- `admin-order-service`
|
||||
- `admin-task-service`
|
||||
- `admin-inventory-service`
|
||||
- `admin-webhook-service`
|
||||
2. `claim-session-service.js`
|
||||
- `claim-session-lifecycle`
|
||||
- `claim-role-service`
|
||||
- `claim-redeem-orchestrator`
|
||||
3. `webhook-service.js`
|
||||
- `webhook-parse`
|
||||
- `webhook-validate`
|
||||
- `webhook-process`
|
||||
|
||||
### 阶段 3:扩大类型覆盖
|
||||
|
||||
目标:
|
||||
|
||||
- 新增模块优先使用 `.ts`
|
||||
- 旧模块逐步迁移,而不是一次性全改
|
||||
- 让 routes / services / repositories 之间共享同一套领域类型
|
||||
|
||||
建议优先顺序:
|
||||
|
||||
1. `src/types/` 下沉淀共享领域模型
|
||||
2. repository 返回值显式类型化
|
||||
3. service 入参 / 出参类型化
|
||||
4. 再迁移最稳定的新模块到 `.ts`
|
||||
|
||||
### 阶段 4:补核心回归测试
|
||||
|
||||
优先补这些主链路:
|
||||
|
||||
1. webhook 入单与重放
|
||||
2. 库存预占 / 释放 / 换码
|
||||
3. claim 会话创建 / 关闭 / 切换
|
||||
4. 腾讯兑换结果分类
|
||||
5. 自动发货触发条件
|
||||
|
||||
## 已开始执行的第一步
|
||||
|
||||
本轮已经落地:
|
||||
|
||||
1. backend 新增 `tsconfig.json`
|
||||
2. backend 新增 `npm run typecheck`
|
||||
3. 新增 `src/types/runtime-config.js`
|
||||
4. `src/config/runtime.js` 已接入第一批类型检查
|
||||
5. 新增 `src/types/admin-read-models.js`
|
||||
6. `src/services/admin/admin-service.js` 的订单 / 任务 / 库存 / webhook 读取返回模型已接入第二批类型检查
|
||||
7. 新增 `src/types/repository-rows.js`
|
||||
8. `admin-service.js` 的核心读取映射函数参数,已开始明确依赖 repository 行模型
|
||||
9. `order / task / inventory / webhook / order-item / task-inventory-binding` repository 已补第一批返回类型
|
||||
10. backend `tsconfig.json` 已将上述 repository 纳入显式 `typecheck` 范围
|
||||
11. `npm --prefix apps/backend run typecheck` 已在 repository 扩围后通过
|
||||
12. 新增 `src/types/repository-inputs.js`
|
||||
13. `order / task / inventory / webhook / order-item` repository 已开始显式使用共享输入类型
|
||||
14. `npm --prefix apps/backend run typecheck` 已在输入类型接入后继续通过
|
||||
15. 新增 `src/services/admin/admin-read-service.js`,开始承接后台查询型接口
|
||||
16. `orders / tasks / inventory / webhook-events` 管理路由已优先切到 `admin-read-service`
|
||||
17. 读服务首轮拆分后,backend `typecheck` 继续通过
|
||||
18. 新增 `src/services/admin/admin-read-helpers.js`,开始承接读侧共享 helper
|
||||
19. `admin-read-service.js` 已改为优先依赖 `admin-read-helpers.js`,不再直接依赖 `admin-service.js`
|
||||
20. helper 下沉后,backend `typecheck` 继续通过
|
||||
21. `admin-service.js` 已开始复用 `admin-read-helpers.js`,并删除首批重复的读侧 helper
|
||||
22. `order / task / inventory / webhook` repository 已补共享查询参数类型
|
||||
23. `npm --prefix apps/backend run typecheck` 已在查询参数类型接入后继续通过
|
||||
24. 新增 `src/types/admin-read-inputs.js`,开始承接后台读接口的 service 入参边界
|
||||
25. `admin-read-service.js` 的订单 / 任务 / 库存 / webhook 查询与详情入口已接入共享输入类型
|
||||
26. `npm --prefix apps/backend run typecheck` 已在 service 输入类型接入后继续通过
|
||||
27. 新增 `src/services/admin/admin-write-service.js`,开始承接后台库存写接口
|
||||
28. `inventory` 管理路由已切到 `admin-write-service`,`admin-service.js` 删除首批库存写侧实现
|
||||
29. 新增 `src/types/admin-write-inputs.js`,库存写接口已开始复用共享输入类型
|
||||
30. backend `typecheck` 已覆盖 `admin-write-service.js`
|
||||
31. 新增 `src/types/admin-write-models.js`,库存写接口返回结构已接入共享响应类型
|
||||
32. `admin-write-service.js` 的 create / import / release / invalidate 已补返回值 JSDoc
|
||||
33. `webhook replay` 与 `Agiso 店铺配置保存` 已下沉到 `admin-write-service.js`
|
||||
34. `webhook-events / platform-config` 写路由已切到 `admin-write-service`
|
||||
35. `admin-service.js` 删除第二批可独立的写侧实现,并保留兼容导出
|
||||
36. `task` 生命周期写操作第一批已下沉到 `admin-write-service.js`
|
||||
37. `tasks` 管理路由已切换首批 task 写接口到 `admin-write-service`
|
||||
38. 新增 task 写侧共享响应类型,task action / binding release 已接入 JSDoc
|
||||
39. `retryAdminTask` 与 `completeAdminTaskManualDispatch` 已下沉到 `admin-write-service.js`
|
||||
40. `tasks` 管理路由已全部切到 `admin-write-service` 承接 task 写接口
|
||||
41. `admin-service.js` 已删除 task 写侧主体实现,仅保留兼容导出
|
||||
42. 新增 `admin-platform-config-service.js`,开始承接 platform-config 整块能力
|
||||
43. `platform-config` 路由已切换到独立 service,不再经过 `admin-service.js`
|
||||
44. `admin-service.js` 已删除平台配置相关读写与 helper,实现进一步收敛
|
||||
45. 新增 `admin-dashboard-service.js`,后台概览已独立
|
||||
46. 新增 `admin-message-delivery-service.js`,消息发送记录查询已独立
|
||||
47. `dashboard / message-deliveries` 路由已直接依赖独立 service
|
||||
48. `admin-service.js` 已收敛为兼容导出层,不再直接承载后台业务实现
|
||||
49. 后台读 / 写 / 平台配置 / 概览 / 消息记录已分散到独立 service 模块
|
||||
|
||||
这一步的设计原则是“只加静态约束,不动运行链路”,所以不会影响:
|
||||
|
||||
- `npm run dev`
|
||||
- `npm run start`
|
||||
- Docker 开发 / 部署
|
||||
|
||||
## 下一步建议
|
||||
|
||||
第一批继续推进时,建议按这个顺序:
|
||||
|
||||
1. 为 `admin` 路由层补请求体 / 查询参数的共享输入类型
|
||||
2. 评估是否把部分 `admin-read-helpers.js` 再细拆成按领域 helper 模块
|
||||
3. 继续补 repository / service 的边界约束,但不急着改 `.ts` 扩展名
|
||||
4. 为 webhook、库存换码、自动发货补测试
|
||||
|
||||
## 执行原则
|
||||
|
||||
- 不为迁移而迁移,优先迁移最常改、最容易改坏的链路
|
||||
- 不追求一口气全量改 `.ts`
|
||||
- 优先建立“新代码进入类型系统”的机制
|
||||
- 每一步都要求可回滚、可验证、不中断现有开发流
|
||||
Reference in New Issue
Block a user