# 状态中心 ↔ 艺赛旗并行开发方案 ## 核心原则:契约驱动,各自独立,最后对接 双方只通过 **HTTP 回调契约** 交互,这个契约在交接文档里已经明确定义。在对接前,双方各自用假数据/假回调验证自己的逻辑,互不阻塞。 --- ## 对接契约(双方共同遵守) ### 基础信息 - **状态中心地址**:联调时给艺赛旗实际 IP,如 `http://192.168.x.x:8000` - **鉴权**:所有回调必须带 header `X-Agent-Token: `(默认 `dev-agent-token`,上线换正式值) - **幂等键**:`complete` 回调可带 `idempotencyKey`,重发同键不会二次推进 ### 三个回调接口(艺赛旗调状态中心) #### 1. 节点开始 ```bash POST /api/jobs/{jobId}/nodes/{nodeCode}/start Content-Type: application/json X-Agent-Token: dev-agent-token { "attemptNo": 1, "executorPlatform": "iS-RPA", "operator": "agent" } ``` #### 2. 节点成功 ```bash POST /api/jobs/{jobId}/nodes/{nodeCode}/complete Content-Type: application/json X-Agent-Token: dev-agent-token { "attemptNo": 1, "executorRunId": "YD-RUN-001", "idempotencyKey": "uuid-xxx", "businessRefs": { "contractSystemNo": "HT2026...", // 三把钥匙,对应节点必传 "rebateOrderNo": "FL2026...", "m5OrderNo": "M5-xxx", "receiptNoticeNo": "SH-xxx" }, "summary": { "orderCount": 12 }, "artifacts": [ { "artifactType": "SOURCE_EXCEL", "storagePath": "/data/jobs/xxx/orders.xlsx", "fileHash": "sha256:abc", "visibleRoles": ["PURCHASER", "PLANNER"], "isMasked": false } ] } ``` **返回**:`{"ok": true, "next_node": "OCR_EXTRACT"}` ← 艺赛旗拿到 `next_node` 继续跑下一节点 #### 3. 节点失败 ```bash POST /api/jobs/{jobId}/nodes/{nodeCode}/fail Content-Type: application/json X-Agent-Token: dev-agent-token { "attemptNo": 1, "errorCode": "SYS_LOGIN_FAIL", // 见交接文档错误码表 "errorSummary": "验证码识别失败", "retryable": true, // 系统类填 true,业务类填 false "routeRole": "OPS", // OPS/PLANNER/PURCHASER "artifacts": [] } ``` **返回**:`{"ok": true, "job_status": "FAILED_RETRYABLE"}` ### 必须回传的业务编号(三把钥匙) | 节点 | 必传字段 | 说明 | | --- | --- | --- | | CONTRACT_CREATE | `contractSystemNo` | 合同编号(后续返利、M5靠它串) | | REBATE_HEADER | `rebateOrderNo` | 返利中心总单号 | | M5_RECEIPT_NOTICE | `receiptNoticeNo` | 收货通知单号(全链终点) | 不传或传空,状态中心返回 **400 拒绝**。 ### 错误码规范(部分,全表见交接文档) | 错误码 | 含义 | retryable | routeRole | | --- | --- | --- | --- | | SYS_LOGIN_FAIL | 登录失败 | true | OPS | | SYS_PAGE_TIMEOUT | 页面超时 | true | OPS | | UI_ELEMENT_NOT_FOUND | 页面改版找不到元素 | false | OPS | | BIZ_CONTRACT_DISABLED | 供应商合同被禁 | false | PURCHASER | | M5_QC_MISSING | M5质管资料缺失 | - | - | | REBATE_DUP_CODE | 返利重复编码 | false | PURCHASER | **特殊规则**:`M5_QC_MISSING` 无条件转 `FAILED_MANUAL`,`retryable` 填什么都没用。 --- ## 状态中心这边(你的任务) ### 阶段1:独立验证(不依赖艺赛旗) **时间**:1-2天 **目标**:后端在真 MySQL 上用假回调跑通全链 #### 任务 1. **环境准备** - Docker 起 MySQL 8,执行 `schema.sql` 建库。 - 配 `MYSQL_DSN`,起后端服务。 - 前端 `npm run dev` 起来。 2. **用假回调跑通一单** - 方式1:用 curl 按顺序调回调接口。 - 方式2:用前端"开发:节点回调模拟"区域的按钮。 - **路径**:建单 → ORDER_FETCH start/complete → PRODUCT_MATCH run(有 MULTI) → 人工确认 → SOURCE_ADD 人工提交 → CONTRACT_CREATE complete(带合同编号) → ... → ARCHIVE_JOB。 - **验证点**: - job 状态正确推进(PENDING → RUNNING → WAITING_USER → ...)。 - 业务编号正确落库(合同编号、返利总单号、M5单号、收货通知单号)。 - job_node 每个节点有历史记录,attempt_no 正确。 - user_task 在该创建时创建,提交后关闭。 - job_artifact 证据落库。 - 条件节点(OCR_EXTRACT/SOURCE_ADD)正确跳过或执行。 3. **发现并修复真实问题** - SQL 语法问题(如 MySQL 8 的保留字冲突)。 - JSON 列序列化问题(Python dict → MySQL JSON → 前端拿到的是对象还是字符串)。 - 事务回滚是否正确。 - datetime 字段格式(MySQL 存的 `YYYY-MM-DD HH:MM:SS`,前端拿到的格式)。 - 幂等逻辑是否真的防重(重复 complete 同一节点,状态不会乱)。 4. **记录真实 API 响应** - 把 `GET /api/jobs/{id}` 的完整响应存下来,给 UI 优化做参考。 #### 产出 - ✅ 一个能在真 MySQL 上跑通全链的后端(用假回调验证)。 - ✅ 一份真实响应示例 + 发现的问题清单。 - ✅ 前端能正常展示真实数据(不再依赖 `demoJobDetailMap`)。 ### 阶段2:优化与补全(不依赖艺赛旗) **时间**:2-3天 **目标**:UI 优化 + 补缺失接口 + 加调度重试 #### 任务 1. **UI 优化** - 按刚才的方案,分区 + 可折叠 + 调试开关。 - 基于阶段1 的真实响应,确保所有字段正确展示。 2. **补缺失接口** - `GET /api/jobs/{id}/match/unmatched`:返回该 job 未命中的订单行列表。 - 其他小接口(如果阶段1 发现有遗漏)。 3. **调度与重试(任务6)** - 定时建单:每天早上扫前一天订单。 - 系统类错误自动重试(`FAILED_RETRYABLE` 状态的 job,重试 1-2 次)。 - 超时升级:合同审批超时、人工待办超时转 OPS。 #### 产出 - ✅ 优化后的前端(信息分层清晰,调试模式可开关)。 - ✅ 完整的接口(验收点名的都实现)。 - ✅ 调度器能定时触发建单和重试。 ### 阶段3:准备对接(等艺赛旗阶段2 完成) **时间**:半天 **目标**:给艺赛旗提供联调环境 #### 任务 1. **部署联调环境** - 把后端部署到艺赛旗能访问的机器(或你本机开端口映射)。 - 给艺赛旗提供:基础地址(如 `http://192.168.x.x:8000`) + 正式 `X-Agent-Token`。 2. **准备测试数据** - 在库里建几个不同类型的 job: - 医药城线上单(会跳过 OCR_EXTRACT)。 - 线下单(会走 OCR_EXTRACT)。 - 有对码 MULTI 的单。 - 缺货源的单(会走 SOURCE_ADD)。 - 每个 job 停在 ORDER_FETCH 节点,等艺赛旗来调 start/complete 推进。 3. **写联调检查表** - 逐节点列验收点,双方对着检查表勾。 #### 产出 - ✅ 一个可供艺赛旗调用的联调环境。 - ✅ 测试数据 + 联调检查表。 --- ## 艺赛旗那边(你要转述给他们) ### 阶段1:独立开发(不依赖状态中心) **时间**:2-3周(他们的活重) **目标**:每个节点的页面自动化都能独立跑通 #### 任务 1. **理解回调契约** - 仔细读 `给艺赛旗的开发交接.md`。 - 确认三个回调接口的字段、错误码表。 2. **逐节点开发页面自动化** - **节点顺序**(他们的优先级): 1. ORDER_FETCH(订单获取):登录医药城/小药药 → 导出 Excel → 存证据。 2. CONTRACT_CREATE(合同建单):登录合同系统 → 填表 → 上传 PDF(医药城) → 提交送审 → 拿到合同编号。 3. REBATE_HEADER(返利总单):登录返利中心 → 按合同编号查总单 → 填必填项 → 保存 → 拿到返利总单号。 4. REBATE_IMPORT(返利细单):下载模板 → 填字段 → 上传 → 读成功/失败行。 5. REBATE_SUBMIT(返利送审):点"提交送审"按钮。 6. M5_CREATE_ORDER(M5建单):桌面自动化 → 录单 → **停,不保存**。 7. CONTRACT_APPROVE_WAIT(合同审批):定时查审批状态(到点被状态中心扫到时才查一次)。 - **每个节点独立验证**: - 不调状态中心,在自己环境里跑(比如用测试账号登录真实系统,手工给初始数据)。 - 验证能拿到业务编号(合同编号、返利总单号等)。 - 验证错误情况能识别出正确的错误码(如登录失败是 `SYS_LOGIN_FAIL`,合同被禁是 `BIZ_CONTRACT_DISABLED`)。 3. **本地模拟回调** - 自己起个假的状态中心(或者直接用 curl 打印 JSON),验证回调 payload 格式对不对。 - 示例(他们自己写个小脚本验证): ```python # 假的状态中心 mock.py from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/api/jobs//nodes//complete', methods=['POST']) def complete(job_id, node_code): data = request.json print(f"收到 {node_code} complete:", data) # 检查必传字段 if node_code == 'CONTRACT_CREATE' and not data.get('businessRefs', {}).get('contractSystemNo'): return jsonify({"error": "缺少合同编号"}), 400 return jsonify({"ok": True, "next_node": "下一节点"}) app.run(port=8000) ``` #### 产出 - ✅ 每个节点的页面自动化脚本(iS-RPA 流程)。 - ✅ 能正确识别各种错误并返回对应错误码。 - ✅ 能拿到三把钥匙(合同编号、返利总单号、收货通知单号)。 ### 阶段2:准备对接(等状态中心阶段3 准备好) **时间**:1天 **目标**:改成调真实状态中心接口 #### 任务 1. **获取联调环境信息** - 从状态中心拿到:基础地址 + `X-Agent-Token`。 2. **改造回调逻辑** - 把阶段1 的假回调改成调真实状态中心接口。 - **关键**:拿到 `complete` 返回的 `next_node`,继续跑下一节点(不要自己写死顺序)。 3. **小范围试跑** - 先跑一个最简单的节点(如 ORDER_FETCH),验证回调能调通、状态中心能正确推进。 #### 产出 - ✅ 能调通状态中心回调接口的 RPA 脚本。 --- ## 联调阶段(双方一起) **时间**:3-5天 **目标**:端到端跑通一单真实流程 ### 任务 1. **按节点逐个验证** - 顺序:ORDER_FETCH → (OCR_EXTRACT) → PRODUCT_MATCH(状态中心内部) → (SOURCE_ADD 人工) → CONTRACT_CREATE → ... - 每个节点验证: - RPA 能正确调 start/complete/fail。 - 状态中心正确推进到下一节点或转人工。 - 业务编号正确落库。 - 证据正确存储。 2. **跑完整链** - 医药城线上单(跳过 OCR)。 - 线下单(走 OCR)。 - 有对码 MULTI 的单(走人工确认)。 - 缺货源的单(走 SOURCE_ADD 人工)。 - 合同审批等待(定时查几次,验证不会死等)。 3. **异常场景** - 登录失败 → 重试。 - 页面改版找不到元素 → 转 OPS。 - M5 质管缺失 → 直接 FAILED_MANUAL,不重试。 - 返利重复编码 → 该单挂起,整批继续。 4. **对照验收规范逐条勾** - 打开 `实施与验收规范.md`,每个节点的代码验收 + 业务验收两栏逐条过。 ### 产出 - ✅ 一个能端到端跑通的完整流程(从订单获取到归档)。 - ✅ 验收报告(哪些通过,哪些有问题)。 --- ## 时间线(并行开发) ``` 第1周 第2周 第3周 状态中心 │────阶段1────│───阶段2───│──阶段3──│────联调────│ 真MySQL跑通 UI优化+补全 准备环境 与艺赛旗对接 (1-2天) (2-3天) (半天) (3-5天) 艺赛旗 │────────────阶段1────────────│─阶段2─│────联调────│ 独立开发各节点页面自动化 改真回调 与状态中心对接 (2-3周) (1天) (3-5天) ``` **关键点**: - 前两周双方完全独立,互不阻塞。 - 状态中心先做完(1周内),等艺赛旗追上。 - 艺赛旗开发周期长(页面自动化细节多),但不影响状态中心进度。 - 第3周开始联调,双方都已在各自环境验证过,对接就是走形式。 --- ## 给艺赛旗的检查清单(转述) 在开始联调前,艺赛旗必须自证能做到: ### 功能检查 - [ ] 能登录医药城/小药药/合同系统/返利中心/M5,并正确处理验证码/超时。 - [ ] 能导出订单 Excel 并存到指定路径。 - [ ] 能建合同、上传 PDF(医药城)、拿到合同编号。 - [ ] 能在返利中心按合同编号查总单、填表、拿到返利总单号。 - [ ] 能下载返利细单模板、填字段、上传、读成功/失败行。 - [ ] 能点返利送审按钮。 - [ ] 能用桌面自动化在 M5 录单(但不保存)。 - [ ] 能定时查合同审批状态(不是连续轮询,是到点查一次)。 ### 回调检查 - [ ] 知道何时调 start、何时调 complete、何时调 fail。 - [ ] complete 时能正确带 `businessRefs`(三把钥匙对应节点必传)。 - [ ] fail 时能正确识别错误码(`SYS_*` vs `BIZ_*`,`retryable` 填对)。 - [ ] 拿到 `next_node` 后能继续跑下一节点(不写死顺序)。 - [ ] 所有回调都带 `X-Agent-Token` header。 ### 异常检查 - [ ] 登录失败时返回 `SYS_LOGIN_FAIL`,`retryable=true`。 - [ ] 页面改版找不到元素时返回 `UI_ELEMENT_NOT_FOUND`,`retryable=false`,`routeRole=OPS`。 - [ ] M5 质管缺失时返回 `M5_QC_MISSING`(状态中心会直接转 FAILED_MANUAL,不重试)。 - [ ] 供应商合同被禁时返回 `BIZ_CONTRACT_DISABLED`,`retryable=false`,`routeRole=PURCHASER`。 --- ## 总结 **你现在要做的**: 1. 马上开始阶段1(真 MySQL 跑通),1-2天搞定。 2. 接着做阶段2(UI 优化 + 补全),2-3天搞定。 3. 然后做阶段3(准备联调环境),半天搞定。 4. **这时你这边已经全部做完**,等艺赛旗追上(他们要 2-3 周)。 **艺赛旗要做的**(你转述给他们): 1. 阶段1:独立开发各节点页面自动化,2-3周。期间不需要状态中心配合,自己用假数据验证。 2. 阶段2:拿到状态中心联调环境信息后,改成调真实接口,1天。 3. 联调阶段:双方一起按检查表逐条验收,3-5天。 **不会互相阻塞**:你 1 周内做完你的,艺赛旗 2-3 周做完他们的,最后 3-5 天一起联调。 --- 要我把这份文档发给艺赛旗吗?或者你先看看有没有要调整的。