31 KiB
采购主链 RPA 一期设计文档(技术设计 / HLD+LLD)
用途:把《采购主链RPA一期需求文档_三方会审版》(业务规则)和《采购主链RPA一期开发规划》(排期与阶段)落成可建库、可对接口、可分工的技术设计底稿。 关系说明:本文取代早期《一期开发任务清单_表结构_API草案.md》的架构与数据模型部分——该草案工程框架扎实,但建立在"合同文件入池→OCR→映射"的错误骨架上,缺订单驱动主线、产品对码、三来源分流。本文保留其状态机/幂等/证据/待办思想,按订单驱动重排实体与节点。 术语:状态中心 = 我们自研的编排与审计中台;执行器 = RPA 平台(影刀/来也等);对码 = 电商药品匹配到 M5 货品ID/编码。
一、设计目标与非目标
1.1 设计目标
- 订单驱动:以电商订单(医药城/小药药)导出为入口,线下手工单为 OCR 分支,贯穿到 M5 录入待保存。
- 分层解耦:业务规则、状态机、审计、对码口径掌握在状态中心;RPA 平台仅做页面执行,可替换。
- 对码可迭代:对码表版本化、累积迭代,未命中一对多转人工,命中率可统计。
- 不可逆保护:一期末端"保存/签订/生成收货通知单"由人工执行,自动化做成配置开关。
- 可追溯:订单号→合同号→返利总单号→M5单号→收货通知单号全链路可查,每节点留证。
1.2 非目标(一期不做)
- 招财平台接入。
- M5 自动保存/签订(阶段三才放开,本期只留开关)。
- 加单/拆单的全自动重做(父订单号先记录,处理转人工,待确认)。
- 返利中心重复订单的全自动重建(规则未讲完,先转人工)。
二、系统架构
2.1 分层架构图
┌─────────────────────────────────────────────────────────────┐
│ 人工协同层 │
│ 企业微信应用消息/群机器人 + H5待办页(Vue/React) │
│ 待办推送 / 一对多对码选择 / 人工检查入口 / 修改留痕 │
└───────────────▲─────────────────────────────▲───────────────┘
│ 待办/回执 │ OAuth身份
┌───────────────┴─────────────────────────────┴───────────────┐
│ 状态中心(自研,核心资产) │
│ ┌──────────┬──────────┬──────────┬──────────┬────────────┐ │
│ │ 建单入池 │ 状态机 │ 对码引擎 │ 待办路由 │ 审计/证据 │ │
│ │ 幂等去重 │ 编排调度 │ mapping │ 角色分派 │ 事件流 │ │
│ └──────────┴──────────┴──────────┴──────────┴────────────┘ │
│ 统一节点契约 / 回调接口 / 执行平台路由 / 查询审计 API │
└──────▲───────────────▲──────────────────▲───────────────────┘
│ HTTP回调 │ OCR调用 │ 平台路由
┌──────┴──────┐ ┌──────┴──────┐ ┌───────┴─────────────────┐
│ RPA执行器 │ │ OCR服务 │ │ 对象存储(MinIO/NAS) │
│ 影刀/来也 │ │ PaddleOCR │ │ 合同PDF/截图/OCR JSON │
│ 登录/导出/ │ │ (本地优先) │ │ 证据目录 │
│ 建单/导入/ │ └─────────────┘ └─────────────────────────┘
│ 截图/上传 │
└──────▲──────┘
│ 操作
┌──────┴───────────────────────────────────────────────────────┐
│ 外部业务系统(RPA 通过 UI 操作,非 API 集成) │
│ 医药城 │ 小药药 │ 合同系统 contract.gzmpc.com │ 返利中心 │
│ │ │ │ flgzmpc.com │
│ M5(INCA) 采购订单管理 1055 │
└───────────────────────────────────────────────────────────────┘
2.2 组件职责边界
| 组件 | 职责 | 明确不做 |
|---|---|---|
| 状态中心 | job 生命周期、状态机流转、对码、幂等、待办路由、证据审计、平台路由、统一查询 | 不直接操作外部系统页面 |
| RPA 执行器 | 登录/导出Excel/建合同/PDF合并上传/返利导入/M5录入/截图/文件读写/HTTP回调 | 不做流程主状态判断、不做业务规则决策 |
| OCR 服务 | 线下手工单图片→结构化字段 JSON + 置信度 | 仅线下单,线上走 Excel 不经 OCR |
| 企业微信/H5 | 待办通知、一对多选择、人工确认、修改留痕、身份识别 | 不直接写业务结果,须经状态中心标准动作 |
| 对象存储 | 合同PDF、截图、OCR JSON、日志等证据落盘 | — |
关键设计原则:RPA 平台"半节点执行过程中不跨平台接力";支持"按新单切换平台""按节点配置平台";切换前跑统一 POC 用例。
2.3 技术选型(推荐,可依公司栈调整)
| 层 | 采用 | 备选 |
|---|---|---|
| 状态中心 | Python 3 + FastAPI(开发快、与 OCR/对码脚本同栈、Pydantic 校验回调契约方便) | Java + Spring Boot |
| 数据库 | MySQL 8(状态中心自有库,只存流程状态+累积别名+证据) | PostgreSQL |
| 状态机 | 轻量自实现(NODE_ORDER 顺序驱动 + 7 状态枚举) | — |
| 对象存储 | MinIO / 公司现有 NAS | 本地文件系统 |
| RPA | 影刀 / 来也UiBot / 艺赛旗 择一 POC | — |
| OCR | PaddleOCR 本地部署(无信息安全顾虑) | 阿里/腾讯云OCR |
| 前端 | Vue/React + 企业微信 OAuth | — |
注:状态中心确定采用 Python 3 + FastAPI + MySQL 8,骨架代码在
state_center/(建库脚本、对码引擎、三回调、整单查询已跑通)。若后续团队要平移到 Java+Spring Boot,数据模型与接口契约保持不变即可。
2.4 部署拓扑
公司内网(与业务系统同网段,便于 RPA 访问)
├── 状态中心服务(容器/虚机) ── MySQL主 ── 对象存储MinIO
├── OCR服务(本地GPU/CPU) [仅阶段2.2线下单启用]
├── RPA运行机(独立虚机, 保持登录态, 定时/7×24)
└── 企业微信应用(内网可信域名 + H5静态站)
安全边界:
- RPA 回调接口用
X-Agent-Token;内部服务用Bearer service-token;H5 用企业微信身份换 JWT。 - 合同/OCR 证据按角色可见(
visible_roles),敏感文件脱敏标记is_masked。 - 网络暴露提醒:状态中心所有写接口须鉴权,回调接口须校验 token + 幂等键,禁止无认证暴露。
三、数据模型设计
按订单驱动重排。核心实体:
job / job_node / order_item / product_match / user_task / mapping_config / executor_binding_config / job_artifact / job_event。 相比早期草案新增:order_item(订单明细行)、product_match(对码结果,核心难点落库);job主键由"合同文件"改为"订单批次"。
3.1 实体关系
job (一次流程实例 = 同供应商同日一批订单)
├─1:N─ order_item (订单明细行, 对码前的原始行)
├─1:N─ product_match (对码结果, 命中/未命中/一对多候选)
├─1:N─ job_node (每节点每次尝试)
├─1:N─ user_task (人工待办)
├─1:N─ job_artifact (证据)
└─1:N─ job_event (事件流)
mapping_config (对码表/供应商对照/承付结算映射, 版本化)
executor_binding_config (节点→平台路由)
3.2 job(流程实例)
一次流程实例 = 同一供应商 + 同一订单日期 + 同一来源 的一批订单(对应一条合同)。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| job_id | varchar(64) unique | 对外流程单号 |
| business_key | varchar(160) | 业务唯一键:来源+供应商+订单日期(见§5.2) |
| parent_job_id | varchar(64) null | 重跑/加单来源 |
| source_type | varchar(16) | YYC(医药城)/XYY(小药药)/OFFLINE(线下单) |
| order_date | date | 订单日期(=合同签约日期) |
| supplier_name | varchar(128) | 供应商/商家 |
| order_no_list_json | json | 该批订单号数组(贯穿追溯) |
| contract_template | varchar(16) null | FORMAT(格式)/NON_FORMAT(非格式承兑) |
| need_pdf_upload | tinyint | 医药城=1,小药药=0 |
| planner_name | varchar(64) null | 计划员 |
| current_node_code | varchar(64) | 当前节点 |
| status | varchar(24) | 见§4.1 |
| mapping_version | varchar(64) null | 绑定的对码表版本 |
| executor_platform | varchar(16) null | 默认执行平台 |
| contract_system_no | varchar(64) null | 合同系统编号 |
| rebate_order_no | varchar(64) null | 返利中心总单号 |
| m5_order_no | varchar(64) null | M5 采购订单号 |
| receipt_notice_no | varchar(64) null | 收货通知单号 |
| current_role | varchar(16) null | 当前责任角色 |
| current_assignee | varchar(64) null | 当前责任人 |
| auto_save_enabled | tinyint | M5自动保存开关(阶段一=0) |
| created_by | varchar(64) | |
| created_at / updated_at / finished_at | datetime |
索引:uniq_job_business_open(business_key, status)、idx_supplier_date(supplier_name, order_date)、idx_status、idx_created_at。
3.3 order_item(订单明细行,对码前原始数据)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| job_id | varchar(64) | |
| order_no | varchar(64) | 订单号 |
| parent_order_no | varchar(64) null | 父订单号(加单/拆单) |
| raw_product_name | varchar(255) | 平台品名 |
| platform_product_code | varchar(64) null | 平台产品编码 |
| spec | varchar(128) null | 规格(对码痛点:15×2 vs 30片) |
| manufacturer | varchar(255) null | 厂家(含进口英文) |
| approval_no | varchar(64) null | 国药准字/批准文号 |
| qty | decimal(14,2) | 数量 |
| unit_price | decimal(14,4) null | 单价 |
| source_row_no | int | 原始 Excel 行号(回溯) |
| created_at | datetime |
索引:idx_job(job_id)、idx_order_no。
3.4 product_match(对码结果,核心难点落库)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| job_id | varchar(64) | |
| order_item_id | bigint | 对应订单行 |
| match_key | varchar(512) | 四要素拼接键:通用名+批准文号+规格+厂家 |
| match_status | varchar(16) | EXACT(精确命中)/FUZZY(模糊)/MULTI(一对多)/NONE(未命中)/MANUAL(人工确认) |
| goods_id | varchar(64) null | 命中的 M5 货品ID |
| goods_code | varchar(64) null | 货品编码 |
| base_unit | varchar(32) null | 基本单位 |
| candidates_json | json null | 一对多候选列表(供H5选择) |
| source_add_needed | tinyint | 是否需 M5 新增货源 |
| confirmed_by | varchar(64) null | 人工确认人 |
| confirmed_at | datetime null | |
| mapping_version | varchar(64) | 使用的对码表版本 |
| created_at | datetime |
索引:idx_job(job_id)、idx_status(match_status)。
3.5 job_node(节点执行)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| job_id | varchar(64) | |
| node_code | varchar(64) | 见§4.2 |
| attempt_no | int | 第几次尝试(不覆盖历史) |
| status | varchar(24) | 节点状态 |
| executor_platform | varchar(16) null | 实际执行平台 |
| executor_run_id | varchar(64) null | 平台侧任务号 |
| started_at / ended_at | datetime null | |
| duration_ms | bigint null | |
| error_code | varchar(64) null | |
| error_summary | varchar(500) null | |
| role_at_failure | varchar(16) null | |
| output_json | json null | 节点输出(业务编号等) |
| callback_idempotency_key | varchar(128) null | 回调幂等键 |
| created_at | datetime |
索引:uniq_job_node_attempt(job_id, node_code, attempt_no)、idx_status。
3.6 user_task(人工待办)
沿用早期草案结构,reason_type 扩展订单驱动场景。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| task_id | varchar(64) unique | |
| job_id | varchar(64) | |
| node_code | varchar(64) | |
| role_code | varchar(16) | PLANNER/PURCHASER/QC/OPS |
| assignee_user_id | varchar(64) null | |
| title | varchar(255) | |
| reason_type | varchar(32) | MATCH_MULTI/MATCH_NONE/SOURCE_ADD/OCR_LOW_CONF/REBATE_DUP/QC_MISSING/CONTRACT_DISABLED/... |
| reason_user_friendly | varchar(500) | 人话说明 |
| why_me_text | varchar(255) | 为什么派给你 |
| editable_fields_json | json | 白名单字段 |
| action_schema_json | json | 允许动作 |
| comparison_snapshot_json | json null | 原值/建议/当前对比 |
| candidates_json | json null | 一对多对码候选 |
| status | varchar(16) | OPEN/SUBMITTED/CANCELLED/EXPIRED |
| submit_token | varchar(64) | 防重复 |
| submitted_action | varchar(24) null | |
| submitted_payload_json | json null | |
| submitted_by / submitted_at | ||
| due_at | datetime null | 超时升级 |
| created_at | datetime |
3.7 mapping_config(映射/对码,版本化)
统一映射表,config_type 区分用途。对码表是其中 PRODUCT 类型的累积数据。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| config_type | varchar(24) | PRODUCT(对码)/SUPPLIER(供应商对照)/SETTLEMENT/PAYMENT_TERM/PLANNER_ROUTE |
| config_key | varchar(512) | 对码时=四要素拼接键 |
| source_value | varchar(512) | 来源值 |
| target_value | varchar(512) | 目标值(对码=货品ID+编码+单位JSON) |
| extra_json | json null | 扩展 |
| version_no | varchar(64) | 版本号 |
| is_active | tinyint | |
| effective_from / effective_to | datetime | |
| updated_by / updated_at |
索引:idx_type_key(config_type, config_key)、idx_active(is_active)。
3.8 executor_binding_config / job_artifact / job_event
沿用早期草案结构(见《一期开发任务清单_表结构_API草案.md》§4.5/4.6/4.7)。要点:
executor_binding_config:node_code → preferred_platform/fallback_platform + required_capabilities_json + route_policy。job_artifact:artifact_type ∈ {SOURCE_EXCEL, SOURCE_IMAGE, OCR_JSON, CONTRACT_PDF, MERGED_PDF, SCREENSHOT, TEMPLATE, LOG};证据目录job_id/node_code/attempt_no/。job_event:event_type ∈ {JOB_CREATED, NODE_STARTED, NODE_SUCCESS, NODE_FAILED, TASK_CREATED, TASK_SUBMITTED, MANUAL_HANDOFF, JOB_FINISHED};operator_type ∈ {SYSTEM, ROBOT, USER}。
3.9 关键 DDL 片段(新增表)
CREATE TABLE job (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
job_id VARCHAR(64) NOT NULL UNIQUE,
business_key VARCHAR(160) NOT NULL,
parent_job_id VARCHAR(64) NULL,
source_type VARCHAR(16) NOT NULL, -- YYC/XYY/OFFLINE
order_date DATE NOT NULL,
supplier_name VARCHAR(128) NOT NULL,
order_no_list_json JSON NULL,
contract_template VARCHAR(16) NULL, -- FORMAT/NON_FORMAT
need_pdf_upload TINYINT NOT NULL DEFAULT 0,
planner_name VARCHAR(64) NULL,
current_node_code VARCHAR(64) NOT NULL,
status VARCHAR(24) NOT NULL,
mapping_version VARCHAR(64) NULL,
executor_platform VARCHAR(16) NULL,
contract_system_no VARCHAR(64) NULL,
rebate_order_no VARCHAR(64) NULL,
m5_order_no VARCHAR(64) NULL,
receipt_notice_no VARCHAR(64) NULL,
current_role VARCHAR(16) NULL,
current_assignee VARCHAR(64) NULL,
auto_save_enabled TINYINT NOT NULL DEFAULT 0,
created_by VARCHAR(64) NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
finished_at DATETIME NULL,
KEY idx_supplier_date (supplier_name, order_date),
KEY idx_status (status),
KEY idx_created_at (created_at)
);
CREATE TABLE order_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
job_id VARCHAR(64) NOT NULL,
order_no VARCHAR(64) NOT NULL,
parent_order_no VARCHAR(64) NULL,
raw_product_name VARCHAR(255) NOT NULL,
platform_product_code VARCHAR(64) NULL,
spec VARCHAR(128) NULL,
manufacturer VARCHAR(255) NULL,
approval_no VARCHAR(64) NULL,
qty DECIMAL(14,2) NOT NULL,
unit_price DECIMAL(14,4) NULL,
source_row_no INT NULL,
created_at DATETIME NOT NULL,
KEY idx_job (job_id),
KEY idx_order_no (order_no)
);
CREATE TABLE product_match (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
job_id VARCHAR(64) NOT NULL,
order_item_id BIGINT NOT NULL,
match_key VARCHAR(512) NOT NULL,
match_status VARCHAR(16) NOT NULL, -- EXACT/FUZZY/MULTI/NONE/MANUAL
goods_id VARCHAR(64) NULL,
goods_code VARCHAR(64) NULL,
base_unit VARCHAR(32) NULL,
candidates_json JSON NULL,
source_add_needed TINYINT NOT NULL DEFAULT 0,
confirmed_by VARCHAR(64) NULL,
confirmed_at DATETIME NULL,
mapping_version VARCHAR(64) NOT NULL,
created_at DATETIME NOT NULL,
KEY idx_job (job_id),
KEY idx_status (match_status)
);
四、状态机与节点设计
4.1 job 主状态机(7 状态)
PENDING ──► RUNNING ──► SUCCESS
│ │ ▲
│ │ └──── (重试 attempt_no+1)
│ ├──► WAITING_USER ──► RUNNING (人工处理后继续)
│ ├──► FAILED_RETRYABLE ──► RUNNING
│ └──► FAILED_MANUAL (转人工手工做,如M5质管缺失)
└──────────────► CANCELLED
| 状态 | 含义 | 触发 |
|---|---|---|
| PENDING | 已建单待调度 | 建单成功 |
| RUNNING | 节点执行中 | 调度启动节点 |
| WAITING_USER | 等人工(对码未命中/一对多/货源新增/OCR确认) | 节点转人工 |
| SUCCESS | 全链完成(一期到 M5 待保存+人工完成后) | 末节点成功 |
| FAILED_RETRYABLE | 可自动重试(网页卡顿/登录失败) | 系统类错误 |
| FAILED_MANUAL | 停止转人工手工做(M5质管资料缺失) | 业务决策终止 |
| CANCELLED | 取消 | 人工终止 |
重试机制:节点失败不覆盖历史,attempt_no+1 新建 job_node 行;系统类自动重试 1-2 次,超时升级。
4.2 节点编码字典(订单驱动重排)
| 顺序 | 节点编码 | 内容 | 转人工场景 | 能力标签 |
|---|---|---|---|---|
| 1 | ORDER_FETCH |
登录平台,选前一天,导出订单Excel(状态过滤【待确认】) | 登录失败→OPS | desktop_ui, file_download |
| 1b | OCR_EXTRACT |
仅线下单:图片→字段 | 低置信度→PLANNER | ocr, http_callback |
| 2 | PRODUCT_MATCH |
四要素对码→货品ID/编码 | 未命中/一对多→PLANNER | (状态中心内算) |
| 2b | SOURCE_ADD |
未采购过品种→通知M5新增货源 | 缺货源→业务 | (通知) |
| 3 | CONTRACT_CREATE |
合同建单,选模板,签约日=订单日;医药城合并PDF上传 | 合同禁掉失效→采购邮件 | desktop_ui, file_upload, pdf_merge |
| 4 | CONTRACT_APPROVE_WAIT |
送审后等半小时查审核 | 未审完→等待 | http_callback, timer |
| 5 | REBATE_HEADER |
按合同号查总单,填必填项保存 | — | desktop_ui |
| 6 | REBATE_IMPORT |
细单模板导入(备注填订单号,特定补偿=0) | 重复编码→采购【待确认】 | excel_template, file_upload |
| 7 | REBATE_SUBMIT |
提交送审(决定M5能否带出协议)⭐ | 未送审→阻断 | desktop_ui |
| 8 | M5_CREATE_ORDER |
M5-1055新增订单+细单,备注填订单号,选正常保管账,不点保存 | 质管资料缺失→FAILED_MANUAL | desktop_ui |
| 9 | M5_MANUAL_CONFIRM |
人工检查→保存→签订(阶段一人工,开关控制) | — | (人工/可配自动) |
| 10 | M5_RECEIPT_NOTICE |
生成收货通知单 | — | (人工/可配自动) |
| 11 | ARCHIVE_JOB |
归档业务编号链 | — | (系统) |
关键卡点:REBATE_SUBMIT 未送审则 M5 带不出协议,是硬阻断点。
4.3 节点转移规则(示例)
PRODUCT_MATCH:全 EXACT/已确认 → 进CONTRACT_CREATE;有 MULTI/NONE →WAITING_USER建user_task。CONTRACT_APPROVE_WAIT:定时器半小时后查;未审完保持等待并重查,超阈值升级 OPS。M5_CREATE_ORDER报质管错 → 直接FAILED_MANUAL(不重试、不等质管),通知业务手工做。
五、幂等、追溯与证据设计
5.1 幂等三层
- 入池幂等:源文件哈希
source_file_hash+business_key,同批订单重复导入不生成多条未完成 job。 - 回调幂等:
job_id + node_code + attempt_no唯一键;重复回调按幂等键去重。 - 人工提交幂等:
task_id + submit_token,防止 H5 重复点击重复推进。 - 执行前检索:RPA 在返利/M5 建单前先检索已有单号,避免重复建单。
5.2 business_key 生成
business_key = source_type + "|" + supplier_name + "|" + order_date
(同供应商同日同来源 = 一条合同 = 一个 job)。加单场景父订单号记录在 order_item,重做逻辑待确认。
5.3 订单号贯穿追溯
| 位置 | 填什么 | 给谁看 |
|---|---|---|
| 电商平台 | 原始订单号 ZQD... | 源头 |
| 返利中心细单备注 | 订单号 | 物流部软件 |
| M5 订单备注 | 订单号 | 采购结算(打结算勾对账) |
归档链:订单号→合同编号→返利总单号→M5单号→收货通知单号,写入 job 各字段 + job_event。
5.4 证据存储
目录 对象存储/{job_id}/{node_code}/{attempt_no}/,含截图、日志、OCR JSON、合同PDF/合并PDF。job_artifact 记录元数据、哈希、可见角色、脱敏标记。
六、接口契约设计
所有写操作经状态中心,RPA/H5/企业微信不得直连数据库。鉴权:内部
Bearer,RPAX-Agent-Token,H5 企业微信身份换 JWT,写接口带X-Idempotency-Key。
6.1 统一节点回调契约
节点开始 POST /api/jobs/{jobId}/nodes/{nodeCode}/start
{ "attemptNo": 1, "executorPlatform": "YINGDAO", "operator": "agent-01", "startedAt": "2026-08-07T22:00:00+08:00" }
节点成功 POST /api/jobs/{jobId}/nodes/{nodeCode}/complete
{
"attemptNo": 1,
"executorRunId": "YD-RUN-001",
"endedAt": "2026-08-07T22:03:00+08:00",
"businessRefs": { "contractSystemNo": "HT...", "rebateOrderNo": null, "m5OrderNo": null, "receiptNoticeNo": null },
"summary": { "importedItemCount": 18, "failedItemCount": 0 },
"artifacts": [ { "artifactType": "SCREENSHOT", "storagePath": "/data/jobs/.../success.png" } ]
}
约束:节点要求业务编号却未回传,不得记 SUCCESS。
节点失败 POST /api/jobs/{jobId}/nodes/{nodeCode}/fail
{
"attemptNo": 1, "errorCode": "REBATE_DUP_CODE", "errorSummary": "存在重复的编码",
"retryable": false, "routeRole": "PURCHASER",
"artifacts": [ { "artifactType": "SCREENSHOT", "storagePath": "/data/jobs/.../error.png" } ]
}
6.2 错误码分层
| 层 | 前缀 | 处理 | 例 |
|---|---|---|---|
| 系统类 | SYS_ |
自动重试1-2次 | SYS_LOGIN_FAIL, SYS_PAGE_TIMEOUT |
| 业务类 | BIZ_ |
转对应角色 | BIZ_MATCH_NONE, REBATE_DUP_CODE, M5_QC_MISSING |
| 配置类 | CFG_ |
转OPS/管理员 | CFG_TEMPLATE_MISSING |
| 页面变化类 | UI_ |
转OPS | UI_ELEMENT_NOT_FOUND |
| 能力不满足 | CAP_ |
换平台/转OPS | CAP_PDF_MERGE_UNSUPPORTED |
特例:M5_QC_MISSING → 不重试,直接 FAILED_MANUAL。
6.3 待办与查询接口
GET /api/tasks/my?roleCode=PLANNER&status=openPOST /api/tasks/{taskId}/submit:动作CONFIRM_AND_CONTINUE/SELECT_CANDIDATE(对码一对多)/RETRY_CURRENT_NODE/TRANSFER_MANUAL/TERMINATE_JOB;返回即时回执receiptMessage/nextExpectedAction。POST /api/jobs/{jobId}/retry、POST /api/jobs/{jobId}/handoffGET /api/jobs/{jobId}:job基础+节点轨迹+待办+对码摘要+业务编号+证据列表。POST /api/wecom/callback:只接动作/身份,转标准动作后调内部逻辑。
6.4 对码专用接口(新增)
POST /api/jobs/{jobId}/match/run:触发对码引擎,返回命中率与未命中清单。GET /api/jobs/{jobId}/match/unmatched:未命中/一对多候选(供H5)。POST /api/match/{matchId}/confirm:人工确认选择候选或新增,回写mapping_config(累积迭代)。
七、对码引擎设计(核心难点)
7.1 匹配流程
order_item ──► 规范化(去空格/全半角/规格标准化) ──► 拼接 match_key(通用名+批准文号+规格+厂家)
│
├─ 精确命中 mapping_config(PRODUCT) ──► EXACT, 填 goods_id/code
├─ 模糊命中(规格/厂家容错规则) ─────► FUZZY(标记待复核)
├─ 同商品名多规格 ─────────────────► MULTI, 生成 candidates
└─ 无命中 ─────────────────────────► NONE, 建 user_task 转人工
7.2 规格标准化规则(针对痛点)
15×2 ↔ 30片(乘积展开)、干一瓶 ↔ ×1瓶、进口厂家英文名容错(大小写/标点/B.V. S.r.l.)。- 规则表化,可迭代补充;未覆盖的进 MULTI/NONE 人工。
7.3 迭代闭环
人工确认结果回写 mapping_config(新版本 version_no),下次自动命中。命中率统计写入 job_event,支撑阶段0/阶段3的准确率报告。
八、关键时序图
8.1 医药城端到端(正向)
定时器 → 状态中心: 触发建单(前一天医药城订单)
状态中心 → RPA: 启动 ORDER_FETCH
RPA → 医药城: 登录/导出Excel
RPA → 状态中心: complete(ORDER_FETCH, artifacts=Excel)
状态中心 → 对码引擎: PRODUCT_MATCH(解析order_item, 匹配)
对码引擎 → 状态中心: 全命中
状态中心 → RPA: 启动 CONTRACT_CREATE(need_pdf_upload=1)
RPA → 合同系统: 建单/选格式模板/改签约日/合并PDF上传/送审
RPA → 状态中心: complete(合同编号)
状态中心: 定时器等30分钟 → RPA: CONTRACT_APPROVE_WAIT 查审核
状态中心 → RPA: REBATE_HEADER(按合同号查总单填必填)
状态中心 → RPA: REBATE_IMPORT(细单模板,备注=订单号,特定补偿0)
状态中心 → RPA: REBATE_SUBMIT(送审) ⭐
状态中心 → RPA: M5_CREATE_ORDER(新增订单+细单,备注订单号,正常保管账,不保存)
RPA → 状态中心: complete(M5待保存)
状态中心 → 企业微信: 推送人工检查待办
计划员 → H5: 检查→保存→签订→生成收货通知单(阶段一人工)
状态中心: ARCHIVE_JOB 归档编号链
8.2 对码未命中(一对多)
对码引擎 → 状态中心: MATCH_MULTI(candidates)
状态中心 → user_task: 建待办(reason=MATCH_MULTI, candidates_json)
状态中心 → 企业微信: 推送"对码待确认"
计划员 → H5: 选择候选/新增
H5 → 状态中心: submit(SELECT_CANDIDATE)
状态中心 → mapping_config: 回写(累积迭代)
状态中心: job WAITING_USER → RUNNING, 继续 CONTRACT_CREATE
8.3 M5 质管资料缺失(转人工,不等质管)
RPA → M5: 新增订单保存时报错(供应商年报过期)
RPA → 状态中心: fail(M5_QC_MISSING, retryable=false)
状态中心: job → FAILED_MANUAL (不重试/不等质管)
状态中心 → 企业微信: 通知业务"此单转人工手工做"
业务: 次日质管维护后自行在M5手工完成
九、模块设计(状态中心内部)
| 模块 | 职责 | 关键接口/服务 |
|---|---|---|
| intake_service | 建单入池、哈希/业务键去重、Excel解析入 order_item | POST /api/jobs |
| workflow_service | 状态机流转、节点调度、转移规则 | node start/complete/fail |
| match_service | 对码引擎、规格标准化、候选生成、迭代回写 | match/run, match/confirm |
| retry_service | 系统类自动重试、超时扫描升级 | 定时任务 |
| task_service | 待办生成、角色路由、提交幂等、回执 | tasks/my, tasks/submit |
| executor_router | 节点→平台路由(FIXED/PREFERRED/FALLBACK) | 内部 |
| ocr_service | 线下单OCR适配、置信度阈值 | OCR_EXTRACT |
| artifact_service | 证据落盘、可见角色、脱敏 | 内部 |
| wecom_service | 企业微信消息、卡片更新、身份 | wecom/callback |
| audit/query | 事件流、整单查询、审计导出 | GET /api/jobs/{id} |
十、与早期草案的设计差异(供评审)
| # | 早期草案 | 本设计 | 原因 |
|---|---|---|---|
| 1 | job=一份合同文件的流程 | job=同供应商同日一批订单 | 订单驱动,合同是产物非入口 |
| 2 | 无 order_item/product_match 表 | 新增两表 | 对码是核心难点须落库 |
| 3 | 节点以合同/OCR为主线 | 节点订单驱动重排(ORDER_FETCH起) | 骨架方向纠正 |
| 4 | 送审在合同系统 | 关键送审 REBATE_SUBMIT | 卡点位置纠正 |
| 5 | 质管缺料转质管重试 | M5_QC_MISSING→FAILED_MANUAL | 业务明确否决重试 |
| 6 | 无自动化开关 | job.auto_save_enabled 分阶段 | 分阶段上线诉求 |
| 7 | 技术栈未定 | 确定 Python+FastAPI+MySQL,骨架已跑通 | 与对码/OCR同栈,开发快 |
| 8 | OCR为全流程核心 | OCR仅线下单分支 | 范围纠正 |
十一、待确认的设计输入(阻塞项)
- OCR信息安全:线上是否获批 / 本地部署预算 → 决定
OCR_EXTRACT节点部署方式。 - 返利中心重复订单完整规则 → 决定
REBATE_DUP_CODE是否可自动重建总单。 - 医药城订单状态过滤:只取"待收货"还是含其他 →
ORDER_FETCH过滤条件。 - 加单场景:父订单号关联与重做逻辑 →
parent_job_id/parent_order_no处理策略。 - 测试账号/对码表(Oracle连接串+表名列名)/供应商对照表/邮箱/计划员表到位时间 → 影响联调。
技术栈已确认:状态中心 = Python 3 + FastAPI + MySQL 8(骨架见
state_center/),不再作为阻塞项。
附录:联调顺序建议
- 状态中心单独跑通建单/状态流转/待办。
- 假OCR+假RPA回调打通全流程。
- 接真实对码引擎(复用阶段0成果)。
- 接真实企业微信+H5身份。
- 接真实RPA(先医药城单来源)。
- 返利中心/M5端到端联调。