AutoRpa/资料/采购主链RPA一期设计文档.md

31 KiB
Raw Permalink Blame History

采购主链 RPA 一期设计文档(技术设计 / HLD+LLD)

用途:把《采购主链RPA一期需求文档_三方会审版》(业务规则)和《采购主链RPA一期开发规划》(排期与阶段)落成可建库、可对接口、可分工的技术设计底稿。 关系说明:本文取代早期《一期开发任务清单_表结构_API草案.md》的架构与数据模型部分——该草案工程框架扎实,但建立在"合同文件入池→OCR→映射"的错误骨架上,缺订单驱动主线、产品对码、三来源分流。本文保留其状态机/幂等/证据/待办思想,按订单驱动重排实体与节点。 术语:状态中心 = 我们自研的编排与审计中台;执行器 = RPA 平台(影刀/来也等);对码 = 电商药品匹配到 M5 货品ID/编码。


一、设计目标与非目标

1.1 设计目标

  1. 订单驱动:以电商订单(医药城/小药药)导出为入口,线下手工单为 OCR 分支,贯穿到 M5 录入待保存。
  2. 分层解耦:业务规则、状态机、审计、对码口径掌握在状态中心;RPA 平台仅做页面执行,可替换。
  3. 对码可迭代:对码表版本化、累积迭代,未命中一对多转人工,命中率可统计。
  4. 不可逆保护:一期末端"保存/签订/生成收货通知单"由人工执行,自动化做成配置开关。
  5. 可追溯:订单号→合同号→返利总单号→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 幂等三层

  1. 入池幂等:源文件哈希 source_file_hash + business_key,同批订单重复导入不生成多条未完成 job。
  2. 回调幂等:job_id + node_code + attempt_no 唯一键;重复回调按幂等键去重。
  3. 人工提交幂等:task_id + submit_token,防止 H5 重复点击重复推进。
  4. 执行前检索: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,RPA X-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=open
  • POST /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}/handoff
  • GET /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仅线下单分支 范围纠正

十一、待确认的设计输入(阻塞项)

  1. OCR信息安全:线上是否获批 / 本地部署预算 → 决定 OCR_EXTRACT 节点部署方式。
  2. 返利中心重复订单完整规则 → 决定 REBATE_DUP_CODE 是否可自动重建总单。
  3. 医药城订单状态过滤:只取"待收货"还是含其他 → ORDER_FETCH 过滤条件。
  4. 加单场景:父订单号关联与重做逻辑 → parent_job_id/parent_order_no 处理策略。
  5. 测试账号/对码表(Oracle连接串+表名列名)/供应商对照表/邮箱/计划员表到位时间 → 影响联调。

技术栈已确认:状态中心 = Python 3 + FastAPI + MySQL 8(骨架见 state_center/),不再作为阻塞项。


附录:联调顺序建议

  1. 状态中心单独跑通建单/状态流转/待办。
  2. 假OCR+假RPA回调打通全流程。
  3. 接真实对码引擎(复用阶段0成果)。
  4. 接真实企业微信+H5身份。
  5. 接真实RPA(先医药城单来源)。
  6. 返利中心/M5端到端联调。