25 KiB
方案 A:订单编排与自动兑换设计
1. 目标
基于现有两个项目:
增加一条面向真实订单的自动化交付链路:
- 第三方平台通过订单创建成功或支付成功通知,将订单数据推送到我方后端。
- 后端根据订单和商品信息匹配需要发放的 CDK,并创建交付任务。
- 后端生成一个面向用户的领取链接,并通过可用渠道发送给用户。
- 用户打开链接后,通过 QQ 或 WX 完成角色绑定。
- 后端复用现有 Playwright 自动化能力,自动提交 CDK、执行绑定、生成截图。
- 前端实时展示绑定状态、兑换状态和最终截图。
该方案采用“订单编排层 + 浏览器自动化执行层”两层结构,不直接把现有浏览器会话工具当作订单系统使用。
2. 当前系统现状
2.1 后端现状
当前后端主线是浏览器会话兑换工具,而不是订单系统。
- Express 入口在 src/index.js
- 现有路由只覆盖腾讯浏览器会话接口,在 src/routes/tencent.js
- 浏览器会话创建、刷新、关闭、兑换都集中在 src/services/session.js
- 验证码识别与兑换提交流程在 src/services/session-redeem.js
- 截图、结果 JSON、证明产物生成在 src/services/session-proof.js
- OCR 子服务使用
uv启动,在 src/services/ocr.js
当前系统没有以下关键能力:
- 没有订单表
- 没有 CDK 库存表
- 没有 webhook 接收和签名校验
- 没有任务状态机
- 没有用户领取链接管理
- 没有消息投递模块
- 没有浏览器会话与订单任务的绑定关系
2.2 前端现状
当前前端是一个单页腾讯兑换工具。
- 路由只包含
/tx/browser,在 src/router/index.ts - 主页面在 src/views/tx/TencentBrowserView.vue
- 页面编排在 src/composables/useTencentBrowserSessionPage.ts
- 状态轮询在 src/composables/tencent/useTencentBrowserSessionPolling.ts
- 兑换前角色确认、兑换按钮、结果展示已经具备基础 UI,可复用
当前前端没有以下能力:
- 没有订单领取页
- 没有 token 校验页
- 没有订单详情页
- 没有任务状态驱动的前端展示
- 没有“订单上下文中的浏览器会话”概念
3. 总体架构
3.1 分层设计
建议分成 4 层:
- 平台接入层 负责接收订单创建成功、支付成功、售后等通知,完成签名校验和原始数据落库。
- 订单编排层 负责订单幂等、商品解析、CDK 预占、任务创建、链接生成、状态流转。
- 浏览器自动化执行层 复用现有腾讯浏览器会话、角色识别、验证码识别、兑换提交、截图生成能力。
- 用户交互层 用户通过领取链接进入前端页面,完成 QQ/WX 登录、角色确认、查看结果。
3.2 核心原则
- 订单与浏览器会话解耦
- CDK 先预占、后核销
- 所有平台通知必须幂等
- 用户访问链接使用 token,不直接使用订单号鉴权
- 浏览器自动化失败必须可重试、可人工接管
- 所有关键节点都要保留审计数据
4. 业务流程
4.1 主流程
- 平台发送订单通知到后端 webhook。
- 后端校验签名,按平台订单号幂等写入
orders。 - 根据订单商品、规格、渠道信息生成一个或多个
delivery_tasks。 - 系统从
cdk_inventory中为任务预占可用 CDK。 - 系统生成
claim_token,构造领取链接。 - 系统通过站内消息、短信、客服人工转发或支付成功页展示,把链接给用户。
- 用户打开链接,前端调用后端校验 token 和任务状态。
- 用户选择 QQ 或 WX 登录,后端创建浏览器 session,并绑定到
delivery_task。 - 用户扫码后,后端识别角色、大区、昵称等信息。
- 前端展示识别结果,用户确认角色无误。
- 后端自动执行 CDK 提交、验证码识别、截图生成。
- 成功后任务进入
redeemed,CDK 进入delivered。 - 前端展示截图、结果消息、订单号、角色信息。
4.2 异常流程
- webhook 重复推送:只更新状态,不重复创建任务
- 商品无法匹配 CDK:任务进入
manual_review - CDK 库存不足:任务进入
waiting_inventory - token 过期:提示用户重新获取领取链接
- 浏览器自动化失败:任务进入
retry_pending或manual_review - 用户长时间未领取:任务进入
expired,释放预占 CDK
5. 数据模型设计
建议先上 SQLite,跑通后如有并发压力再切 PostgreSQL。当前项目还没有数据库依赖,先用 SQLite 可以把复杂度压低。
5.1 orders
订单主表。
字段建议:
idplatformplatform_order_idorder_statuspay_statusbuyer_idbuyer_nameraw_payload_jsoncreated_atupdated_atpaid_at
约束建议:
platform + platform_order_id唯一
5.2 order_items
订单子项表,用于一单多商品。
字段建议:
idorder_idsku_codesku_namequantityspec_jsondelivery_modecreated_atupdated_at
5.3 cdk_inventory
CDK 库存表。
字段建议:
idbatch_nosku_codecdk_codestatusreserved_by_task_iddelivered_atinvalid_reasoncreated_atupdated_at
状态建议:
availablereserveddeliveredinvalid
5.4 delivery_tasks
交付任务表,是整个方案的核心。
字段建议:
idorder_idorder_item_idplatform_order_idtask_notask_statuslogin_typeclaim_token_idreserved_cdk_idbrowser_session_idrole_idrole_nameareapartitionnicknameresult_coderesult_messagescreenshot_pathartifacts_jsonlast_errorretry_countexpires_atclaimed_atredeemed_atcreated_atupdated_at
状态建议:
pending_paymentpaidwaiting_inventorycdk_reservedlink_generatedclaimedrole_confirmedredeemingredeemedretry_pendingmanual_reviewexpiredclosed
5.5 claim_tokens
领取链接表。
字段建议:
idtask_idtokenstatusexpired_atused_atmax_use_countused_countcreated_atupdated_at
状态建议:
activeusedexpiredrevoked
5.6 webhook_events
保留原始通知,便于审计和重放。
字段建议:
idplatformevent_typeevent_keysignature_validheaders_jsonquery_jsonbody_jsonprocessedprocess_errorcreated_at
6. 后端模块拆分
建议在现有后端上新增以下模块。
6.1 路由层
新增路由目录建议:
src/routes/webhooks.jssrc/routes/orders.jssrc/routes/claims.jssrc/routes/admin.js
说明:
webhooks.js接平台推送orders.js给内部系统或管理端查订单和任务claims.js给用户领取页调用admin.js做手工补单、重试、失效处理
6.2 服务层
新增服务建议:
src/services/order-service.jssrc/services/order-item-service.jssrc/services/delivery-task-service.jssrc/services/cdk-service.jssrc/services/claim-service.jssrc/services/webhook-service.jssrc/services/message-service.jssrc/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.jssrc/repositories/order-repo.jssrc/repositories/task-repo.jssrc/repositories/cdk-repo.jssrc/repositories/claim-repo.jssrc/repositories/webhook-repo.js
7. 对现有浏览器自动化能力的复用方式
现有后端能力不废弃,而是作为执行器继续使用。
7.1 可直接复用的能力
- 创建浏览器会话:
createTencentBrowserSession - 轮询会话状态:
getTencentBrowserSession、getTencentBrowserSessionSummary - 扫码登录后识别角色信息:
session.js内已有活动页信息抽取逻辑 - 执行兑换:
redeemTencentBrowserSession - 生成截图和结果 JSON:
saveRedeemArtifacts - OCR worker:继续使用当前
uv run ocr-worker worker
7.2 需要补的一层包装
不要让订单系统直接操作裸 sessionId,应通过 redeem-executor-service 管理:
- 根据
taskId创建浏览器 session - 把
taskId <-> sessionId关系落库 - 用户扫码完成后,把识别到的角色信息同步回任务
- 用户确认角色后,再触发兑换
- 兑换完成后,把截图路径和最终结果回写到任务
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 更稳的做法
更稳的做法不是把管理页面直接混进用户领取页,而是拆成两套入口:
- 用户前台 继续服务用户领取、扫码登录、角色确认、结果查看。
- 运营后台 专门服务订单排查、CDK 管理、任务重试、异常处理。
建议形式:
- 用户前台继续使用 order-site-rewrite
- 运营后台先作为同仓库下独立路由模块实现,例如
/admin - 后端单独提供
/api/v1/admin/*接口
这样做的好处:
- 用户链路和运营链路隔离
- 权限边界更清楚
- 页面状态不会互相污染
- 以后即使单独拆管理后台,也不会影响用户前台
9.3 后台第一版目标
第一版后台不追求复杂权限系统和漂亮界面,目标只有一个:
让你能稳定运营这条自动交付链路。
第一版最少应覆盖:
- 查订单
- 查交付任务
- 导入和维护 CDK
- 查看 webhook 日志
- 重试失败任务
- 释放预占 CDK
- 重新生成领取链接
9.4 后台页面模块建议
1. 概览页
展示核心数字:
- 今日订单数
- 已支付待领取数
- 领取中任务数
- 兑换成功数
- 异常任务数
- 库存不足的 SKU 数
这页主要用来让你快速判断系统是否在健康运行。
2. 订单列表页
展示字段建议:
- 订单号
- 平台
- 商品摘要
- 支付状态
- 订单状态
- 创建时间
- 更新时间
- 对应任务数
支持筛选:
- 平台订单号
- 支付状态
- 时间范围
- 商品 SKU
进入详情后可查看:
- 原始订单通知
- 子项列表
- 对应交付任务
3. 交付任务列表页
这是后台最核心的页面。
展示字段建议:
task_no- 平台订单号
- 商品 SKU
- 当前任务状态
- 预占 CDK
- 登录方式
- 角色名
- 角色 ID
- 浏览器 sessionId
- 重试次数
- 最后错误
- 创建时间
- 更新时间
支持操作:
- 查看详情
- 重新生成链接
- 重试任务
- 释放 CDK
- 关闭任务
- 标记人工处理
4. CDK 管理页
这是第二核心页面。
应支持:
- 单条新增
- 批量导入
- 按 SKU 查看库存
- 查看状态分布
- 查看预占任务
- 手动失效
- 手动释放
展示字段建议:
sku_codebatch_nocdk_codestatusreserved_by_task_idcreated_atupdated_at
导入方式建议至少支持:
- 文本框多行粘贴导入
- CSV 文件导入
导入校验建议:
- 去重
- 空值校验
- SKU 必填
- 批次号可选但建议保留
5. webhook 日志页
展示:
- 平台
- 事件类型
- 平台订单号
- 验签是否通过
- 是否处理成功
- 错误信息
- 到达时间
支持查看:
- headers
- query
- body
- 对应生成的订单和任务
可选操作:
- 手动重放处理
6. 任务详情页
任务详情页建议整合以下信息:
- 订单信息
- 商品和 SKU
- claim token 状态
- 浏览器 session 状态
- 角色识别结果
- 兑换结果
- 截图预览
- 原始错误日志
- 操作按钮
这个页面会成为你处理异常单的主工作台。
9.5 后台最小接口集
建议新增后台接口:
订单
GET /api/v1/admin/ordersGET /api/v1/admin/orders/:orderId
任务
GET /api/v1/admin/tasksGET /api/v1/admin/tasks/:taskIdPOST /api/v1/admin/tasks/:taskId/retryPOST /api/v1/admin/tasks/:taskId/closePOST /api/v1/admin/tasks/:taskId/release-cdkPOST /api/v1/admin/tasks/:taskId/regenerate-claim-linkPOST /api/v1/admin/tasks/:taskId/mark-manual-review
CDK
GET /api/v1/admin/cdksPOST /api/v1/admin/cdksPOST /api/v1/admin/cdks/importPOST /api/v1/admin/cdks/:cdkId/invalidatePOST /api/v1/admin/cdks/:cdkId/release
webhook
GET /api/v1/admin/webhook-eventsGET /api/v1/admin/webhook-events/:eventIdPOST /api/v1/admin/webhook-events/:eventId/replay
概览
GET /api/v1/admin/dashboard/summary
9.6 后台权限建议
第一版建议至少分两层:
admin可导入 CDK、改任务状态、重试任务、关闭任务、重放 webhook。operator可查订单、查任务、看截图、看日志,但不能做高风险修改。
如果第一版先不做完整账号体系,也至少要做一个最简单的后台鉴权,例如:
- 独立后台口令
- 基础登录态
- 后端校验管理接口访问权限
不要把后台接口直接裸露给公网无鉴权访问。
9.7 后台与前台的边界
建议明确边界:
- 前台只处理用户领取和结果查看
- 后台只处理运营和异常处置
前台不要出现:
- 手动改任务状态
- 手动改 CDK
- 看其他订单
后台不要承担:
- 用户扫码登录入口
- 用户领取页展示逻辑
这样职责清晰,后续维护成本最低。
9.8 后台的落地顺序
建议放在整体计划的阶段 4 中作为正式内容,但有两个能力应提前做:
- CDK 导入
- 任务列表与详情查看
原因:
- 没有 CDK 导入能力,阶段 1 根本跑不顺
- 没有任务查看能力,阶段 2 和阶段 3 出问题时无法排查
因此更合适的执行顺序是:
- 阶段 1 完成订单骨架后,先补一个极简后台
- 阶段 2 和 3 继续完善业务闭环
- 阶段 4 再补全 webhook 重放、统计面板、权限细化
9.9 后台最小可行版本
后台 MVP 建议包含:
- 登录页
- 概览页
- 订单列表页
- 任务列表页
- 任务详情页
- CDK 导入页
- webhook 日志页
这已经足够支撑第一版系统上线和日常运营。
9.10 结论
后端侧需要一个管理界面,而且应当作为正式模块设计,不建议临时拼接。
更稳的做法是:
- 用户前台与运营后台分离
- 后台先做最小可行版本
- 优先覆盖查单、查任务、导入 CDK、重试异常
- 后台接口独立鉴权
10. 前端设计
9.1 路由设计
现有前端只有 /tx/browser,建议新增:
/claim/:token
这个页面才是正式业务入口。
/tx/browser 保留为内部调试工具页,不作为正式交付页面。
9.2 页面结构
建议页面拆成 4 个区域:
- 订单摘要区 展示订单号、商品名、状态说明
- 登录绑定区 复用现有二维码卡片能力
- 角色确认区 复用现有角色展示和确认能力
- 结果展示区 复用现有截图和结果卡片能力
9.3 可复用的前端模块
可以复用:
- TencentAuthCard.vue
- TencentRedeemPanel.vue
- TencentResultPanel.vue
- useTencentBrowserSessionPolling.ts
- useTencentBrowserSessionPresentation.ts
但需要改造:
- 不再让前端自己输入兑换码
- 不再让前端自己决定是否创建裸 session
- 页面所有操作都基于
claim token
9.4 前端状态设计
建议页面状态围绕任务状态展开,而不是围绕裸 session:
tokenValidtaskStatusorderSummarysessionactivityInforoleConfirmedredeemResultscreenshotUrl
11. 状态机设计
10.1 任务状态流转
主状态机建议如下:
pending_paymentpaidwaiting_inventorycdk_reservedlink_generatedclaimedrole_confirmedredeemingredeemed
异常分支:
retry_pendingmanual_reviewexpiredclosed
10.2 CDK 状态流转
availablereserveddelivered
异常分支:
invalid
10.3 token 状态流转
activeused
异常分支:
expiredrevoked
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
- 生成领取链接
要做的事:
- 增加数据库和表结构
- 实现 webhook 验签和幂等
- 实现订单、任务、CDK、token 的 repository 和 service
- 实现任务状态机基础逻辑
- 实现领取链接生成
阶段产出:
- 支付成功后,系统能自动生成领取任务和领取链接
阶段 2:极简后台 + 前端领取页
目标:
- 有基本运营能力
- 用户通过 token 打开页面
- 看见订单信息
- 创建登录 session
- 扫码并确认角色
要做的事:
- 新增
/admin路由和极简后台骨架 - 实现订单列表、任务列表、任务详情、CDK 导入
- 新增
/claim/:token路由 - 新增 claim 页面和 API service
- 复用当前二维码、轮询、角色确认组件
- 页面状态从
session 驱动改成task + session 双驱动
阶段产出:
- 运营能查任务和导入 CDK,用户能进入正式领取页面并完成角色绑定
阶段 3:自动兑换闭环
目标:
- 用户确认角色后自动提交 CDK
- 生成截图并前端展示
要做的事:
- 封装
redeem-executor-service - 将任务与 session 建立绑定关系
- 后端从预占 CDK 读取兑换码,不再由前端输入
- 回写截图路径、业务返回码、错误信息
阶段产出:
- 从订单通知到最终截图形成完整闭环
阶段 4:补偿和完整运营能力
目标:
- 可重试
- 可过期释放
- 可人工处理
- 有更完整的后台能力
要做的事:
- 增加任务重试接口
- 增加过期清理任务
- 增加 webhook 重放
- 增加概览统计和异常筛选
- 增加后台权限细化
- 增加关键日志和审计落库
阶段产出:
- 系统具备基本上线可运维性
16. 最小可行版本定义
第一版上线目标不建议追求“完全无人值守”,建议定义为:
- 平台支付成功后自动创建领取任务
- 自动预占 CDK
- 自动生成领取链接
- 用户进入页面后自行扫码登录
- 用户确认角色后系统自动提交 CDK
- 成功后展示截图
- 运营可在后台查看订单、任务、库存和 webhook 日志
- 失败后进入重试或人工处理
这是当前代码基础上最稳、最容易落地、售后风险最低的版本。
17. 关键决策结论
- 采用方案 A 是合理的
- 不建议直接用订单号做访问凭证
- 不建议前端继续保留“手输 CDK”的正式业务模式
- 不建议一开始就做全自动零确认绑定
- 建议先做订单编排层,再复用现有浏览器自动化执行层
- 建议优先做 SQLite 版本跑通链路
- 建议把
/tx/browser保留为内部调试页,把/claim/:token做成正式业务入口 - 建议把运营后台作为正式模块纳入,而不是后补工具页
18. 下一步落地建议
下一步建议直接进入“阶段 1 实施设计”,输出更细的开发清单:
- 数据库表结构 SQL
- 后端目录和文件改造清单
- API 请求和响应示例
- 前台
/claim/:token页面状态图 - 后台
/admin页面结构与接口契约 - 任务状态机枚举定义
如果继续推进,我建议下一步先把:
- 表结构
- 后端 API 草案
- 前端
/claim/:token页面接口契约 - 后台
/admin核心接口契约
这三部分先定死,然后再开始编码。