Files
order_site/apps/backend/方案A-订单编排与自动兑换设计.md
T
2026-04-08 16:30:42 +08:00

25 KiB
Raw Blame History

方案 A:订单编排与自动兑换设计

1. 目标

基于现有两个项目:

增加一条面向真实订单的自动化交付链路:

  1. 第三方平台通过订单创建成功或支付成功通知,将订单数据推送到我方后端。
  2. 后端根据订单和商品信息匹配需要发放的 CDK,并创建交付任务。
  3. 后端生成一个面向用户的领取链接,并通过可用渠道发送给用户。
  4. 用户打开链接后,通过 QQ 或 WX 完成角色绑定。
  5. 后端复用现有 Playwright 自动化能力,自动提交 CDK、执行绑定、生成截图。
  6. 前端实时展示绑定状态、兑换状态和最终截图。

该方案采用“订单编排层 + 浏览器自动化执行层”两层结构,不直接把现有浏览器会话工具当作订单系统使用。

2. 当前系统现状

2.1 后端现状

当前后端主线是浏览器会话兑换工具,而不是订单系统。

当前系统没有以下关键能力:

  • 没有订单表
  • 没有 CDK 库存表
  • 没有 webhook 接收和签名校验
  • 没有任务状态机
  • 没有用户领取链接管理
  • 没有消息投递模块
  • 没有浏览器会话与订单任务的绑定关系

2.2 前端现状

当前前端是一个单页腾讯兑换工具。

当前前端没有以下能力:

  • 没有订单领取页
  • 没有 token 校验页
  • 没有订单详情页
  • 没有任务状态驱动的前端展示
  • 没有“订单上下文中的浏览器会话”概念

3. 总体架构

3.1 分层设计

建议分成 4 层:

  1. 平台接入层 负责接收订单创建成功、支付成功、售后等通知,完成签名校验和原始数据落库。
  2. 订单编排层 负责订单幂等、商品解析、CDK 预占、任务创建、链接生成、状态流转。
  3. 浏览器自动化执行层 复用现有腾讯浏览器会话、角色识别、验证码识别、兑换提交、截图生成能力。
  4. 用户交互层 用户通过领取链接进入前端页面,完成 QQ/WX 登录、角色确认、查看结果。

3.2 核心原则

  • 订单与浏览器会话解耦
  • CDK 先预占、后核销
  • 所有平台通知必须幂等
  • 用户访问链接使用 token,不直接使用订单号鉴权
  • 浏览器自动化失败必须可重试、可人工接管
  • 所有关键节点都要保留审计数据

4. 业务流程

4.1 主流程

  1. 平台发送订单通知到后端 webhook。
  2. 后端校验签名,按平台订单号幂等写入 orders
  3. 根据订单商品、规格、渠道信息生成一个或多个 delivery_tasks
  4. 系统从 cdk_inventory 中为任务预占可用 CDK。
  5. 系统生成 claim_token,构造领取链接。
  6. 系统通过站内消息、短信、客服人工转发或支付成功页展示,把链接给用户。
  7. 用户打开链接,前端调用后端校验 token 和任务状态。
  8. 用户选择 QQ 或 WX 登录,后端创建浏览器 session,并绑定到 delivery_task
  9. 用户扫码后,后端识别角色、大区、昵称等信息。
  10. 前端展示识别结果,用户确认角色无误。
  11. 后端自动执行 CDK 提交、验证码识别、截图生成。
  12. 成功后任务进入 redeemedCDK 进入 delivered
  13. 前端展示截图、结果消息、订单号、角色信息。

4.2 异常流程

  • webhook 重复推送:只更新状态,不重复创建任务
  • 商品无法匹配 CDK:任务进入 manual_review
  • CDK 库存不足:任务进入 waiting_inventory
  • token 过期:提示用户重新获取领取链接
  • 浏览器自动化失败:任务进入 retry_pendingmanual_review
  • 用户长时间未领取:任务进入 expired,释放预占 CDK

5. 数据模型设计

建议先上 SQLite,跑通后如有并发压力再切 PostgreSQL。当前项目还没有数据库依赖,先用 SQLite 可以把复杂度压低。

5.1 orders

订单主表。

字段建议:

  • id
  • platform
  • platform_order_id
  • order_status
  • pay_status
  • buyer_id
  • buyer_name
  • raw_payload_json
  • created_at
  • updated_at
  • paid_at

约束建议:

  • platform + platform_order_id 唯一

5.2 order_items

订单子项表,用于一单多商品。

字段建议:

  • id
  • order_id
  • sku_code
  • sku_name
  • quantity
  • spec_json
  • delivery_mode
  • created_at
  • updated_at

5.3 cdk_inventory

CDK 库存表。

字段建议:

  • id
  • batch_no
  • sku_code
  • cdk_code
  • status
  • reserved_by_task_id
  • delivered_at
  • invalid_reason
  • created_at
  • updated_at

状态建议:

  • available
  • reserved
  • delivered
  • invalid

5.4 delivery_tasks

交付任务表,是整个方案的核心。

字段建议:

  • id
  • order_id
  • order_item_id
  • platform_order_id
  • task_no
  • task_status
  • login_type
  • claim_token_id
  • reserved_cdk_id
  • browser_session_id
  • role_id
  • role_name
  • area
  • partition
  • nickname
  • result_code
  • result_message
  • screenshot_path
  • artifacts_json
  • last_error
  • retry_count
  • expires_at
  • claimed_at
  • redeemed_at
  • created_at
  • updated_at

状态建议:

  • pending_payment
  • paid
  • waiting_inventory
  • cdk_reserved
  • link_generated
  • claimed
  • role_confirmed
  • redeeming
  • redeemed
  • retry_pending
  • manual_review
  • expired
  • closed

5.5 claim_tokens

领取链接表。

字段建议:

  • id
  • task_id
  • token
  • status
  • expired_at
  • used_at
  • max_use_count
  • used_count
  • created_at
  • updated_at

状态建议:

  • active
  • used
  • expired
  • revoked

5.6 webhook_events

保留原始通知,便于审计和重放。

字段建议:

  • id
  • platform
  • event_type
  • event_key
  • signature_valid
  • headers_json
  • query_json
  • body_json
  • processed
  • process_error
  • created_at

6. 后端模块拆分

建议在现有后端上新增以下模块。

6.1 路由层

新增路由目录建议:

  • src/routes/webhooks.js
  • src/routes/orders.js
  • src/routes/claims.js
  • src/routes/admin.js

说明:

  • webhooks.js 接平台推送
  • orders.js 给内部系统或管理端查订单和任务
  • claims.js 给用户领取页调用
  • admin.js 做手工补单、重试、失效处理

6.2 服务层

新增服务建议:

  • src/services/order-service.js
  • src/services/order-item-service.js
  • src/services/delivery-task-service.js
  • src/services/cdk-service.js
  • src/services/claim-service.js
  • src/services/webhook-service.js
  • src/services/message-service.js
  • src/services/redeem-executor-service.js

职责划分建议:

  • webhook-service 负责签名校验、原始通知记录、事件分发
  • order-service 负责订单幂等写入和状态更新
  • delivery-task-service 负责任务状态机
  • cdk-service 负责 CDK 分配、预占、释放、核销
  • claim-service 负责 token 生成、校验、过期
  • message-service 负责发链接
  • redeem-executor-service 负责把任务桥接到现有腾讯浏览器会话执行器

6.3 持久化层

建议新增:

  • src/db/
  • src/repositories/

推荐目录:

  • src/db/client.js
  • src/repositories/order-repo.js
  • src/repositories/task-repo.js
  • src/repositories/cdk-repo.js
  • src/repositories/claim-repo.js
  • src/repositories/webhook-repo.js

7. 对现有浏览器自动化能力的复用方式

现有后端能力不废弃,而是作为执行器继续使用。

7.1 可直接复用的能力

  • 创建浏览器会话:createTencentBrowserSession
  • 轮询会话状态:getTencentBrowserSessiongetTencentBrowserSessionSummary
  • 扫码登录后识别角色信息:session.js 内已有活动页信息抽取逻辑
  • 执行兑换:redeemTencentBrowserSession
  • 生成截图和结果 JSONsaveRedeemArtifacts
  • OCR worker:继续使用当前 uv run ocr-worker worker

7.2 需要补的一层包装

不要让订单系统直接操作裸 sessionId,应通过 redeem-executor-service 管理:

  1. 根据 taskId 创建浏览器 session
  2. taskId <-> sessionId 关系落库
  3. 用户扫码完成后,把识别到的角色信息同步回任务
  4. 用户确认角色后,再触发兑换
  5. 兑换完成后,把截图路径和最终结果回写到任务

8. API 设计

8.1 平台 webhook

POST /api/v1/webhooks/agiso/trade

职责:

  • 接收订单创建成功通知
  • 接收支付成功通知
  • 验签
  • 幂等入库
  • 推进任务状态

8.2 用户领取页相关

GET /api/v1/claim/:token

返回:

  • token 是否有效
  • 订单号
  • 商品摘要
  • 当前任务状态
  • 是否已绑定角色
  • 是否已有截图

POST /api/v1/claim/:token/session

入参:

  • loginType: qq | wx

职责:

  • 校验 token
  • 创建腾讯浏览器 session
  • 将 session 绑定到任务
  • 返回二维码和 session 摘要

GET /api/v1/claim/:token/session/summary

职责:

  • 轮询当前领取任务下的 session 状态
  • 返回角色信息、二维码状态、截图状态

POST /api/v1/claim/:token/confirm-role

职责:

  • 用户确认当前角色无误
  • 把任务推进到 role_confirmed

POST /api/v1/claim/:token/redeem

职责:

  • 后端读取预占 CDK
  • 调用执行器自动提交
  • 回写结果

GET /api/v1/claim/:token/screenshot

职责:

  • 返回最终截图

8.3 管理后台或内部接口

POST /api/v1/admin/tasks/:taskId/retry

POST /api/v1/admin/tasks/:taskId/release-cdk

POST /api/v1/admin/tasks/:taskId/close

GET /api/v1/admin/orders/:orderId

GET /api/v1/admin/tasks/:taskId

9. 运营后台设计

9.1 是否需要后台

需要,而且应作为正式模块纳入方案。

原因:

  • 订单通知会重复、失败或延迟,必须能人工排查
  • CDK 库存需要日常导入、校验、释放、核销
  • 自动化链路不是 100% 成功,必须有失败任务处理入口
  • 用户咨询时,需要快速看到订单、任务、角色、截图和错误原因
  • 后续一定会出现补发、重试、关闭任务、重新生成链接等运营动作

如果没有后台,系统上线后所有异常都会退化成查数据库、查日志、手改数据,维护成本会快速失控。

9.2 更稳的做法

更稳的做法不是把管理页面直接混进用户领取页,而是拆成两套入口:

  1. 用户前台 继续服务用户领取、扫码登录、角色确认、结果查看。
  2. 运营后台 专门服务订单排查、CDK 管理、任务重试、异常处理。

建议形式:

  • 用户前台继续使用 order-site-rewrite
  • 运营后台先作为同仓库下独立路由模块实现,例如 /admin
  • 后端单独提供 /api/v1/admin/* 接口

这样做的好处:

  • 用户链路和运营链路隔离
  • 权限边界更清楚
  • 页面状态不会互相污染
  • 以后即使单独拆管理后台,也不会影响用户前台

9.3 后台第一版目标

第一版后台不追求复杂权限系统和漂亮界面,目标只有一个:

让你能稳定运营这条自动交付链路。

第一版最少应覆盖:

  1. 查订单
  2. 查交付任务
  3. 导入和维护 CDK
  4. 查看 webhook 日志
  5. 重试失败任务
  6. 释放预占 CDK
  7. 重新生成领取链接

9.4 后台页面模块建议

1. 概览页

展示核心数字:

  • 今日订单数
  • 已支付待领取数
  • 领取中任务数
  • 兑换成功数
  • 异常任务数
  • 库存不足的 SKU 数

这页主要用来让你快速判断系统是否在健康运行。

2. 订单列表页

展示字段建议:

  • 订单号
  • 平台
  • 商品摘要
  • 支付状态
  • 订单状态
  • 创建时间
  • 更新时间
  • 对应任务数

支持筛选:

  • 平台订单号
  • 支付状态
  • 时间范围
  • 商品 SKU

进入详情后可查看:

  • 原始订单通知
  • 子项列表
  • 对应交付任务

3. 交付任务列表页

这是后台最核心的页面。

展示字段建议:

  • task_no
  • 平台订单号
  • 商品 SKU
  • 当前任务状态
  • 预占 CDK
  • 登录方式
  • 角色名
  • 角色 ID
  • 浏览器 sessionId
  • 重试次数
  • 最后错误
  • 创建时间
  • 更新时间

支持操作:

  • 查看详情
  • 重新生成链接
  • 重试任务
  • 释放 CDK
  • 关闭任务
  • 标记人工处理

4. CDK 管理页

这是第二核心页面。

应支持:

  • 单条新增
  • 批量导入
  • 按 SKU 查看库存
  • 查看状态分布
  • 查看预占任务
  • 手动失效
  • 手动释放

展示字段建议:

  • sku_code
  • batch_no
  • cdk_code
  • status
  • reserved_by_task_id
  • created_at
  • updated_at

导入方式建议至少支持:

  • 文本框多行粘贴导入
  • CSV 文件导入

导入校验建议:

  • 去重
  • 空值校验
  • SKU 必填
  • 批次号可选但建议保留

5. webhook 日志页

展示:

  • 平台
  • 事件类型
  • 平台订单号
  • 验签是否通过
  • 是否处理成功
  • 错误信息
  • 到达时间

支持查看:

  • headers
  • query
  • body
  • 对应生成的订单和任务

可选操作:

  • 手动重放处理

6. 任务详情页

任务详情页建议整合以下信息:

  • 订单信息
  • 商品和 SKU
  • claim token 状态
  • 浏览器 session 状态
  • 角色识别结果
  • 兑换结果
  • 截图预览
  • 原始错误日志
  • 操作按钮

这个页面会成为你处理异常单的主工作台。

9.5 后台最小接口集

建议新增后台接口:

订单

  • GET /api/v1/admin/orders
  • GET /api/v1/admin/orders/:orderId

任务

  • GET /api/v1/admin/tasks
  • GET /api/v1/admin/tasks/:taskId
  • POST /api/v1/admin/tasks/:taskId/retry
  • POST /api/v1/admin/tasks/:taskId/close
  • POST /api/v1/admin/tasks/:taskId/release-cdk
  • POST /api/v1/admin/tasks/:taskId/regenerate-claim-link
  • POST /api/v1/admin/tasks/:taskId/mark-manual-review

CDK

  • GET /api/v1/admin/cdks
  • POST /api/v1/admin/cdks
  • POST /api/v1/admin/cdks/import
  • POST /api/v1/admin/cdks/:cdkId/invalidate
  • POST /api/v1/admin/cdks/:cdkId/release

webhook

  • GET /api/v1/admin/webhook-events
  • GET /api/v1/admin/webhook-events/:eventId
  • POST /api/v1/admin/webhook-events/:eventId/replay

概览

  • GET /api/v1/admin/dashboard/summary

9.6 后台权限建议

第一版建议至少分两层:

  1. admin 可导入 CDK、改任务状态、重试任务、关闭任务、重放 webhook。
  2. operator 可查订单、查任务、看截图、看日志,但不能做高风险修改。

如果第一版先不做完整账号体系,也至少要做一个最简单的后台鉴权,例如:

  • 独立后台口令
  • 基础登录态
  • 后端校验管理接口访问权限

不要把后台接口直接裸露给公网无鉴权访问。

9.7 后台与前台的边界

建议明确边界:

  • 前台只处理用户领取和结果查看
  • 后台只处理运营和异常处置

前台不要出现:

  • 手动改任务状态
  • 手动改 CDK
  • 看其他订单

后台不要承担:

  • 用户扫码登录入口
  • 用户领取页展示逻辑

这样职责清晰,后续维护成本最低。

9.8 后台的落地顺序

建议放在整体计划的阶段 4 中作为正式内容,但有两个能力应提前做:

  1. CDK 导入
  2. 任务列表与详情查看

原因:

  • 没有 CDK 导入能力,阶段 1 根本跑不顺
  • 没有任务查看能力,阶段 2 和阶段 3 出问题时无法排查

因此更合适的执行顺序是:

  • 阶段 1 完成订单骨架后,先补一个极简后台
  • 阶段 2 和 3 继续完善业务闭环
  • 阶段 4 再补全 webhook 重放、统计面板、权限细化

9.9 后台最小可行版本

后台 MVP 建议包含:

  1. 登录页
  2. 概览页
  3. 订单列表页
  4. 任务列表页
  5. 任务详情页
  6. CDK 导入页
  7. webhook 日志页

这已经足够支撑第一版系统上线和日常运营。

9.10 结论

后端侧需要一个管理界面,而且应当作为正式模块设计,不建议临时拼接。

更稳的做法是:

  • 用户前台与运营后台分离
  • 后台先做最小可行版本
  • 优先覆盖查单、查任务、导入 CDK、重试异常
  • 后台接口独立鉴权

10. 前端设计

9.1 路由设计

现有前端只有 /tx/browser,建议新增:

  • /claim/:token

这个页面才是正式业务入口。

/tx/browser 保留为内部调试工具页,不作为正式交付页面。

9.2 页面结构

建议页面拆成 4 个区域:

  1. 订单摘要区 展示订单号、商品名、状态说明
  2. 登录绑定区 复用现有二维码卡片能力
  3. 角色确认区 复用现有角色展示和确认能力
  4. 结果展示区 复用现有截图和结果卡片能力

9.3 可复用的前端模块

可以复用:

但需要改造:

  • 不再让前端自己输入兑换码
  • 不再让前端自己决定是否创建裸 session
  • 页面所有操作都基于 claim token

9.4 前端状态设计

建议页面状态围绕任务状态展开,而不是围绕裸 session:

  • tokenValid
  • taskStatus
  • orderSummary
  • session
  • activityInfo
  • roleConfirmed
  • redeemResult
  • screenshotUrl

11. 状态机设计

10.1 任务状态流转

主状态机建议如下:

  1. pending_payment
  2. paid
  3. waiting_inventory
  4. cdk_reserved
  5. link_generated
  6. claimed
  7. role_confirmed
  8. redeeming
  9. redeemed

异常分支:

  • retry_pending
  • manual_review
  • expired
  • closed

10.2 CDK 状态流转

  1. available
  2. reserved
  3. delivered

异常分支:

  • invalid

10.3 token 状态流转

  1. active
  2. used

异常分支:

  • expired
  • revoked

12. 安全与风控

11.1 token 安全

  • token 必须使用高熵随机串
  • 不要把订单号作为唯一访问凭证
  • token 要支持过期
  • token 要支持撤销
  • token 校验失败不要泄露订单是否存在

11.2 webhook 安全

  • 验签必须在业务处理前完成
  • 保存原始 query、body、headers
  • 根据平台订单号和事件类型做幂等
  • 对重复通知返回成功,避免平台无限重试

11.3 自动化风险

  • 登录状态和验证码都可能失败
  • 活动页 DOM 变更会导致自动化失效
  • 平台风控弹窗可能导致流程卡死
  • 必须保留重试和人工兜底入口

13. 失败补偿机制

12.1 自动重试

可自动重试的场景:

  • OCR 识别失败
  • 页面刷新失败
  • 临时网络超时
  • 可恢复的验证码错误

不建议自动无限重试,应限制次数并回写错误。

12.2 人工接管

以下场景建议直接转人工:

  • 角色识别异常
  • 商品和 CDK 映射无法确认
  • 自动化连续失败
  • CDK 已疑似提交但返回结果不明确

12.3 过期处理

  • 领取链接超时未使用,任务转 expired
  • 任务过期后释放 reserved 状态的 CDK
  • 如已创建浏览器 session,应主动清理

14. 技术选型建议

13.1 后端

当前后端是 Node.js + Express + Playwright,建议保持不变。

新增建议:

  • 数据库先用 SQLite
  • 数据访问先用简单 repository 封装
  • 后续需要时再引入 ORM

原因:

  • 先把主流程跑通
  • 当前系统规模小,没必要过早引入太重的基础设施
  • 浏览器自动化已经在 Node 侧,订单编排继续留在同一进程更简单

13.2 前端

当前前端是 Vue 3 + Element Plus,建议保持不变。

只需要增加:

  • claim 页面
  • claim 相关 API service
  • task 视图模型

13.3 Python 相关

当前 Python 仅用于 OCR worker,继续使用 uv 管理即可。

如果后续需要:

  • 图像增强
  • 模板匹配
  • 截图后处理

仍然应放在独立 Python 子服务里,并统一通过 uv 维护依赖。

15. 推荐执行顺序

建议分 4 个阶段实施。

阶段 1:后端订单骨架

目标:

  • 接 webhook
  • 落订单
  • 建任务
  • 预占 CDK
  • 生成领取链接

要做的事:

  1. 增加数据库和表结构
  2. 实现 webhook 验签和幂等
  3. 实现订单、任务、CDK、token 的 repository 和 service
  4. 实现任务状态机基础逻辑
  5. 实现领取链接生成

阶段产出:

  • 支付成功后,系统能自动生成领取任务和领取链接

阶段 2:极简后台 + 前端领取页

目标:

  • 有基本运营能力
  • 用户通过 token 打开页面
  • 看见订单信息
  • 创建登录 session
  • 扫码并确认角色

要做的事:

  1. 新增 /admin 路由和极简后台骨架
  2. 实现订单列表、任务列表、任务详情、CDK 导入
  3. 新增 /claim/:token 路由
  4. 新增 claim 页面和 API service
  5. 复用当前二维码、轮询、角色确认组件
  6. 页面状态从 session 驱动 改成 task + session 双驱动

阶段产出:

  • 运营能查任务和导入 CDK,用户能进入正式领取页面并完成角色绑定

阶段 3:自动兑换闭环

目标:

  • 用户确认角色后自动提交 CDK
  • 生成截图并前端展示

要做的事:

  1. 封装 redeem-executor-service
  2. 将任务与 session 建立绑定关系
  3. 后端从预占 CDK 读取兑换码,不再由前端输入
  4. 回写截图路径、业务返回码、错误信息

阶段产出:

  • 从订单通知到最终截图形成完整闭环

阶段 4:补偿和完整运营能力

目标:

  • 可重试
  • 可过期释放
  • 可人工处理
  • 有更完整的后台能力

要做的事:

  1. 增加任务重试接口
  2. 增加过期清理任务
  3. 增加 webhook 重放
  4. 增加概览统计和异常筛选
  5. 增加后台权限细化
  6. 增加关键日志和审计落库

阶段产出:

  • 系统具备基本上线可运维性

16. 最小可行版本定义

第一版上线目标不建议追求“完全无人值守”,建议定义为:

  • 平台支付成功后自动创建领取任务
  • 自动预占 CDK
  • 自动生成领取链接
  • 用户进入页面后自行扫码登录
  • 用户确认角色后系统自动提交 CDK
  • 成功后展示截图
  • 运营可在后台查看订单、任务、库存和 webhook 日志
  • 失败后进入重试或人工处理

这是当前代码基础上最稳、最容易落地、售后风险最低的版本。

17. 关键决策结论

  • 采用方案 A 是合理的
  • 不建议直接用订单号做访问凭证
  • 不建议前端继续保留“手输 CDK”的正式业务模式
  • 不建议一开始就做全自动零确认绑定
  • 建议先做订单编排层,再复用现有浏览器自动化执行层
  • 建议优先做 SQLite 版本跑通链路
  • 建议把 /tx/browser 保留为内部调试页,把 /claim/:token 做成正式业务入口
  • 建议把运营后台作为正式模块纳入,而不是后补工具页

18. 下一步落地建议

下一步建议直接进入“阶段 1 实施设计”,输出更细的开发清单:

  1. 数据库表结构 SQL
  2. 后端目录和文件改造清单
  3. API 请求和响应示例
  4. 前台 /claim/:token 页面状态图
  5. 后台 /admin 页面结构与接口契约
  6. 任务状态机枚举定义

如果继续推进,我建议下一步先把:

  • 表结构
  • 后端 API 草案
  • 前端 /claim/:token 页面接口契约
  • 后台 /admin 核心接口契约

这三部分先定死,然后再开始编码。