|
|
||
|---|---|---|
| .. | ||
| app | ||
| sql | ||
| tests | ||
| README.md | ||
| requirements.txt | ||
| 给Trae的开发交接.md | ||
| 给艺赛旗的开发交接.md | ||
README.md
状态中心(采购主链RPA一期 · 第1步骨架)
Python + FastAPI + MySQL。库里只存流程状态 + 对码累积别名 + 证据;对码货品主数据在 Oracle,直连查(不复制)。
目录
state_center/
├── sql/schema.sql 建库脚本(job 那套 + mapping_config + 证据)
├── app/
│ ├── config.py 配置(MySQL/Oracle 连接、表名列名,从环境变量读)
│ ├── db.py MySQL 访问(pymysql)
│ ├── match_engine.py 对码引擎(两级命中 + 规格标准化 + Oracle 取数)
│ └── main.py FastAPI:建单 + 回调(含幂等/乱序拒绝)+ 合同审批等待 + 整单查询
├── tests/test_match_engine.py 对码单测(不依赖 DB/Oracle)
└── requirements.txt
起步
以下命令在 macOS/Linux 的终端执行;Windows PowerShell 只需要把激活命令换成下一段对应写法,接口和浏览器端完全相同。
python -m pip install -r state_center/requirements.txt
# 1) 建库(先起个 MySQL 8)
mysql -uroot -p < state_center/sql/schema.sql
# 2) 配连接(默认 root/root@127.0.0.1)
export MYSQL_DSN="mysql+pymysql://root:root@127.0.0.1:3306/rpa_center?charset=utf8mb4"
# 3) 起服务
uvicorn state_center.app.main:app --reload --port 8000
# 打开 http://127.0.0.1:8000/docs 有全部接口可点
Windows PowerShell:
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r state_center\requirements.txt
$env:DEV_MODE = "true"
$env:MYSQL_DSN = "mysql+pymysql://root:root@127.0.0.1:3306/rpa_center?charset=utf8mb4"
python -m uvicorn state_center.app.main:app --reload --port 8000
浏览器端另开一个终端:
cd state_center_web
npm install
npm run dev
然后打开 http://localhost:5173/。Windows 和 macOS 均使用标准浏览器访问,不需要 Electron 或系统专用脚本。DEV_MODE=true 只用于本地样例货品和节点回调模拟,生产环境不要开启。
本地样例联调顺序:建单 → 写入订单明细(POST /api/jobs/{job_id}/items) → 开发接口装载样例货品(POST /api/dev/match/load-fixture-goods) → 运行对码。真实环境把样例接口替换为 Oracle 货品装载即可。
验证第1步(不接真实系统,假回调走全链)
# 建单
curl -X POST http://127.0.0.1:8000/api/jobs -H 'Content-Type: application/json' \
-d '{"source_type":"YYC","order_date":"2026-08-04","supplier_name":"重庆亿昊药业"}'
# 拿到 job_id 后,模拟艺赛旗回调(带 token):
curl -X POST http://127.0.0.1:8000/api/jobs/<JOB_ID>/nodes/ORDER_FETCH/start \
-H 'X-Agent-Token: dev-agent-token' -H 'Content-Type: application/json' -d '{"attemptNo":1}'
curl -X POST http://127.0.0.1:8000/api/jobs/<JOB_ID>/nodes/ORDER_FETCH/complete \
-H 'X-Agent-Token: dev-agent-token' -H 'Content-Type: application/json' \
-d '{"attemptNo":1,"summary":{"orderCount":12}}'
# 反复 start/complete 走完各节点,最后 GET 看整单轨迹:
curl http://127.0.0.1:8000/api/jobs/<JOB_ID>
节点编排(13 = 11 主 + 2 条件)
主链 11 步(每单都走):
ORDER_FETCH → PRODUCT_MATCH → CONTRACT_CREATE → CONTRACT_APPROVE_WAIT → REBATE_HEADER → REBATE_IMPORT → REBATE_SUBMIT → M5_CREATE_ORDER → M5_MANUAL_CONFIRM → M5_RECEIPT_NOTICE → ARCHIVE_JOB
2 个条件节点(不满足条件自动跳过,不算漏节点):
| 条件节点 | 何时执行 | 何时跳过 |
|---|---|---|
OCR_EXTRACT |
线下手工单(source_type=OFFLINE) | 线上单(医药城/小药药) |
SOURCE_ADD |
对码判定缺货源(needs_source_add=1) | 货源齐全 |
转移不是"数组下一个",是 main.py 里的显式转移表 TRANSITIONS + 条件跳过。
每个节点标了执行方 NODE_EXECUTOR:STATE_CENTER(对码,内部算)/ RPA(艺赛旗点页面)/ HUMAN(一期人工)。
所以文档里"11 个节点"和代码里"13 个"是同一套:11 主 + 2 条件 = 13。
关键设计点(会审确认)
- 回调幂等:同节点同 attempt 重复 complete 不会二次推进(靠 job_node 状态 +
callback_idempotency_key)。 - 乱序拒绝:回调节点必须是 job 当前节点,否则 409(避免网络重发把状态推乱)。
- 合同审批等待(CONTRACT_APPROVE_WAIT)不连续轮询:进
WAITING_EXTERNAL态,每CONTRACT_WAIT_INTERVAL_MIN(默认 30 分)由调度器扫GET /api/scheduler/due-contract-checks触发 RPA 查一次,结果回POST .../CONTRACT_APPROVE_WAIT/check-result;查满CONTRACT_WAIT_MAX_CHECKS(默认 8 次)还没过就升级 OPS。 - 对码状态只有 4 种:EXACT / ALIAS / MULTI / NONE(无 FUZZY;ALIAS=累积别名命中,非当次人工)。
- PRODUCT_MATCH 是状态中心自己算的节点(executor=STATE_CENTER),不走 RPA 三回调,靠
POST /api/jobs/{id}/match/run推进:跑order_item→ 落product_match→ MULTI/NONE 建 待办转 WAITING_USER,全命中自动往下走;POST /api/match/{id}/confirm人工确认后沉淀mapping_config别名并推进。前置:order_item已落库 +POST /api/match/reload-goods从 Oracle 装载过货品主数据(这两样是 Trae 任务2/3 的接真数据部分)。 - SOURCE_ADD 触发源明确:对码拿到
goods_id后,状态中心按历史supplier_name + goods_id判断是否已有货源;没历史则写product_match.source_add_needed=1、同步job.needs_source_add=1并创建一条聚合的SOURCE_ADD待办。 - 对码重跑幂等:
product_match以(job_id, order_item_id)为当前态唯一键,match/run重跑时 upsert 结果并重建 OPEN 待办,不重复造结果/待办。 - alias 当前态唯一:
match/confirm会先失效同一config_key的旧 active 记录,再插入新 active; 装载 alias 时按updated_at/id固定顺序读取,历史脏数据也有确定结果。
对码接 Oracle(拿到连接串后)
pip install oracledb- 在
config.py/ 环境变量填ORACLE_DSN/USER/PASSWORD,把ORACLE_GOODS_TABLE和各列名改成真实的。 - 开工时
load_goods_from_oracle(settings)拉全量 →MatchEngine.load_goods(...),之后内存匹配。
对码单测
python3 state_center/tests/test_match_engine.py # 6/6 passed