unis_sip/docx/mcp-data-tools-plan.md

172 KiB
Raw Blame History

仓储 / 采购 / 财务 / 项目 数据类 MCP 工具方案(v15 · 已实现并端到端验证)

状态:已实现(13 个新工具,共 15 个),编译通过 + 认证态端到端 34/34 用例通过 适用范围:ruoyi-sip 模块 com.ruoyi.sip.llm.tools 下的只读查询工具 关联文档:prompt.md(MCP Server 原始规格)、mcp-tools-index-ddl.sql(索引核对与补建脚本)

最终方案速览(TL;DR)

项 结论
工具数 13 个 = 标识符点查 3 + 聚合 3 + 列表/范围 5 + 扩展 2(project_list、cross_domain_aggregate)
覆盖 仓储 / 采购 / 财务 / 项目(含 POC、进度、报价) 四域 + manage 域合同与合同明细 + 审批待办与已办,含基础列表能力、签收、撤回历史、时间维度、计收、备货、主数据批量翻译(含仓库与己方公司主体)、受限跨域透视(客户×产品×月 等)、账龄分桶、财务历史时点余额
性能 过滤键实测对齐索引;游标分页不重不漏;聚合默认 mode=SUMMARY 一次算完;类型对齐防索引退化;查询超时 + 限流;RAG 路由控制 schema token
统计维度 产品 / 仓库 / 订单 / 采购单 / 供应商 / 合作伙伴 / 代表处 / 项目 / 状态 / 时间(月、季)/ 账龄分桶 / 无维度(OWNER 已移除,见 15.2)
索引 必加 2(P0)+ 建议 3(P1,含覆盖索引)+ 条件加 23(P2 编号 26 条,其中 3 条已作废,按表规模触发)
不改动 不改数据库字段、不改任何业务写入逻辑、不改现有 2 个工具与 MCP 框架(McpService/McpToolRegistry/ToolInitializer/McpController)
明确不做 写操作、财务附件的文件内容(仅元数据)、财务运营报表物化表(改用实时聚合 + 时点重算)、审计日志、报表导出文件、任意 SQL / 无白名单的任意维度
业务口径 已配置化落地(在库口径、价格含税口径),业务跑一次校验 SQL 后改配置即可,不阻塞编码(见 16.3)
工程项 索引 DDL 执行方案(16.4)、时间区间策略(16.5)、从库路由(16.6)、RAG 工具路由(16.7,13 个工具下为必须)

一、目标与范围

1.1 目标

在现有 MCP Server(已有 project_order_info、product_info 两个工具)基础上,新增覆盖仓储、采购、财务三个数据域的只读查询工具,满足:

  1. 覆盖高频业务问题,且一次调用尽量拿全,减少 Agent 轮次;
  2. 单次调用快:过滤键全部命中索引,不产生 N+1 与全表扫描;
  3. 数据不缺失:范围类查询支持游标分页,由 Agent 自行翻页直至取完;
  4. 不越权:与页面入口的数据权限、菜单权限对齐;
  5. 不改数据库字段、不改业务写入逻辑:仅新增只读 SQL 与索引。

1.2 范围外(明确不做)

  • 任何写操作(新增/修改/删除/审批/撤回/红冲);
  • 基于 oms_finance_operate_report 的工具(原因见 2.1);
  • 采购/财务审批待办与已办(bu_todo 相关,属流程域)、财务附件内容;
  • 改造现有两个工具的行为。

二、前置验证结论(实测,非推断)

验证环境:oms_test(192.168.124.202:3307),MySQL 8.0.37。

2.1 财务物化表不可用 —— 已否决原设计

项 实测结论
刷新入口 全仓仅 GET /test,且带 @Anonymous(免鉴权),属临时手工触发口
定时任务 无(全仓无 @Scheduled / quartz job 引用 statisticsReport())
刷新方式 增量水位制:以 maxDataDate() 为 watermark(OmsFinanceOperateReportServiceImpl#statisticsReport)
数据现状 oms_finance_operate_report 与 oms_finance_operate_report_detail 均为 0 行

结论:该表是人工触发、且漏触发会永久偏差的物化表,不能作为 MCP 财务数据源。财务工具一律走实时单据表聚合。

2.2 数据量级(oms_test,仅作参考)

表 行数 表 行数
oms_inventory_info 64,823(精确) oms_inventory_delivery 756
oms_inventory_delivery_detail 49,015(估) oms_inventory_outer_detail 750
oms_purchase_order_item 951 oms_inventory_outer 734
oms_purchase_order 941 oms_receivable_bill 627
oms_inventory_inner 580 oms_payable_bill 583
oms_stock_info 526 oms_payment_bill 64
oms_ticket_bill 21 oms_warehouse_info 14
oms_receipt_bill 1 oms_invoice_bill 0

结论:按百万级保守设计,约束集中在 oms_inventory_info 与 oms_inventory_delivery_detail 两张表。

2.3 oms_inventory_info 关键事实

  • 主键 id 为 int(非 bigint),游标可安全编码;
  • inventory_status 取值实测仅 '0'(在库 15,532) / '1'(出库 49,291);
  • tax_rate 实测存在 NULL,未税换算需兜底为 0;
  • 表结构含 product_code / product_sn / inner_code / outer_code / warehouse_id / inner_price / outer_price / order_code / payable_bill_code。

2.4 列存在性验证(用于索引 DDL 可行性)

表 关键列存在情况
oms_inventory_outer 有 outer_code、order_code;无 warehouse_id
oms_inventory_outer_detail 有 outer_code、warehouse_id
oms_inventory_inner 有 inner_code、order_code、purchase_no
oms_inventory_delivery 有 outer_code;无 order_code、无 product_code(均来自 join)
oms_inventory_delivery_detail 有 delivery_id、product_sn
oms_purchase_order 有 purchase_no、vendor_id、status、purchase_date
oms_purchase_order_item 有 purchase_id、product_code、inner_quantity
oms_purchase_order_map 有 order_id、purchase_id、product_code、bind_num

重要:oms_inventory_delivery.order_code 不是本表列,不能对其建索引;按订单号查发货单必须经 oms_inventory_outer.order_code 关联(该列已有索引)。

2.5 现有索引实测(与本方案相关的部分)

表 现有索引 是否满足需求
oms_inventory_info product_sn(UNIQUE)、inner_code、outer_code、product_code ✅ 满足;warehouse_id 无索引
oms_inventory_inner inner_code(UNIQUE) ⚠️ order_code 无索引
oms_inventory_delivery outer_code ✅
oms_inventory_delivery_detail delivery_id、product_sn ✅
oms_inventory_outer 仅 order_code,outer_code 无索引 ❌ 需新增
oms_inventory_outer_detail 仅主键 ❌ 需新增
oms_receivable_bill receivable_bill_code(UNIQUE)、order_code、inventory_code ✅
oms_payable_bill payable_bill_code(UNIQUE)、order_code、inventory_code ✅
oms_receipt_bill / oms_payment_bill 单号 UNIQUE ✅
oms_invoice_bill / oms_ticket_bill 单号 UNIQUE ✅
收/付/开票/收票 计划与明细表 receivable_bill_id、payable_bill_id、*_plan_id、*_bill_code 均有索引 ✅
oms_purchase_order 仅 purchase_no(UNIQUE) ⚠️ vendor_id/status/时间无索引
oms_purchase_order_item purchase_id ✅
oms_purchase_order_map 仅主键 ⚠️ order_id/purchase_id 无索引
oms_stock_info 仅主键 ⚠️ 未纳入本方案
全部表 无任何时间列索引 ❌ 时间范围不作主过滤键

补充:代码中 oms_receivable_write_off_detail / oms_payable_write_off_detail 两个 domain 实际不参与查询(库中无此表),核销真实链路为 receipt_detail.write_off_id / payment_detail.write_off_id;这两列无索引,因此工具不支持按核销单号反查(已规避)。


三、设计原则

  1. 面向问题,而非面向表:一个工具回答一类业务问题,避免"一表一工具"导致工具数膨胀、Agent 选错率与 schema token 上升。
  2. 点查不翻页、范围必翻页:入参为单号/SN 精确值时结果有界,一次返回;入参为条件/聚合时强制游标分页。
  3. 过滤键必须命中索引:每个工具的必填入口键都对应一个实测存在的索引;无索引的维度不开放为入口。
  4. 消除 N+1:一律"先查主表取键集合 → in (...) 批量查子表 → 内存分组"。
  5. 权限前置:handle 第一行做菜单权限校验,再按页面口径回填行级权限过滤。
  6. 只读:不开启事务、不写库、不触发业务副作用。
  7. 口径显式:聚合口径、字段含义写入 metadata,避免 Agent 把口径差异误判为"数据缺失"。

3.8 与现有 MCP 工具的一致性基线(实现前必须先明确)

本方案不是另起一套,而是完全沿用现有工具的范式。现有两个工具:ProjectOrderInfoToolProvider.java、ProductInfoToolProvider.java,公共基类 AbstractMcpToolProvider.java。

维度 现有实现 本方案
注册方式 @Component + extends AbstractMcpToolProvider;ToolInitializer.java 启动时扫描 List<McpToolProvider> 自动注册 一致:只新增工具类,不改 ToolInitializer / McpToolRegistry / McpService / McpController
工具命名 snake_case(project_order_info、product_info) 一致
工具描述 getToolDescription() 返回中文一句话 一致;分页工具在描述末尾追加 Agent 翻页指令(见 5.6)
入参 Schema objectSchema(properties, required...) + stringProperty("中文说明"),additionalProperties=false 一致;基类扩展 integerProperty / arrayProperty / enumProperty(中文说明)
返回结构 response(metadata, query, data);data.total + data.items;items 字段为英文驼峰 一致;仅新增 data.page_info
字段注释 metadata.item_fields 中文键值对(见 buildItemFieldMetadata()) 一致;每个工具必须提供完整字段字典(附录 A)
字典翻译 DictUtils.getDictLabel(dictType, code) 一致;枚举类字段用 XxxEnum#getValue(),两类来源在附录 A.13 区分
批量加载 先查主表取编码集合 → in (...) 批量 → groupingBy 组装(ProjectOrderInfoToolProvider#loadShipmentSummaryMap 即此写法) 一致
日期格式 DateUtil.format(value, "yyyy-MM-dd") / DateUtils.YYYY_MM_DD_HH_MM_SS 一致
空值处理 固定字段集全量输出(空值输出 null 或空串) 一致(不采用"空值字段不输出"的裁剪方式,避免模型误判字段缺失)
错误 throw new RuntimeException("英文消息"),由 McpController#toMcpError 映射 一致,不新增错误类型

3.9 对现有写法的有意偏离(共 4 处,均有理由)

# 偏离 理由
1 新增分页(data.page_info + 游标) 现有工具无分页能力,project_order_info 按时间范围查询可能返回超大结果集。data.total 键予以保留以兼容现有约定:默认 null,include_total=true 时填真实值
2 新增菜单/行级权限校验 现有工具依赖 Service 层 @DataScope;但仓储与采购的行级权限在 Controller 层(IInventoryAuthService),直调 Service 会越权,必须显式对齐
3 不使用动态列 现有 project_order_info 用 softwareCode1/2/3… + dynamic_field_rules 表达明细行,对模型不友好;新工具统一用嵌套数组(items[].subItems[])
4 新增 metadata.dict_fields / aggregation_rule 用于向 Agent 声明字典取值来源与聚合口径,减少"口径误解被当成数据缺失"

四、工具清单(13 个,最终版)

划分原则:① 标识符点查(有界,一次拿全,不分页)→ ② 聚合(默认 mode=SUMMARY 一次算完)→ ③ 列表/范围查询(游标分页)。 版本演进:v6 拟定的 purchase_order_detail、finance_bill_detail 已合并进 purchase_list / finance_list(用 code_list + include_detail=true 表达点查),工具数由 11 收敛为 9;v8 纳入主数据工具 master_data_list(warehouse_list 扩到 7 个 entity)→ 10 个;v9 纳入 project_list 与 cross_domain_aggregate → 12 个;v13 纳入 approval_list(审批待办/已办)→ 13 个。

4.1 A 类:标识符点查(不分页)3 个

# 工具名 必填标识符 返回 数据链路 索引支撑
1 inventory_sn_trace product_sn_list(≤50) / inner_code_list(≤20) / outer_code_list(≤20) 三选一 SN 明细:库存状态、入库价/出库价(含税口径待确认,见 15.8)、税率、所属入库单/出库单/合同号、仓库 oms_inventory_info 直查 product_sn(UK)、inner_code、outer_code ✅
2 inventory_flow outer_code 或 order_code 6 分组:inner、outer、outerDetails、deliveries、snDetails、stock(新增·备货状态) 入库单 + 出库单(±明细) + 发货单(±明细) + SN + oms_stock_info 依赖 P0-1/P0-2;stock 按 order_code(小表)
3 finance_order_position order_code 14 分组:receivable、receiptPlans、invoicePlans、receipts、receiptWriteOffs、invoices、payable、paymentPlans、ticketPlans、payments、tickets、paymentWriteOffs、ticketWriteOffs、charge(新增·计收) 应收/应付 + 计划/明细/核销 + 收付票单 + oms_finance_charge 各单号 UNIQUE、order_code、oms_finance_charge.order_code(UK) ✅

子列表(如某订单的收款单条数很多)超单页上限时,返回该子列表的 sub_cursor 局部游标,仅供该子列表翻页。

4.2 B 类:聚合(mode=SUMMARY 默认,LIST 可选)3 个

# 工具名 group_by 可选值 度量 数据来源
4 inventory_stock_aggregate PRODUCT / WAREHOUSE / PRODUCT_WAREHOUSE / STATUS / TIME_MONTH / TIME_QUARTER / NONE in_stock_qty、out_stock_qty、inner_amount、outer_amount oms_inventory_info
5 purchase_arrival_aggregate ORDER(采购单号) / VENDOR / PRODUCT / STATUS / TIME_MONTH / NONE purchase_qty、inner_qty、pending_qty、arrival_rate、arrival_delay_days、amount_total、tax_total oms_purchase_order ⋈ oms_purchase_order_item
6 finance_balance_aggregate ORDER / PARTNER / STATUS / TIME_MONTH / NONE 5 流余额(应收/收款/开票/应付/付款/收票)+ overdue_days oms_receivable_bill / oms_payable_bill

分页粒度选择理由:oms_inventory_info 索引顺序为 (product_code, id),以产品为页边界可沿用同一条索引;按"产品+仓库"组合分页需跨索引排序,成本更高。 重要:聚合工具必须同时具备"汇总模式"与"明细模式",否则全局统计只能靠 Agent 翻页累加,会退化为多次全表扫描且可能被 max_pages 截断。详见 第十三章。

4.3 C 类:列表 / 范围查询(游标分页)5 个

# 工具名 entity 可选值 过滤维度 明细
7 warehouse_list INNER / OUTER / DELIVERY / ORDER_DELIVERY(manage 域发货单,含签收) / STOCK / SN / RECALL(撤回历史) 单号、状态、时间范围、仓库、产品、合同号 include_detail=true 返回明细(来源见下表)
8 purchase_list ORDER / ITEM / HISTORY / ORDER_BIND 单号、状态、审批/确认状态、时间范围、供应商、产品 include_detail=true 返回明细行
9 finance_list RECEIVABLE / PAYABLE / RECEIPT / PAYMENT / INVOICE / TICKET / CHARGE / ATTACHMENT(v13 新增,仅元数据) 单号、状态、审批状态、时间范围、合作伙伴 include_detail=true 返回计划/明细/核销
10 master_data_list PARTNER / CUSTOMER / AGENT / VENDOR / PRODUCT / USER / WAREHOUSE(v10 新增) / COMPANY(v10 新增) code_list(批量,≤200)、名称模糊、类型/状态 无明细(主数据)
11 approval_list(v13 新增) TODO(待办)/ DONE(已办) 审批人(默认当前登录人)、process_key、业务主键 business_key、时间范围 无明细(TODO 返回当前节点;DONE 返回审批意见与结果)

approval_list 的价值:回答"我还有哪些单要审""现在卡在谁那儿""为什么被驳回""审批耗了多久"。数据来自 bu_todo(61) 与 bu_todo_completed(5,876) 两张业务表,不需要读 Flowable 的 act_*(实测两表已冗余 business_key/process_key/task_name/approve_user_name/apply_time/approve_opinion/approve_status)。已覆盖流程:order_approve_online/offline、purchase_order_online、finance_payment、fianance_ticket、order_reback、outer_reback。

master_data_list 的作用:解决"Agent 拿到 partner_code/vendor_code/product_code 却查不到名称"的关联实体缺口(原 purchase_list(entity=VENDOR) 已并入本工具的 VENDOR,避免重复)。

列表工具的护栏:

  1. entity=SN 走大表(oms_inventory_info 6.5 万行)→ 必须给出 product_sn_list / inner_code_list / outer_code_list / product_code_list 之一,否则返回 INVALID_PARAMS;
  2. 其余 entity 所在表当前均 < 1000 行 → 允许状态/时间/合作伙伴维度直接过滤(依据 14.4 的表规模分级规则),并在 metadata 注明"该表当前规模小,增长后需补索引"(见 P2);
  3. include_detail 默认 false(列表只要表头,省 token);需要完整明细树时置 true;
  4. 单据点查用 code_list + include_detail=true。

各 entity 的明细来源(v10 修正:入库明细以实测数据为准)

entity 明细子表 说明
INNER oms_inventory_info(按 inner_code) 实测:入库明细实际落在 SN 表——oms_inventory_info 中 579/580 张入库单有 SN 明细(样例 R-20250917001 有 500 条 SN)。oms_inventory_inner_detail 表虽定义存在,但实测仅 1 行(未启用),不采用
OUTER oms_inventory_outer_detail 按仓库拆分的出库数量
DELIVERY oms_inventory_delivery_detail 仓储域发货 SN 明细
ORDER_DELIVERY delivery_list manage 域发货 SN 明细(delivery_id + serial_number),必须过滤 deleted_at is null。另:本 entity 的 orderId 指向 manage 域合同表 order_info(实测 355/355 全部命中),必须 join order_info 才能输出合同编号/客户/代理商(且需 trim(),见 15.14)
STOCK / SN / RECALL 无 —

另有 oms_inventory_inner_maintenance(维保入库,当前 0 行)不纳入本轮范围。

⚠️ 口径提醒(v10 实测发现):oms_inventory_info.order_code 在 SN 未出库时为空——实测 64,823 行中 15,532 行为空,且该数量恰好等于在库数量(inventory_status='0')。因此不能用 inventory_info.order_code 反查"在库货物属于哪个订单",必须经 inner_code → oms_inventory_inner.order_code。

4.4 D 类:扩展能力(v9 新增)2 个

# 工具名 用途 详见
12 project_list 项目 / 项目产品 / 项目进度 / POC / 报价单 / manage 域合同与合同明细(v11 新增)(entity 参数化) 16.1(含字段字典与索引)
13 cross_domain_aggregate 受限跨域透视:"客户 × 产品 × 月"等组合分析(维度/度量白名单 + 单链路约束) 16.2(含白名单、链路与护栏)

这两个工具的字段字典直接写在第十六章对应小节(避免与附录 A 重复)。附录 A 收录其余 11 个工具的字段字典(含 v13 新增的 approval_list,见 A.15)。

4.5 统一响应契约(对齐现有工具,仅新增 page_info)

完全沿用现有 AbstractMcpToolProvider#response(metadata, query, data);字段注释一律放 metadata.item_fields(与现有工具写法一致):

{
  "metadata": {
    "tool": "...",                                  // 工具名(与现有工具一致)
    "description": "...",                           // 中文说明
    "query_fields": { "<入参>": "<中文注释>" },      // 入参字段注释
    "data_fields": { "total": "...", "items": "...", "page_info": "..." },
    "item_fields": { "<返回字段>": "<中文注释>" },   // ★ 字段注释(附录 A 为权威来源)
    "dict_fields": { "<返回字段>": "<字典类型或枚举类>" },
    "aggregation_rule": { ... }                     // 仅聚合类工具提供
  },
  "query": { ...规范化后的入参回显... },
  "data":  { "total": null, "items": [ ... ], "page_info": { ... } }
}
  • data.total:保留现有约定;默认 null(不额外 count),include_total=true 时填真实值,超 count_cap 时并置 total_count_capped=true;
  • metadata.item_fields 必填:每个工具都要有完整中文字段注释(对齐现有 buildItemFieldMetadata() 的做法,内容取附录 A);
  • metadata.dict_fields:声明哪些字段做了翻译、来源是字典表还是枚举类,便于 Agent 理解取值;
  • 错误契约:沿用 McpErrorUtils 的 INVALID_PARAMS / METHOD_NOT_FOUND / AUTH_ERROR / INTERNAL_ERROR,不新增错误码。

五、分页协议(核心)

5.1 为什么用游标而非页码

方案 问题
只返回前 N 条 + truncated 标记 Agent 无法继续取,数据缺失(且无补救手段)
OFFSET n LIMIT m 页码分页 翻页期间数据增删会导致漏行或重复行;深分页性能随 offset 线性退化
游标(keyset)分页 按不可变排序键推进,不重不漏,深分页性能恒定 ✅

5.2 入参(通用)

参数 默认 说明
page_size 20 聚合类上限 200,明细类上限 100
cursor — 上轮返回的 next_cursor,首轮不传;与 page 互斥
page — 兼容用页码(内部转 OFFSET,仅在数据不变时稳定,不推荐)
include_total false 置 true 时执行 count,受 count_cap=50000 限制

5.3 返回(data.page_info)

{
  "returned": 20,
  "page_size": 20,
  "has_more": true,
  "next_cursor": "<opaque>",
  "sort_by": "outer_code,id",
  "page_no": 3,
  "total_count": null,
  "total_count_capped": false,
  "truncated_by_bytes": false
}

5.4 游标编码(无状态、可校验)

base64url({ "v":1, "t":"tool_name", "k":[排序键值...], "f":"filterHash", "p":pageNo })

  • k:最后一行/组的排序键值(游标推进依据);
  • f:入参过滤条件 + 当前用户权限指纹(授权仓库集合 / 供应商集合)的哈希。校验不一致直接报错"cursor 与当前过滤条件或权限不匹配,请从第一页重新开始",防止串用游标导致漏数;
  • p:页码,用于 max_pages 保护(聚合默认 20 页、明细默认 50 页,超限提示收窄条件);
  • 每页以 limit+1 探测 has_more,不额外执行 count;
  • 单页响应超 ~200KB 时自动下调 page_size 并置 truncated_by_bytes=true(换页而非丢数据)。

5.5 各工具固定排序键

工具 排序键(sort_by)
inventory_sn_trace 随入参:product_sn,id / inner_code,id / outer_code,id
inventory_stock_aggregate 随 group_by:product_code / warehouse_id,product_code / inventory_status,product_code / time_bucket,product_code
purchase_arrival_aggregate purchase_no / vendor_id,purchase_no / product_code,purchase_no / time_bucket,purchase_no
finance_balance_aggregate order_code / partner_code,order_code / time_bucket,order_code
warehouse_list inner_code,id(INNER) / outer_code,id(OUTER) / outer_code,id(DELIVERY) / delivery_code,id(ORDER_DELIVERY) / order_code,id(STOCK) / product_sn,id(SN) / order_code,version_code(RECALL)
purchase_list purchase_no,id(ORDER/ITEM) / order_id,purchase_id(ORDER_BIND) / purchase_no,id(HISTORY)
finance_list <bill_code>,id / order_code,id(CHARGE)
master_data_list 各自主键:partner_code / customer_code / agent_code / vendor_code / product_code / user_id / warehouse_code / company_code
approval_list(v13) apply_time desc, id(TODO)/ approve_time desc, id(DONE)

时间维度统一以 time_bucket 作为分组别名(TIME_MONTH → 2026-01,TIME_QUARTER → 2026Q1),实现上用区间下推而非列函数(见 14.4 技术注意)。

SQL 形态(MySQL 8 支持行构造器,但为索引友好改用显式比较):

-- 单值过滤:纯索引游标(推荐路径)
where outer_code = :oc and id > :lastId order by id limit :n+1;
-- 多值 IN:跨多段索引区间,会产生 filesort(可接受代价,见 5.6)
where product_code in (:list) order by product_code, id limit :n+1;

5.6 Agent 侧循环指令(写入 description)

分页查询工具。若返回 page_info.has_more 为 true,必须携带 page_info.next_cursor 再次调用本工具,重复直到 has_more 为 false,否则结果不完整。不要用 page 参数替代 cursor。

5.7 已知局限(诚实声明)

  1. 多值 IN + 游标会产生 filesort:故明细类 page_size ≤ 100、IN 长度 ≤ 50,并优先引导 Agent 使用单号/SN 精确查询。
  2. 不提供快照一致性:keyset 保证不重不漏,但翻页期间新增数据可能出现在后续页。严格快照需 and update_time <= :as_of,而全部表的时间列无索引,代价过高,本版不做。
  3. 聚合跨页口径:聚合结果随上游数据变化,page_info 中的 page_no 仅代表游标推进次序,不代表固定快照。

补充:全局统计不应由 Agent 翻页累加(会退化为多次全表扫描且可能被 max_pages 截断),应使用聚合工具的 mode=SUMMARY 一次算完,详见 第十三章。


六、需要新增的索引

v15 实测更新:在 oms_test 核对 information_schema 后发现 P0-1 / P0-2 / P1-1 / P1-2 四枚索引已存在(此前巡检查漏),无需再建;仅 P1-3 覆盖索引缺失,已在 oms_test 执行并实测:建索引 1.3s、按产品聚合 141.8ms → 66.6ms、EXPLAIN 显示 Using index(免回表)。生产库执行前请用 mcp-tools-index-ddl.sql 的核对语句确认。

6.1 必加(P0)

序号 表 索引名 列 服务的工具 理由
P0-1 oms_inventory_outer idx_outer_code outer_code inventory_flow 出库单号是该工具主入口,当前仅 order_code 有索引,按单号查会全表扫描
P0-2 oms_inventory_outer_detail idx_outer_code outer_code inventory_flow 明细批量查询依赖它;当前仅主键,不补索引则新增的 listByOuterCodeList 反而成为性能陷阱
ALTER TABLE oms_inventory_outer
  ADD INDEX idx_outer_code (outer_code), ALGORITHM=INPLACE, LOCK=NONE;
ALTER TABLE oms_inventory_outer_detail
  ADD INDEX idx_outer_code (outer_code), ALGORITHM=INPLACE, LOCK=NONE;

6.2 建议加(P1,防数据增长后退化)

序号 表 索引名 列 理由
P1-1 oms_inventory_inner idx_order_code order_code inventory_flow 以 order_code 为入口时走 selectOmsInventoryInnerByOrderCodeList;该列当前无索引
P1-2 oms_purchase_order idx_vendor_id vendor_id 采购聚合/明细若要按供应商收窄;当前仅 purchase_no(UK)
P1-3 oms_inventory_info idx_pc_status_amt (product_code, inventory_status, inner_price, outer_price) 覆盖索引:聚合可完全走索引、免回表(实测 count 14.2ms → 含 sum 需回表 36.7ms)。仅对这张线性增长的大表加,小表不值得(应收表 627 行全表聚合仅 21.3ms)。需权衡索引体积与入库写入放大
-- P1-3 覆盖索引(需权衡写入开销,建议 DBA 评估后执行)
ALTER TABLE oms_inventory_info
  ADD INDEX idx_pc_status_amt (product_code, inventory_status, inner_price, outer_price),
  ALGORITHM=INPLACE, LOCK=NONE;

注意:加 P1-3 前须确认字段长度不超 InnoDB 索引上限(product_code/inventory_status 为 varchar(255) utf8mb4,四列合计约 2.1KB < 3072B,可行)。

ALTER TABLE oms_inventory_inner
  ADD INDEX idx_order_code (order_code), ALGORITHM=INPLACE, LOCK=NONE;
ALTER TABLE oms_purchase_order
  ADD INDEX idx_vendor_id (vendor_id), ALGORITHM=INPLACE, LOCK=NONE;

6.3 可选(P2,仅在启用对应能力时添加)

序号 表 索引名 列 启用条件
P2-1 oms_purchase_order_map idx_order_id order_id 若要支持"按订单号反查采购单"(当前不支持该入口)
P2-2 oms_purchase_order_map idx_purchase_id purchase_id 同上
P2-3 oms_inventory_info.idx_warehouse_id — — 已被 P2-7 取代((warehouse_id, inventory_status) 组合更适用)
P2-4 oms_stock_info.idx_order_code — — 已并入 P2-8
P2-5 oms_receivable_receipt_detail idx_write_off_id write_off_id 若要支持按核销单号反查(当前不支持)
P2-6 oms_payable_payment_detail idx_write_off_id write_off_id 同上
P2-7 oms_inventory_info idx_wh_status (warehouse_id, inventory_status) 若要支持 warehouse_list(entity=SN) 仅按仓库+状态查询(大表,无此索引必全表扫描)
P2-8 oms_stock_info idx_order_code order_code 备货范围查询(warehouse_list(entity=STOCK)、inventory_flow.stock);当前 526 行
P2-9 oms_finance_charge idx_charge_status charge_status 计收状态维度统计;点查已可用 order_code(UK)
P2-10 oms_purchase_order idx_status_date (status, purchase_date) 采购范围查询与时间维度;当前 941 行
P2-11 oms_receivable_bill / oms_payable_bill / oms_receipt_bill / oms_payment_bill / oms_invoice_bill / oms_ticket_bill idx_partner_code / idx_vendor_code partner_code / vendor_code finance_balance_aggregate(group_by=PARTNER) 与按合作伙伴列表查询;当前均 < 700 行
P2-12 oms_inventory_info idx_create_time create_time group_by=TIME_MONTH/TIME_QUARTER 时间维度(大表);小表无需
P2-13 oms_inventory_info idx_wh_pc (warehouse_id, product_code) group_by=PRODUCT_WAREHOUSE 的 LIST 模式游标对齐(否则 filesort,见 15.2)
P2-14 oms_inventory_info idx_status_pc (inventory_status, product_code) group_by=STATUS 的 LIST 模式游标对齐
P2-15 oms_inventory_inner idx_purchase_no purchase_no metrics=ARRIVAL_DELAY_DAYS 需按采购单号 join,当前无索引(见 15.3)
P2-16 order_delivery idx_status_created (delivery_status, created_at) manage 域发货单按状态/时间范围查询;当前 355 行
P2-17 delivery_list idx_delivery_deleted (delivery_id, deleted_at) manage 域发货 SN 明细的软删除过滤(现只能靠 idx_delivery_id 后再过滤);当前 32,718 行
P2-18 project_order_info_recall idx_order_code order_code 撤回历史按合同号查询;当前仅主键(40 行)
P2-19 oms_inventory_inner_detail — — 不采用:该表实测仅 1 行(未启用),入库明细以 oms_inventory_info(按 inner_code)为准,故无需索引
P2-20 product_info idx_vendor_code vendor_code master_data_list(entity=PRODUCT) 按制造商过滤;当前 341 行
P2-21 project_poc_info idx_project_id project_id project_list(entity=POC) 按项目查询;当前仅主键(998 行)
P2-22 oms_quotation / oms_quotation_product_info idx_quotation_code / idx_quotation_id quotation_code / quotation_id project_list(entity=QUOTATION);当前均仅主键(0 行,启用后加)
P2-23 project_product_info idx_product_bom_code product_bom_code cross_domain_aggregate 的 PRODUCT 维度与"跨项目按产品查询";当前仅 idx_project_id
P2-24 project_info idx_partner_code partner_code cross_domain_aggregate 的 PARTNER 维度(project_info 现有 customer_code/agent_code 索引,缺 partner_code)
P2-25 project_order_info idx_approve_time approve_time cross_domain_aggregate 的 MONTH/QUARTER 时间维度(order_code/partner_code/project_id 已有索引)
P2-26 bu_todo_completed idx_approve_user_time (approve_user, approve_time) approval_list(entity=DONE) 按"审批人 + 时间"翻页;当前 5,876 行仅 idx_business_key,全表扫尚可,增长后需补

以上 P2 的触发条件统一为:对应表行数增长到 10 万+(或该查询已成为高频热点)。当前除 oms_inventory_info、oms_inventory_delivery_detail 外全部 < 1000 行,暂不执行。

6.4 明确"不能加"的索引

表.列 原因
oms_inventory_delivery.order_code 该列不存在(由 oms_inventory_outer join 得出),建索引会直接失败;按订单号查发货必须经 outer_code 关联
oms_inventory_outer.warehouse_id 该列不存在(仓库在 oms_inventory_outer_detail 上)

时间列索引策略(v7 调整):v5 曾"撤销时间范围过滤",因其无索引会全表扫描。v7 依据表规模分级(14.4)改为:小表(<1000 行)允许时间范围过滤(全表扫描 <10ms,无需索引);大表(oms_inventory_info 等)的时间维度聚合需先加 create_time 索引(P2-12),或仅接受"带索引键收窄后 + 时间二次过滤"。

6.5 索引上线注意

  1. 先在生产/正式测试库执行 SHOW INDEX FROM <table> 复核本方案的"现有索引"结论(本文结论基于 oms_test);
  2. 使用 ALGORITHM=INPLACE, LOCK=NONE 在线加索引,建议低峰执行;
  3. 加完用 EXPLAIN 验证目标语句 type 非 ALL;
  4. 加索引属 DDL,不改字段、不改业务逻辑。

七、需要新增的只读 SQL

# 位置 内容 目的
1 InventoryOuterDetailMapper.java/.xml listByOuterCodeList(List<String>):outer_code in (...) 消除出库明细 N+1(依赖 P0-2 索引)
2 OmsInventoryDeliveryDetailMapper.java/.xml listByDeliveryIdList(List<Long>):delivery_id in (...) 消除发货明细 N+1(idx_delivery_id 已具备)
3 InventoryInfoMapper.java/.xml aggregateStock(List<String> productCodes, String groupBy, String lastKey, int limit, ...):按 group_by 动态分组 + 游标 + order by inventory_stock_aggregate 的 SUMMARY/LIST 两种模式
4 InventoryInfoMapper.java/.xml aggregateStockByWarehouse(List<String> productCodes):group by product_code, warehouse_id 上者的组内仓库拆分
5 OmsPurchaseOrderMapper.java/.xml aggregateArrival(String groupBy, String lastKey, int limit, ...):oms_purchase_order ⋈ item 动态分组聚合 purchase_arrival_aggregate
6 OmsReceivableBillMapper.java/.xml + OmsPayableBillMapper.java/.xml 按 order_code / partner_code / status / time_bucket 动态分组的余额聚合 finance_balance_aggregate
7 OmsStockInfoMapper.java/.xml 新增 list(OmsStockInfo) 带 order_code / stock_status / create_time 范围条件(当前仅 queryAll 且无索引条件) warehouse_list(entity=STOCK)、inventory_flow.stock
8 OmsFinanceChargeMapper.java/.xml 复用现有 selectOmsFinanceChargeList;如需按计收状态/时间聚合,追加 <if> 条件 finance_list(entity=CHARGE) 与 finance_order_position.charge
9 OmsPurchaseOrderHistoryMapper.java/.xml 新增按 purchase_no / 时间范围查询历史(现仅按 purchase_id 单值) purchase_list(entity=HISTORY)
10 VendorInfoMapper.java/.xml 复用 selectVendorInfoList(支持 vendorCodeList / vendorNameList 批量) master_data_list(entity=VENDOR)
11 InventoryOuterMapper.xml / OmsInventoryInnerMapper.xml / InventoryDeliveryMapper.xml / 财务各表 追加时间范围与状态的 <if> 条件(仅小表启用,见 6.4 时间列索引策略) 三域列表工具的范围过滤
12 OrderDeliveryMapper.java/.xml 新增按 code_list / status_list / 时间范围查询,并强制 deleted_at is null warehouse_list(entity=ORDER_DELIVERY)(manage 域发货单,含签收)
13 DeliveryListMapper.java/.xml 新增按 delivery_id in (...) 批量查 SN,带 deleted_at is null 同上的 SN 明细
14 OmsInventoryInnerDetailMapper.java/.xml 新增按 inner_code in (...) 查询 warehouse_list(entity=INNER, include_detail=true)
15 主数据 Mapper(PartnerInfoMapper / CustomerInfoMapper / AgentInfoMapper / ProductInfoMapper / VendorInfoMapper / SysUserMapper) 复用现有 selectXxxList;为 PRODUCT 增加 productCodeList 批量条件 master_data_list(含"批量编码翻译"能力)
16 ProjectOrderInfoRecallMapper.java/.xml 新增按 order_code 查询 warehouse_list(entity=RECALL) 撤回历史
17 ProjectInfoMapper / ProjectProductInfoMapper / ProjectWorkProgressMapper / ProjectPocInfoMapper / QuotationMapper(+QuotationProductInfoMapper) 新增/复用 list 查询,补 project_id in (...) / code_list / 时间范围条件 project_list(POC/报价表当前仅主键,需配套 P2-21/P2-22)
18 新增 CrossDomainAggregateMapper.java/.xml 按链路各一个分组聚合 select:SALES / PURCHASE / STOCK / FINANCE_AR / FINANCE_AP(维度动态、时间区间下推) cross_domain_aggregate
19 各聚合 Mapper 为时间维度追加区间下推条件(>= begin and < end),禁止 date_format() 所有聚合与透视工具的时间维度
20 SysUserMapper 复用按 user_id in (...) / 部门查询 master_data_list(entity=USER)
21 OrderInfoMapper.java/.xml 新增按 order_code in (...)(trim() 对齐)/ order_type / status / 时间范围查询,默认 deleted_at is null project_list(entity=CONTRACT);并供 warehouse_list(entity=ORDER_DELIVERY) 补全合同信息
22 OrderListMapper.java/.xml 新增按 order_id in (...) 批量查询,默认 deleted_at is null project_list(entity=CONTRACT_PRODUCT)
23 新增 BuTodoMapper.java/.xml listTodo(approveUser, processKeyList, businessKey, timeRange) 与 listDone(...):分别查 bu_todo / bu_todo_completed,按 apply_time / approve_time 倒序 + 游标分页 approval_list(entity=TODO/DONE)
24 OmsFinAttachmentMapper.java/.xml 新增按 related_bill_id in (...) + related_bill_type 查询,过滤 del_flag='0'(只返回元数据列,不含 file_path 内容) finance_list(entity=ATTACHMENT)
25 OmsReceivableBillMapper + OmsReceivableReceiptDetailMapper 新增时点重算聚合:Σ应收(create_time ≤ T) 与 Σ已收(receipt_time ≤ T)(支持 as_of_date) finance_balance_aggregate(as_of_date=…) 的历史时点余额

其余查询全部复用现有 in (...) 批量方法,不新增。


八、权限对齐规则

现有权限是异构的,必须逐个工具对齐其对应页面入口。

8.1 行级权限(数据范围)

数据域 机制 实现要求
仓储(出入库/发货/库存/备货/SN) IInventoryAuthService:authAll() / currentVendor() / authWarehouse() / authProductCode();部分在 Controller 拼、部分在 Service 拼;发货另带 @DataScope("t8") 工具内按对应 Controller 的拼法回填。不要照抄 OmsInventoryInnerServiceImpl 只取 currentVendor().get(0) 第一家的既有缺陷
采购(采购单/明细/历史/供应商/绑定) 行级 authVendorCodeList(Controller 层由 currentVendor() 生成) purchase_list(entity=ORDER/ITEM/HISTORY) 回填 authVendorCodeList;entity=VENDOR 直接用 currentVendor() 结果集
财务 无行级权限,仅菜单权限 只做菜单校验
订单(对比参考) selectProjectOrderInfoList 自带 @DataScope("t5") + authSql 走 service 即自动生效

8.2 菜单权限(isPermitted)—— 逐个工具/entity 对照来源

工具 / entity 权限串来源(实现时从该 Controller 的 @RequiresPermissions 抄取) 已知值
inventory_sn_trace / inventory_stock_aggregate InventoryInfoController、VueInventoryInfoController 待抄取
inventory_flow InventoryOuterController、VueDeliveryController 待抄取
warehouse_list(INNER/OUTER/DELIVERY/STOCK/SN) OmsInventoryInnerController、InventoryOuterController、VueDeliveryController、OmsStockInfoController、InventoryInfoController 待抄取
warehouse_list(ORDER_DELIVERY) manage 域发货单对应 Controller(OrderDeliveryController 及 VueDeliveryController 同路径) 待抄取
warehouse_list(RECALL) ProjectOrderInfoController(撤回/版本相关接口) 待抄取
purchase_arrival_aggregate / purchase_list(ORDER/ITEM/ORDER_BIND) OmsPurchaseOrderController sip:purchaseorder:list ✅
purchase_list(HISTORY) OmsPurchaseOrderController 历史接口 待抄取
master_data_list(PARTNER / CUSTOMER / AGENT) PartnerInfoController / CustomerInfoController / AgentInfoController(含 Vue* 版本) 待抄取
master_data_list(VENDOR) VendorInfoController、VueVendorInfoController 待抄取
master_data_list(PRODUCT) ProductInfoController、VueProductInfoController 待抄取
master_data_list(USER) SysUserController(系统用户) 待抄取
master_data_list(WAREHOUSE) OmsWarehouseInfoController 待抄取
master_data_list(COMPANY) OmsCompanyInfoController 待抄取
project_list(PROJECT/PROJECT_PRODUCT/PROGRESS/POC) ProjectInfoController、ProjectOrderInfoController(含 Vue* 版本) 待抄取
project_list(QUOTATION) QuotationController(含 Vue* 版本) 待抄取
project_list(CONTRACT/CONTRACT_PRODUCT) manage 域合同对应 Controller(OrderInfoController / VueOrderInfoController 等) 待抄取
cross_domain_aggregate 按链路做最小权限校验:需同时具备所访问链路的菜单权限(SALES 需订单/项目查看权限;FINANCE_AR/FINANCE_AP 需对应财务权限) 待抄取
finance_order_position / finance_balance_aggregate OmsReceivableBillController、OmsPayableBillController finance:receivable:list ✅,应付待抄取
finance_list(RECEIPT/PAYMENT/INVOICE/TICKET) OmsReceiptBillController、OmsPaymentBillController、OmsInvoiceBillController、OmsTicketBillController 待抄取
finance_list(CHARGE) OmsFinanceChargeController 待抄取
finance_list(ATTACHMENT) OmsFinAttachmentController(或财务单据 Controller 的附件接口) 待抄取
approval_list(TODO/DONE) 待办/已办接口对应 Controller(OmsPurchaseOrderController 的 approveList/approvedList、财务付款审批接口、ApprovalTaskController 等) 待抄取

8.3 统一要求

  1. handle 第一行做 isPermitted 校验,不通过返回 AUTH_ERROR(不要返回空列表,否则 Agent 会误判为"无数据");
  2. 行级权限指纹并入游标 filterHash(见 5.4),避免权限变化导致游标错位;
  3. "权限不足"与"确实无数据"必须在响应中可区分:前者走 error,后者 data.items = []。

九、聚合口径定义

工具 度量 口径
inventory_stock_aggregate in_stock_qty / out_stock_qty count(*) where inventory_status='0' / ='1'
inner_amount / outer_amount sum(inner_price) / sum(outer_price),NULL 计 0
未税金额(如返回) 按明细税率换算;tax_rate 为空时按 0 计(实测存在 NULL)
purchase_arrival_aggregate purchase_qty / inner_qty / pending_qty oms_purchase_order_item.quantity / inner_quantity / 两者差值
arrival_rate inner_qty / purchase_qty,HALF_UP 保留 2 位;分母为 0 时返回 0
amount_total / tax_total sum(amount_total) / sum(tax_total)
finance_balance_aggregate 应收侧 4 值 / 应付侧 4 值 直接 sum 冗余列:unreceived_amount、uninvoiced_amount、unpaid_payment_amount、unreceived_ticket_amount。不重算,避免口径偏差与额外开销

v7 新增度量口径

工具 度量 口径
purchase_arrival_aggregate arrival_delay_days datediff(入库时间, item.delivery_date) 的均值;未入库的不计(或按参数 include_pending_delay=true 用今天计算);分母 0 返回 null
finance_balance_aggregate overdue_days datediff(今天, plan_receipt_date)(仅当 unreceived_amount > 0);< 0 返回 0 表示未到期
财务类 PARTNER 维度 应收按 partner_code(客户),应付按 vendor_code(制造商);两者需在 metadata 注明维度主体差异

v7 新增维度口径

group_by 分组键 说明
STATUS 各表状态列 返回状态编码 + *Name(枚举翻译)
TIME_MONTH time_bucket = yyyy-MM 区间下推实现:create_time >= '2026-01-01' and create_time < '2026-02-01',禁止 date_format(create_time,...)
TIME_QUARTER time_bucket = yyyyQn 同上,按季度区间
PARTNER partner_code / vendor_code 见上表维度主体差异
VENDOR vendor_id 采购侧制造商(依赖 P1-2)
PRODUCT product_code 采购侧按产品

时间维度的默认区间:未传 time_range 时取近 12 个月,避免无界扫描;区间跨度上限 36 个月。

v13 新增口径

工具 项 口径
finance_balance_aggregate group_by=OVERDUE_BUCKET(账龄分桶) 基于 datediff(今天, plan_receipt_date) 分桶:0-30 / 31-60 / 61-90 / 90+;仅统计 unreceived_amount > 0 的行;边界左闭右闭(=30 入 0-30);同时返回 in_bucket_qty、in_bucket_amount
finance_balance_aggregate as_of_date(历史时点余额) 重算口径:应收(≤T) = Σ receivable_bill.total_price_with_tax where create_time ≤ T;已收(≤T) = Σ receipt_detail.receipt_amount where receipt_time ≤ T;时点未收 = 两者差。与冗余列"当前值"口径不同,metadata 必须标注 basis: RECALCULATED@<as_of_date>,且未传 as_of_date 时仍用冗余列(basis: CURRENT)

上述口径必须写入返回的 metadata.aggregation_rule。


十、实施步骤

  1. 索引 DDL:执行 P0(必须)+ P1(建议),并按 16.4 的执行方案(预检 → 分步 → 验证 → 可回滚)推进;P2 暂不执行(按表规模触发);
  2. 新增只读 SQL:第七节 25 项;
  3. 公共能力:在 llm/tools/support 增加分页/游标/入参解析/权限回填的轻量基类(继承 AbstractMcpToolProvider),含 page_size 上限、limit+1 探测、游标编解码与 filterHash 校验、max_pages 保护、mode/group_by/metrics/entity/dimensions 枚举校验与类型规范化(varchar 强制字符串)、参数冲突校验(见 15.5)、表白名单(见 15.12)、从库路由开关(见 16.6)、时间区间配置(见 16.5);
  4. 实现 13 个工具:A 类 3 个(含 stock/charge 分组)→ B 类 3 个(SUMMARY 优先,再补 LIST;含 OVERDUE_BUCKET 与 as_of_date)→ C 类 5 个(entity 参数化,含 master_data_list 与 approval_list)→ D 类 2 个(project_list、cross_domain_aggregate),继承基类 + @Component 自动注册(无需改 ToolInitializer);
  5. RAG 路由(16.7)与 D 类同期上线:ToolRetriever / ToolRouter + tools/list 的 query/detail 支持,否则 13 个工具的 schema token 不可接受;
  6. 逐层验收:每完成一类即按第十一节验证,并记录 P95 延迟、返回字节数、schema token 三个指标。

10.1 建议的实施批次(可按需截断)

批次 内容 价值
第 1 批 索引 P0 + 只读 SQL 1/2 + 公共基类 + A 类 3 个工具 覆盖"单号追溯"与"订单财务/货流全景"
第 2 批 B 类 3 个(含 SUMMARY/Top-N/时间维度) 覆盖"统计分析",避免 Agent 翻页
第 3 批 C 类 5 个(列表/范围查询 + 主数据批量翻译 + 审批待办/已办) 补齐"本月有哪些/多少""编码→名称""我还有哪些单要审"
第 4 批 RAG 路由(16.7) 控制 schema token、降低选错率
第 5 批 D 类 2 个(项目/POC/报价、受限跨域透视) 补齐项目域与组合分析
第 6 批 P1 索引(含覆盖索引)+ 性能调优 + 压测(见 15.13) 大规模下提速与容量验证

十一、验收标准

11.1 正确性

  1. 不重不漏:同一过滤条件下,逐页拉完的结果集与一次性全量取的结果逐行比对一致;
  2. 翻页稳定性:翻页过程中并发插入/删除若干行,结果集不出现重复行;
  3. 游标防误用:篡改 cursor 中的过滤条件/权限指纹 → 必须报错,而非返回错数据;
  4. 口径一致:聚合结果与页面同条件展示一致(金额、数量、税率为 NULL 的兜底);
  5. 端到端:以真实单号(发货 775、出库/采购/应收各一条)跑完整翻页,has_more=false 后条目数与页面一致。

11.2 性能

  1. 每个游标过滤键 EXPLAIN 结果 type 非 ALL;
  2. inventory_sn_trace 在 50 个 SN 下目标 P95 < 300ms;
  3. 记录各工具 P95 延迟与返回字节数(作为基线入库)。

11.3 保护机制

  1. max_pages 超限、include_total 超 count_cap、单页字节超限,三条路径均触发预期行为。

11.4 安全

  1. 无权限用户调用 → 返回 AUTH_ERROR(非空列表);
  2. 供应商/仓库行级权限生效:越权数据不可见。

十二、遗留与待确认

# 事项 说明
1 生产库量级 本文量级来自 oms_test(oms_inventory_info 6.5 万)。若生产为百万级,保持 product_sn_list ≤ 50、禁止仅按 product_code/warehouse_id 查 SN,并触发 P2-7/P2-12 索引
2 生产索引复核 执行 SHOW INDEX FROM <table> 复核 2.5 节结论(索引结构理论上与测试库一致,但需确认)
3 聚合分页粒度 已定:库存聚合以产品为页、仓库作组内嵌套;采购按 purchase_no;财务按 order_code。按"产品+仓库"行粒度分页需重新评估跨索引排序成本
4 include_total 默认值 当前默认 false(避免百万级 count);若业务更关心总数可改为 true + 提高 count_cap
5 菜单权限串 财务/采购部分工具与 entity 的精确 @RequiresPermissions 需实现时逐个从 Controller 抄取(对照表见 8.2)
6 P0/P1 索引 DDL 执行窗口 需 DBA 在低峰期执行,P1-3 覆盖索引须先评估写入放大
7 时间维度默认区间 当前定义为近 12 个月、跨度上限 36 个月;若业务需要更长历史,需同时补大表时间索引(P2-12)并放宽上限
8 从库路由(可选) slave 数据源当前 enabled=false;若要启用统计查询走从库,需 DBA 确认延迟与可用性
9 "在库"口径 未配置化(2026-09-23 回滚):曾尝试以 mcp.inventory.stock-basis 配置化,经确认不引入配置机制,已全部回滚。当前口径写死在 InventoryInfoMapper.xml 的 inventory_status='0',并在工具 metadata 中声明"未扣除已发货占用,业务待确认";业务确认后需改代码
10 价格含税口径 未配置化(2026-09-23 回滚):同上,不引入配置。当前仅在 inventory_stock_aggregate / inventory_sn_trace 的 metadata 中声明"是否含税未经业务确认,不做换算",工具不做任何含税/未税换算
11 备份表白名单 库中存在 oms_inventory_info_copy1(42,836 行) 等大量备份表,实现时必须以白名单登记可访问表(见 15.12)
12 项目进度 / POC / 报价 已纳入(见 16.1):project_list 工具,entity = PROJECT/PROJECT_PRODUCT/PROGRESS/POC/QUOTATION/CONTRACT/CONTRACT_PRODUCT
13 RAG 工具路由 13 个工具下必须实现(见 16.7),否则每轮 schema token 不可接受;需 20 条中文问题做命中率验收
14 跨域透视成本 cross_domain_aggregate 的 SALES 链路为 3 表 join + 分组,属兜底能力;须在生产数据上验证 P95,必要时补 P2-23/24/25
15 两套订单模型的关系 manage 域 order_info(366) 与项目域 project_order_info(809) 仅 330/366 可按 order_code 对齐;二者业务关系(新旧两代?两套视角?)需业务确认,并确认"合同编号"以哪张表为准
16 order_type 取值含义 实测为 zq/dls(非注释的 1/2),需业务确认 zq=直签、dls=代理商
17 是否纳入审批待办/已办 已纳入(v13):新增工具 approval_list(entity = TODO/DONE),工具数 12 → 13;数据源 bu_todo/bu_todo_completed,无需碰 Flowable act_*(见 A.15)
18 是否纳入账龄分桶 已纳入(v13):finance_balance_aggregate 增加 group_by=OVERDUE_BUCKET(0-30/31-60/61-90/90+),零成本(见 9 章 v13 新增口径)
19 是否需要财务历史时点余额 已纳入(v13):finance_balance_aggregate 增加 as_of_date,按明细重算时点余额;口径与冗余列不同,metadata.basis 标注 RECALCULATED@<date> vs CURRENT
20 fianance_ticket 拼写 process_key 存在源码级拼写错误(少一个 n),匹配必须按原样,勿"修正"

十三、分析统计场景优化(v5,实测驱动)

13.1 问题:分析统计会退化为"Agent 驱动的多次全表扫描"

原设计只提供"游标分页明细",没有"一次算完"的能力。当 Agent 需要全局统计(如"某产品总在库量""所有订单未收款合计")时,只能翻页累加,后果:

  1. 扫描量一点没省:实测分页第 1 页 34.4ms ≈ 全局汇总 38.1ms —— 分页对聚合并不减少扫描;
  2. 往返与 token 放大 N 倍:5 页 = 5 次请求 + 5 份 schema;
  3. 可能拿不到全量:明细模式 6.5 万行 ≈ 648 页(page_size=100),必被 max_pages 截断 → 与"数据不缺失"目标直接冲突;
  4. 回表成本:count(*) 走覆盖索引 14.2ms,含 sum(inner_price/outer_price) 后 36.7ms(2.6×),因现有索引不含金额列。

13.2 优化 1(收益最大,零 DDL):聚合工具新增 mode=SUMMARY

聚合类 3 个工具(#4/#5/#6)新增参数:

参数 取值 说明
mode SUMMARY(默认)/ LIST SUMMARY=一次算完并返回汇总,不分页;LIST=游标分页返回完整分组明细
group_by 库存:NONE / PRODUCT / WAREHOUSE / PRODUCT_WAREHOUSE / STATUS / TIME_MONTH / TIME_QUARTER;采购:NONE / ORDER / VENDOR / PRODUCT / STATUS / TIME_MONTH;财务:NONE / ORDER / PARTNER / STATUS / TIME_MONTH 分析维度;NONE 只返回一行总计。口径见第九章
top_n int,默认 10,上限 100 SUMMARY 下按度量取前 N,由 DB 完成(order by <metric> desc limit N)
metrics 数组,如 ["QTY","AMOUNT","ARRIVAL_RATE","ARRIVAL_DELAY_DAYS","OVERDUE_DAYS"] 只计算需要的度量,减少回表列
include_summary bool LIST 模式下附带一次全局总计(1 行),便于同时拿到"总量 + 明细"

SUMMARY 实现:单条 SQL 完成全量聚合:

-- group_by=NONE:1 行总计(实测约 38ms @6.5 万行)
select count(*) total_qty,
       sum(case when inventory_status = '0' then 1 else 0 end) in_stock_qty,
       sum(inner_price) inner_amount
from oms_inventory_info
where <过滤条件>;

-- group_by=PRODUCT + top_n=10:排序取前 10 由 DB 完成
select product_code,
       count(*) total_qty,
       sum(case when inventory_status = '0' then 1 else 0 end) in_stock_qty,
       sum(inner_price) inner_amount
from oms_inventory_info
where <过滤条件>
group by product_code
order by total_qty desc
limit 10;

收益:把"N 页 × 全表扫描"变为"1 次全表扫描",结果完整且不受 max_pages 影响。

13.3 优化 2(零 DDL):Top-N 取代全量明细

分析统计的绝大多数问题是"最大的 / 最差的 / 占比 Top-N",用 top_n + 排序交给 MySQL,只返回 N 行。只有当需求明确是"逐行导出 / 对账"时才用 LIST 翻页。

护栏:LIST 模式对聚合类工具必须给出范围(如 product_code_list、order_code_list),否则返回 INVALID_PARAMS 并提示改用 SUMMARY,避免 Agent 无意识触发 648 页翻页。

13.4 优化 3(需确认):覆盖索引,让"1 次全表扫描"也变快

  • 现状:idx_product_code 只能覆盖 count(*)(实测 Using index,14.2ms);sum(inner_price/outer_price) 需回表(36.7ms)。
  • 建议:仅对 oms_inventory_info 加覆盖索引 (product_code, inventory_status, inner_price, outer_price)(见索引 P1-3)。
  • 原则:只对线性增长的大表加;小表不加(应收表 627 行全表聚合仅 21.3ms,加索引反而增加写入开销)。
  • 代价:索引体积 + 入库写 SN 时的写放大,需 DBA 评估。

13.5 优化 4(零 DDL):数据类型严格对齐,防止索引退化

实测对比:

写法 EXPLAIN 结果
where inner_code = 'R-20250917001' key=idx_code,rows=const 精确定位 ✅
where inner_code = 0(数字) key=idx_code,rows=None,Extra=Using where 索引退化为逐行过滤 ❌
where product_code = '9801H0BC' rows=const 精确定位 ✅
where product_code = 9801(数字) rows=None,Extra=Using where 退化 ❌

强制规则(在工具内做类型规范化,不交给模型自由传类型):

  • varchar 列一律传字符串:编码类、inventory_status(实测为 varchar(255),必须传 '0'/'1');
  • int 列传 int:如 warehouse_id;
  • 禁止在 where 中对列做函数或类型转换。

13.6 优化 5(零 DDL):组内嵌套改用 IN 收窄

实测仓库拆分查询出现 Using temporary。改为"先取本页产品码 → where product_code in (本页产品码) 查仓库拆分",把临时表规模限制在一页之内(实测单产品 21.8ms)。

13.7 优化 6:查询超时 + 并发限流(防止拖垮库)

  • 实测配置 Druid maxActive=20(ruoyi-admin/src/main/resources/application-dev.yml),Agent 循环/并行调用会占满连接池。
  • 措施:① 聚合 SQL 设置执行超时(MySQL 8 支持 /*+ MAX_EXECUTION_TIME(3000) */ 或 JDBC setQueryTimeout),超时返回明确错误;② MCP 层对同一 bot 限流(如 60 次/分);③ 工具内不做并行查询(沿用现有串行写法);④ 保留 max_pages 兜底。

13.8 优化 7(可选):统计类查询路由从库

application-dev.yml 已有 slave 数据源占位(enabled=false)。分析类聚合可考虑路由只读从库,避免影响主库。 限制:主从延迟 → 财务金额不可走从库;库存/汇总类可。需 DBA 确认从库可用性与延迟。

13.9 优化 8:向 Agent 声明"支持的统计维度清单"

在 metadata 中声明允许的 group_by / metrics 枚举,清单外维度明确不支持,避免 Agent 用明细工具硬凑而触发全表扫描。

13.10 仍然存在的边界(诚实声明)

  1. SUMMARY 本身仍是 1 次 O(N) 索引扫描;不做预聚合表则无法做到亚秒级(当前 6.5 万行 ~38ms,百万级预计 ~0.6s,可接受);
  2. 不支持跨表任意维度(如"客户 × 产品 × 月份");那需要通用 SQL 能力,出于安全与性能不开放;
  3. 若要亚秒级 + 固定维度统计,需预聚合表 + 可靠定时任务,属新增功能须单独评估;oms_finance_operate_report 的教训是:必须有可靠的定时刷新机制,不能依赖手工触发接口;
  4. include_total=true 在大表上仍是额外 count 开销,故默认关闭。

13.11 优化后的调用形态对照

Agent 的问题 优化前 优化后
"某产品还有多少库存" 翻页累加,约 5 次调用 1 次 mode=SUMMARY, group_by=NONE
"库存最多的 10 个产品" 翻完 94 组后在模型侧排序 1 次 group_by=PRODUCT, top_n=10
"本月采购到货率" 翻页累加或模型计算 1 次 group_by=ORDER, mode=SUMMARY(含 arrivalRate)
"这些订单还欠多少钱" 多页累加 1 次 group_by=ORDER, mode=SUMMARY 或 order_code_list 精确查
"导出全部 SN 明细" 648 页(被截断) mode=LIST 且必须给范围;否则明确拒绝并提示收窄条件

十四、覆盖度缺口分析与完善(v6 分析 / v7 已补全)

本章回答三个问题:统计维度覆盖了多少?三大域是否完整?当前是否算"最优"?

v7 状态:本章识别出的缺口已全部并入最终方案——A 档(时间/状态/伙伴/负责人维度 + 时效度量)、B 档(warehouse_list/purchase_list/finance_list 三个列表工具,并把 v6 的 purchase_order_detail/finance_bill_detail 合并进去)、C 档(inventory_flow.stock 备货分组、finance_order_position.charge 计收分组)、D 档(P2 条件索引)均已落地。下面的矩阵保留作为缺口审计记录与后续回归依据。

14.1 三大域覆盖度矩阵

仓储域(7 个对象 / v11 已全部覆盖)

对象 / 表 v6 判定 现状(v11)
入库单 oms_inventory_inner ✅ ✅ warehouse_list(INNER) + inventory_flow
出库单 oms_inventory_outer(+_detail) ✅ ✅ warehouse_list(OUTER) + inventory_flow
发货单 oms_inventory_delivery(+_detail) ✅ ✅ warehouse_list(DELIVERY);另有 manage 域 ORDER_DELIVERY(含签收)
SN 条码明细 oms_inventory_info ✅ ✅ inventory_sn_trace / inventory_stock_aggregate
备货状态 oms_stock_info ❌ ✅ v7 闭合:warehouse_list(STOCK) + inventory_flow.stock
仓库主数据 oms_warehouse_info ❌ ✅ v10 闭合:master_data_list(WAREHOUSE)
单据范围查询(按状态/时间) ❌ ✅ v7 闭合:warehouse_list 支持状态/时间范围过滤

采购域(6 个对象 / v11 覆盖 5)

对象 / 表 v6 判定 现状(v11)
采购单 oms_purchase_order(+_item) ✅ ✅ purchase_list(ORDER/ITEM) + purchase_arrival_aggregate
采购单范围查询 ❌ ✅ v7 闭合:purchase_list 支持状态/时间/供应商
供应商主数据 oms_vendor_info ❌ ✅ v8 闭合:master_data_list(VENDOR)
采购-订单绑定 oms_purchase_order_map ❌ ✅ v8 闭合:purchase_list(ORDER_BIND)
采购历史版本 ❌ ✅ v8 闭合:purchase_list(HISTORY)
采购审批待办 / 已办 ❌ ❌ 仍缺 → 见 16.9(实测可低成本闭合)

财务域(8 个对象 / v11 覆盖 6)

对象 / 表 v6 判定 现状(v11)
应收 / 应付 / 收款 / 付款 / 开票 / 收票 ✅ ✅ 单号点查 + 订单全景 + 5 流余额汇总
计划表 / 明细表 / 核销表 ✅ ✅ finance_order_position / finance_list
计收 oms_finance_charge ❌ ✅ v7 闭合:finance_list(CHARGE) + finance_order_position.charge
财务单据范围查询 ❌ ✅ v7 闭合:finance_list 支持状态/时间/合作伙伴
财务运营报表 oms_finance_operate_report ❌(刻意) ❌ 仍缺(刻意剔除物化表)→ 见 16.9:可用"按明细重算"补历史时点余额
财务附件 oms_fin_attachment ❌ ⚠️ 可闭合为"附件元数据"→ 见 16.9

14.2 统计维度覆盖矩阵

维度 v6 判定 现状(v11)
产品 PRODUCT / 仓库 WAREHOUSE / 采购单 ORDER / 订单 ORDER / 无维度 NONE ✅ ✅
时间维度(月/季) ❌ ✅ v7 闭合:TIME_MONTH / TIME_QUARTER(区间下推)
合作伙伴/客户 PARTNER ❌ ✅ v7 闭合(LIST 需 P2-11)
状态分布 STATUS ❌ ✅ v7 闭合(LIST 需 P2-14)
及时率 / 超期 ❌ ✅ v7 闭合:ARRIVAL_DELAY_DAYS / OVERDUE_DAYS
负责人/销售 OWNER ❌ ⛔ 主动移除(无索引 + 业务价值未确认)→ 见 16.9
账龄分桶(0-30/31-60/61-90/90+) — ❌ 仍缺 → 见 16.9(零成本可补:group_by=OVERDUE_BUCKET)

结论(更新):统计维度覆盖 v11 约 90%;仅剩 账龄分桶(可零成本补)与 OWNER(主动移除)两项。

14.3 最严重的缺口不是"统计维度",而是"列表能力"整体缺失

当前 8 个工具中,所有单据类查询都强制"按单号点查"(outer_code / purchase_no_list / bill_code_list)。直接后果是这些最基础的问题无法回答:

  • "本月有哪些采购单?" / "哪些采购单还没入库?"
  • "这个月发货了多少单?" / "哪些出库单还没确认?"
  • "本月开了多少票、收了多少款?"

这类问题在业务里出现频率极高,而当前方案要么拒绝、要么逼 Agent 用 SN/明细工具硬凑(必然触发大表扫描)。这是比统计维度更优先要补的缺口。

14.4 实测修正:我此前"无索引=不支持"的规则过严

实测行数分布推翻了"一刀切":

规模档 表 全表扫描代价 结论
大表(线性增长) oms_inventory_info 64,823;oms_inventory_delivery_detail 49,015 数十~数百 ms 必须严守索引约束
小表(<1000 行) 采购 941/951、出库 734/750、入库 580、发货 756、应收 627、应付 583、备货 526、计收 287、历史 121/142、供应商 17、仓库 14 <10 ms 可以直接支持按状态/时间/伙伴过滤,无需索引

方案修正:把"无索引 = 不支持该入口"改为按表规模分级:

  • 大表:只用索引列做入口(维持原约束);
  • 小表:允许状态/时间/伙伴维度的范围查询与统计(接受全表扫描),并标注"该表当前规模小,若增长需补索引"。

这一条修正同时解开了 14.1、14.2、14.3 的多数缺口。

技术注意:时间维度聚合若写成 date_format(create_time,'%Y-%m'),对列做函数会使索引失效;正确做法是按区间下推(create_time >= '2026-01-01' and create_time < '2026-02-01'),未来若为大表加时间索引才有效。

14.5 是否算"最优"?——分两个层面回答

层面 评价 依据
性能与实现质量 接近该架构下的上限 过滤键实测对齐索引、游标分页不重不漏、SUMMARY 一次算完、类型对齐防索引退化、超时限流;均有实测支撑
业务覆盖完备性 原为不完备(统计维度约 60%、列表能力缺失);v7 已补全至 ~95% 通过 A/B/C/D 四档补全:时间/状态/伙伴/负责人维度、三域列表查询、计收、备货、供应商主数据、采购-订单绑定、采购历史

并且"完备"与"轻量"本质冲突:工具数越多,schema token 与模型选错率越高(当前 8 个已接近无路由时的上限)。因此不存在绝对最优,只有按实际提问分布做取舍。

14.6 完善建议(按性价比分三档)

A 档 · 零新增工具(只扩参数枚举)→ 建议立即纳入

项 做法
时间维度 聚合类 group_by 增加 TIME_MONTH / TIME_QUARTER(区间下推实现)
状态分布 增加 group_by=STATUS
负责人维度 已移除:OWNER 无索引支撑且业务价值未确认,见 15.2
及时率/超期 度量增加 OVERDUE_DAYS(计划收款日 vs 今天)、ARRIVAL_DELAY(交货日 vs 实际入库日)

B 档 · 补"域内列表查询"(补基础能力,建议每域 1 个,工具数 8 → 11)

工具 覆盖 过滤维度(分页)
warehouse_list entity = DELIVERY / OUTER / INNER / STOCK 单号、状态、时间范围、仓库、产品、合同号
purchase_list entity = PURCHASE_ORDER / VENDOR / ORDER_BIND / HISTORY 单号、状态、审批/确认状态、时间范围、供应商
finance_list entity = RECEIVABLE / PAYABLE / RECEIPT / PAYMENT / INVOICE / TICKET / CHARGE 单号、状态、审批状态、时间范围、合作伙伴

用一个工具 + entity 参数而非每表一个工具,是为了在补全能力的同时把工具数增长压到最小。代价是单工具 schema 稍复杂。

C 档 · 零新增工具,把高价值点查塞进现有工具

项 做法 支撑
计收 finance_order_position 增加 charge 分组(计收状态、收入/成本/毛利) oms_finance_charge.order_code 有唯一索引 ✅
备货 inventory_flow 增加 stock 分组(备货状态、一次备齐) 526 行,按 order_code 扫可接受
采购-订单绑定 purchase_list 的 ORDER_BIND(若要开,需 P2-1/P2-2 索引) oms_purchase_order_map 仅主键

D 档 · 索引补充(仅当对应表增长时)

表 建议索引 触发条件
oms_stock_info idx_order_code(order_code) 行数 > 10 万
oms_finance_charge idx_charge_status(charge_status) 行数 > 10 万
oms_purchase_order idx_status_date(status, purchase_date) 行数 > 10 万
oms_inventory_outer/inner/delivery idx_create_time(create_time) 行数 > 10 万(当前 580~756 行,不必加)

14.7 补全后的代价与取舍(v7 已决策)

选择 工具数 覆盖 token / 选错率
原 v5 8 统计维度 60%、无列表能力 低
v6 拟定的 A+B+C 11 三大域基本完整 中
v7 采用 9 三大域覆盖完整(除审批待办、附件、运营报表);统计维度 ~95% 低(把 A/B/C 三档合并进 9 个工具,且用 entity/group_by 参数化而非新增工具)
v8 最终 10 三域 + 签收 + 撤回历史 + 主数据(批量编码翻译) 低(仅新增 1 个主数据工具,机制复用)

v7 决策:采纳 A + B + C + D 全部四档,但通过 ①合并 v6 的 purchase_order_detail/finance_bill_detail 进 purchase_list/finance_list、②用参数枚举表达维度与单据类型 两个手段,把工具数从 11 压回 9,在"覆盖完整"与"轻量"之间取得平衡。

14.8 明确不覆盖的范围(v9 更新)

  1. 采购/财务审批待办与已办(bu_todo 相关,属流程域);
  2. 财务附件与文件内容;
  3. 财务运营报表物化表(已否决,见 2.1);
  4. 跨域任意维度 → 已改为"受限透视":仅白名单维度与单链路度量,见 16.2;真正的任意 SQL / 无白名单组合仍不开放;
  5. 项目进度/POC/报价 → 已纳入 project_list,见 16.1;
  6. 任何写操作(新增/修改/删除/审批/撤回/红冲)。

十五、规格待定项定义(v8 补全)

本章专门消除"实现时必然产生歧义"的规格空白。上一版(v7)有 9 处未定义,本章逐条定义。

15.1 时间维度依据字段(原文只说"支持 TIME_MONTH",未说基于哪个字段)

工具 默认时间字段 可切换 索引依赖
inventory_stock_aggregate oms_inventory_info.create_time — P2-12(否则 1 次全表扫描)
purchase_arrival_aggregate oms_purchase_order.purchase_date(业务口径) time_field=CREATE_TIME P2-10
finance_balance_aggregate 各单 create_time(记账口径) time_field=PLAN_DATE → 应收取 plan_receipt_date、应付取 plan_payment_date P2-11 系列
  • 默认区间:近 12 个月;跨度上限 36 个月;跨月/季一律用区间下推(>= 月初 and < 下月初),禁止 date_format(create_time,...)(会使索引失效)。

15.2 维度可用性矩阵(v8 修正:解决"声称支持但无索引支撑"的矛盾)

区分两种模式:SUMMARY 只需 1 次扫描,维度不受索引限制;LIST 的分页游标必须与索引顺序一致。

group_by SUMMARY LIST(游标分页) 依赖索引
NONE ✅ —(不分页) —
PRODUCT ✅ ✅ oms_inventory_info.idx_product_code
WAREHOUSE ✅ ✅ 库存大表需 P2-7
PRODUCT_WAREHOUSE ✅ ⚠️ 需 P2-13 (warehouse_id, product_code),否则 filesort P2-13
STATUS ✅(1 次全表扫描,实测 ~40ms @6.5 万行) ⚠️ 需 P2-14 (inventory_status, product_code) P2-14
TIME_MONTH / TIME_QUARTER ✅ ⚠️ 需 P2-12 / P2-10 P2-12 / P2-10
ORDER ✅ ✅ 各表 order_code 索引
VENDOR ✅ ✅ P1-2
PARTNER ✅ ⚠️ 需 P2-11 P2-11
OVERDUE_BUCKET(v13 新增) ✅ ⚠️ 仅 SUMMARY(分桶后行数固定 ≤4,无需 LIST 分页) —

统一规则:

  1. LIST 模式下,若该维度无索引支撑 → 不静默 filesort,而是返回 INVALID_PARAMS 并提示"该维度请使用 mode=SUMMARY"(避免深分页把库拖垮);
  2. 移除 OWNER 维度:oms_purchase_order.owner_name 无索引且业务价值未确认(v7 的 A 档曾声称支持,属方案自相矛盾,此处更正)。

15.3 arrival_delay_days 语义与成本(原文未定义)

  • 定义:对每条采购明细行,delay = datediff(该明细首次入库时间, item.delivery_date);采购单维度取 max(delay)(最晚到货);未入库的明细不计入;无入库记录返回 null。
  • 首次入库时间来源:oms_inventory_inner(按 purchase_no 取 min(create_time))按 product_code 与 oms_inventory_inner_detail 对齐。
  • 成本与前提:oms_inventory_inner.purchase_no 无索引 → 需 P2-15;默认不计算(仅当 metrics 显式包含 ARRIVAL_DELAY_DAYS 时才发起该 join),并在 metadata 标注该度量的额外成本。

15.4 sub_cursor 协议(原文只提名字,未定义用法)—— ✅ 已实现

项 约定
入参回传 sub_list(子列表名)+ sub_cursor(字符串);sub_cursor 与主 cursor 互斥;首次翻页无游标时须给 sub_parent(父实体标识),sub_parent 也随游标携带,续页时无需再传
返回 data = {sub_list, sub_parent, total(该子列表总条数), items(本页), sub_page_info};sub_page_info 结构与 page_info 完全一致,sort_by 为该子表的排序键
编码 同主游标格式,但 t = "<tool>:<sub_list>";f 中额外包含主实体标识(inventory_flow 为 outer_code,finance_order_position 为账单号);k[0] = 续页偏移量
返回游标位置 主响应截断时 data.truncated_sub_lists[] 给出 {list, parent, total, returned, next_cursor},调用方直接回传 next_cursor 即可续页(不需要自己算偏移)
上限 子列表 page_size ≤ 100;max_pages ≤ **200**(原文为 20,实测不足:20×100=2000 行 < 实测单出库单 2682 条 SN 明细,会导致"截断 + 游标也取不完"的数据缺失;改为 200 后单父实体最多可取 20,000 行)
幂等性 主实体不变时同一 sub_cursor 可重复调用且结果稳定(实现上按子表主键 id 升序定序后切片;部分子表 SQL 无 order by,故在内存中按 id 统一定序,保证"截断点 == 续页起点")
覆盖范围 inventory_flow:outerDetails / snDetails / deliveries;finance_order_position:receiptPlans / receiptDetails / invoicePlans / paymentPlans / paymentDetails / ticketPlans
明确不覆盖 嵌套二级子列表(如 deliveries[].productSns)不单独提供游标;如需按物流单追溯 SN,请用 warehouse_list(entity=SN) 或 inventory_flow 的 snDetails 子列表

15.5 metrics 全枚举 与 参数冲突规则(原文只给示例)

metrics 全枚举

域 取值
库存 QTY、IN_STOCK_QTY、OUT_STOCK_QTY、INNER_AMOUNT、OUTER_AMOUNT
采购 PURCHASE_QTY、INNER_QTY、PENDING_QTY、ARRIVAL_RATE、ARRIVAL_DELAY_DAYS、AMOUNT_TOTAL、TAX_TOTAL
财务 RECEIVABLE_*、RECEIVED_*、UNRECEIVED_*、INVOICED_*、UNINVOICED_*、PAYABLE_*、PAID_*、UNPAID_*、TICKETED_*、UNTICKETED_*、OVERDUE_DAYS(* ∈ WITH_TAX / WITHOUT_TAX / TAX)

参数冲突规则(一律返回 INVALID_PARAMS 并回显允许值,不静默忽略)

冲突组合 处理
mode=SUMMARY + cursor / page_size 报错(SUMMARY 不分页)
mode=LIST + top_n 报错(top_n 仅 SUMMARY 可用)
cursor + page 报错
mode=LIST 且聚合工具未给范围 报错并提示改用 SUMMARY
metrics 含未定义值 报错并回显允许枚举
include_detail=false + code_list(列表工具) 允许(等价于按单号查表头)
group_by=OVERDUE_BUCKET + mode=LIST 报错(分桶行数固定 ≤4,只支持 SUMMARY)
as_of_date 早于最早单据日期 允许,返回 0 并在 metadata 标注

15.6 include_zero 判定标准(原文未定义)

  • 定义:分组内全部所请求 metrics 的值均为 0 或 null → 视为"全零行";
  • 默认 include_zero=false → 过滤掉全零行;置 true 则返回;
  • 判定基于本次请求的 metrics 集合,而非固定字段集,避免语义歧义。

15.7 "在库"口径(⚠️ 待业务确认)

候选 定义 风险
A(当前默认) inventory_status='0' 的全部 SN 视为在库 若业务含"已被发货单占用未出库"的占用量,则会高估在库
B 在 A 基础上排除已被发货单占用但未出库的 SN(需 join oms_inventory_delivery(_detail) / delivery_list) 成本更高,需确认占用判定规则
  • 当前处理:默认按 A,并在 metadata.aggregation_rule 明确写出"在库 = inventory_status='0',未扣除已发货占用";
  • 待业务确认后若改为 B,需同步补索引并重新评估性能。

15.8 含税 / 未税口径(⚠️ 待确认清单)

字段 实测现状 处理
oms_inventory_info.inner_price / outer_price 列注释仅"入库价 / 出库价",无"含税"字样 标注待确认,metadata 中不得写"含税"(v7 曾自行断言为含税,此处更正)
oms_inventory_inner_detail.inner_price 列注释明确"入库单价(含税)" 可作为入库明细的含税口径依据
采购 / 财务金额 total_price_with_tax / ..._without_tax 字段名自解释 直接采用

原则:字段名未自解释的,一律标注待确认,禁止在 metadata 中自行断言口径。

15.9 多币种策略

  • 采购 / 财务均有 currency 字段(实测固定人民币);
  • 不自动换算,按原币返回;一次聚合跨多种币种时返回 metadata.mixed_currency=true 并提示按币种分组查看;
  • 不引入汇率表(属新增功能,超出范围)。

15.10 大小写与排序规则(实测发现,原方案未考虑)

  • 实测:oms_inventory_info / oms_inventory_outer / oms_receivable_bill / oms_inventory_delivery 等表的 collation 均为 utf8mb4_unicode_ci → 大小写与重音不敏感,即 where outer_code = 'c-xxx' 会匹配到 C-XXX;
  • 策略:编码类过滤保持现状(与页面行为一致),但在 metadata 声明"编码匹配不区分大小写";如需严格区分,仅对单号精确点查提供 exact_case=true → SQL 使用 where binary outer_code = :code。

15.11 运行参数与可观测性(原文缺失)—— 超时/限流 ✅ 已实现

项 约定
查询超时 ✅ 聚合类 3000ms、列表/点查类 5000ms。实现:McpService 在调用工具前按工具名(*_aggregate → 3s,其余 5s)写入 McpQueryTimeout 线程上下文,McpQueryTimeoutInterceptor(MyBatis StatementHandler.prepare 插件)读取后 Statement.setQueryTimeout(n);未设置上下文的普通页面/报表 SQL 不受影响。超时由驱动 KILL QUERY 中止并映射为 -32003 query_timeout
限流 ✅ 同一机器人 60 次/分(滑动窗口)。实现偏离原文:项目未引入 Redis,故落为进程内 McpRateLimiter(key = X-Bot-Id,无凭证时退回绑定用户/匿名;命中返回 -32002 rate_limit_error)。多实例部署时为单实例口径,若需全局精确限流须改 Redis
日志埋点 ⏳ 未实现(tool/entity/mode/duration_ms/rows/page_no/has_more/truncated_by_bytes/filter_hash)
schema 版本 metadata.schema_version = 1(以代码常量为准;破坏性变更时递增)
类型规范化 varchar 一律 String.valueOf() 传入;int 一律 Integer/Long(见 13.5 实测)

15.12 备份表与白名单(防误用,实测发现风险)

实测 oms_test 存在大量备份/历史表,必须显式排除,任何工具不得指向:

oms_inventory_info_copy1(42,836 行)、delivery_list_0618(11,659)、product_info_260916、product_info_20260904bak、project_info_20260904bak、project_info_0707、project_order_info_1028 / _0627 / _bak1、project_product_info_bak / _0708 / _0627、order_info_0707 / _0708 / _bak、oms_purchase_order_1211、oms_payable_bill_copy1、oms_finance_operate_report* 等。

另有两张"看起来能用但已确认不采用"的表(v10 补充):

表 行数 处理
vendor_info 5 明确不采用:供应商主数据只用 oms_vendor_info(17 行,字段含账期/银行/省市);不因查不到编码而回退此表
oms_inventory_inner_detail 1 不采用:定义上是入库产品行,但实测仅 1 行(未启用);入库明细以 oms_inventory_info(按 inner_code)为准

实现要求:工具内以表白名单方式声明可访问表(而非黑名单),新增 entity 必须显式登记;oms_inventory_info_copy1 行数与正式表同量级,误用后果严重。

15.13 测试数据与压测方案(原文缺失)

  • 边界数据:部分入库的采购单、多仓发货的出库单、已签收/未签收的 manage 域发货单、超期未收的应收单、已撤回的发货单、tax_rate 为 NULL 的 SN、软删除(deleted_at 非空)的发货明细;
  • 压测用例:inventory_sn_trace(50 SN)、warehouse_list(entity=SN)(连续翻页 1 万行)、三个聚合工具的 SUMMARY(group_by=NONE 与 top_n=10),逐一记录 P95;
  • 并发验证:模拟 3 个并发 bot 调用,确认 Druid maxActive=20 不被占满、限流与超时按预期触发。

15.14 编码与枚举的实测异常(v11 新增)

① 编码字段存在脏数据(前导制表符)

  • 实测:order_info.order_code 有 14 行以制表符 \t 开头(project_order_info 为 0 行);
  • 影响:直接 = 或 join 会漏关联;且 utf8mb4_unicode_ci 下部分控制字符被折叠,导致同一条件下 EXISTS 与 LEFT JOIN 结果不一致(实测 EXISTS 命中 330 行,而 LEFT JOIN 取前 5 行均为 null);
  • 处理:所有编码比对与 join 一律 trim();返回结果中的编码也 trim() 后输出,避免 Agent 看到脏值。

② 枚举实际值与列注释不符

  • order_info.order_type 列注释为"1-直签合同,2-代理商合同",实测实际值是 zq(205) 与 dls(161)(推测 zq=直签、dls=代理商,需业务确认);
  • 处理:orderTypeName 按实际值 zq/dls 映射,不得按注释的 1/2;并在 metadata 标注"取值与列注释不一致,已按实测值映射";
  • order_info.status 实测 0(345)/1(21),与注释"0-有效,1-无效"一致 ✅;且 status=0 的行数恰等于 deleted_at is null 的 345 行 → 两者语义重合,工具按 deleted_at is null 过滤即可。

③ 两套订单模型只能部分对齐:order_info 去空白后仅 330/366 能命中 project_order_info → 必须容忍关联不到(详见 16.1)。

④ 通用规则(并入 15.5 的类型规范化):所有 varchar 编码的入参与出参统一 trim();发现脏数据不得静默忽略,需在 metadata.data_quality 提示(如 { "trimmed_codes": 14 })。

⑤ 实测补充(v14,实现期发现,务必遵守)

项 实测结论 处理
MySQL TRIM() 不去制表符 trim(order_code) in ('ZGXV-20250716SCS001') 命中 0 行,而 trim(replace(order_code,'\t','')) 命中 1 行 order_info 的编码比对/游标/排序三处统一用 trim(replace(order_code,'\t',''));Java 侧出参用 trim() 即可
order_delivery.delivery_status 取值为拼音缩写 实测 qs(324)/yf(31),不是列注释的 1/2/3 翻译按实测值:qs=已签收、yf=已发货,并保留数字 1/2/3 兜底
purchase_order_map.order_id 指向 project_order_info 实测 1116/1123 命中 project_order_info,仅 28 条巧合命中 order_info 该表的绑定关系 join project_order_info.id;注意与 order_delivery.order_id(→order_info)是两条不同链路
oms_inventory_info 无 purchase_no 列 列清单中不存在(Java 实体字段为派生值) SN 的采购单号经 inner_code 关联 oms_inventory_inner.purchase_no 批量补齐,不在 SN 表直取
oms_receivable_bill 无 plan_receipt_date 列 该列在 oms_receivable_receipt_plan 账龄分桶经 last_receipt_plan_id 关联收款计划取 plan_receipt_date;无计划的行归入 NO_PLAN 桶
/mcp 响应头 Transfer-Encoding: chunked 重复(既有问题) McpController 手工 setHeader("Transfer-Encoding","chunked") 后 Tomcat 再补一次 → 出现两个同名头,curl 会丢弃响应体(Python http.client 正常) v15 已修复:移除手工设置那一行;修复后 curl 可直接调试 /mcp(实测 HTTP 200 + 正常响应体)
java.time.LocalDateTime 序列化缺失(v15 实测) SQL 以 Map 返回时 DATETIME 列可能是 LocalDateTime(如 order_delivery.sign_time),McpController 的裸 ObjectMapper 未注册 jsr310 → InvalidDefinitionException,且被外层 catch 吞掉 → HTTP 200 + 空响应体(极难排查) v15 已修复:McpController 注册 java.time 序列化器(日期 yyyy-MM-dd、时间 yyyy-MM-dd HH:mm:ss,与工具约定一致);同时把外层 catch 改为回写 error 响应,不再静默吞异常
空列表导致 where col in ()(v15 实测) inventory_sn_trace 对未提供的入口也发起查询 → in (...) 空列表是非法 SQL(SQLSyntaxErrorException) v15 已修复:Java 侧仅对非空列表发起查询(同时减少无用查询)
SQL 引用不存在的列(v15 实测) 3 处:oms_inventory_outer.receivable_bill_code(列不存在)、oms_payable_bill.vendor_name(只有 vendor_code)、project_order_info.project_code/project_name(在 project_info 上) v15 已修复:分别删除该列、改经 oms_vendor_info 关联取名称、改用 project_info 的列。并新增系统性校验脚本:解析新增 select 的全部 别名.列 与 information_schema 比对(77 条语句 → 最终 0 处不存在列)

十六、扩展覆盖与工程完善(v9)

本章补齐上一版的 3 类遗留:① 覆盖缺口(跨域任意维度、项目进度/POC/报价);② 业务口径从"待确认"变为"可配置化落地";③ 工程项(索引 DDL 执行方案、时间区间策略、从库路由、RAG 工具路由)。

16.1 新增覆盖 A:项目 / POC / 进度 / 报价 / manage 域合同 → 新工具 project_list

为什么单独成工具:这属于"项目域",与仓储/采购/财务的单据结构差异大;塞进 master_data_list 会让 schema 更混乱。

入参:entity(必填)、code_list、project_id_list、time_range、name_like、stage_list、include_detail、page_size、cursor

entity 主表 关键返回字段 索引
PROJECT project_info(2,212) projectCode/projectName、customerCode/customerName、agentCode代表处、partnerCode/partnerName代理商、industryType行业、bgPropertyBG、projectStage项目阶段、projectGraspDegree把握度、estimatedAmount预计金额、currencyType、estimatedOrderTime/estimatedDeliverTime、competitor竞争对手、countryProduct是否国产、jointTrial是否会审、poc、hzSupportUser、operateInstitution、keyProblem、projectDesc unq_idx(project_code)、idx_customer_code、idx_agent_code ✅;按 partner_code 需 P2-24
PROJECT_PRODUCT project_product_info(2,809) productBomCode、model、productCode、productDesc、quantity、cataloguePrice目录价、catalogueAllPrice目录总价、price/allPrice、guidanceDiscount、discount、taxRate idx_project_id ✅;按产品编码跨项目查询需 P2-23
PROGRESS project_work_progress(7) projectId、workContent变更内容、workUser更新人、workTime更新时间 idx_project_id ✅
POC project_poc_info(998) + _detail(2) projectId、serverConfig、terminalConfig、operateSystem、vdiVersion、processPerson/processPhone研发、handlePerson/handlePhone现场、hzInterfacePerson/hzInterfacePhone、startDate、h3cPerson、planFinishTime/realFinishTime;明细 testProgress测试进展 仅 PRIMARY → 需 P2-21 (project_id)
QUOTATION oms_quotation(0) + oms_quotation_product_info(0) quotationCode报价单号、quotationName、quotationAmount/discountAmount、quotationStatus、agentCode、amountType币种、customerName;明细产品行同 PROJECT_PRODUCT 结构 仅 PRIMARY → 需 P2-22(两张表各 1 条)
CONTRACT(v11 新增) order_info(366) manage 域合同:orderCode合同编号、versionCode版本号、projectCode关联项目编号、orderName合同名称、customerCode/customerName/customerAddress/customerContact/customerPhone/customerEmail、orderType/orderTypeName合同类型、orderAgentCode代表处编码、bgTypeBG属性、orderPartnerCode代理商编码、orderDate合同签订日期、status/statusName合同状态、industryType一级行业、customerPostcode、remark、createdAt/updatedAt;默认过滤 deleted_at is null uk_order_code(order_code, version_code)(UK) ✅ 可按合同号查
CONTRACT_PRODUCT(v11 新增) order_list(854) orderId合同ID、productCodeBOM编码、quantity数量、price单价、discount折扣、amount总价、remark、createdAt/updatedAt、status数据状态;默认过滤 deleted_at is null idx_order_id、idx_product_code ✅

两套订单/合同模型的关系(v11 实测,务必按此实现)

体系 主表 明细 发货 说明
manage 域(销售/交付侧) order_info(366) order_list(854) order_delivery(355) + delivery_list(32,718) order_delivery.order_id 355/355 指向 order_info
项目域 project_order_info(809) project_product_info(2,809) — 由现有 project_order_info 工具与 project_list 覆盖
  1. 禁止按 id 跨体系关联:order_delivery.order_id 只能关联 order_info.id(按 id 关联 project_order_info 会有 242 条"假命中",属数值巧合); ⚠️ v14 补充:oms_purchase_order_map.order_id 恰好相反——实测 1116/1123 指向 project_order_info.id(仅 28 条巧合命中 order_info),故 purchase_list(entity=ORDER_BIND) 按 project_order_info join。两张表的 order_id 语义不同,切勿套用同一条链路。
  2. order_code 只能"部分对齐":去空白后 order_info 中 330/366 能命中 project_order_info,36 行命中不了 → 工具必须容忍关联不到,metadata 中标注"该合同在项目域无对应记录",不得报错也不得臆造;
  3. 对齐时必须 trim():order_info 有 14 行 order_code 带前导制表符(脏数据),不 trim 会漏关联(见 15.14)。

价值:补齐"按合同号点查订单/项目""这个项目的 POC 到哪一步了""报价单有哪些产品"等此前完全无法回答的问题(project_order_info 现有工具仅支持按创建时间范围查询)。

16.2 新增覆盖 B:跨域任意维度(客户 × 产品 × 月)→ 新工具 cross_domain_aggregate

先说结论:真正的"任意维度"不可能安全实现(等价于开放任意 SQL,性能与安全都不可控)。因此本方案提供的是受限透视(restricted pivot):维度与度量来自白名单,且度量必须属于同一条数据链路。

入参:dimensions(1–3 个,白名单)、metrics(白名单)、time_range(必填,≤36 个月)、time_granularity(MONTH/QUARTER)、top_n(默认 20,上限 200)、filters

维度白名单:PARTNER(客户/进货商)、CUSTOMER、AGENT(代表处)、PRODUCT、PROJECT、WAREHOUSE、MONTH、QUARTER

度量白名单与数据链路(关键:同链路才可混合)

链路 事实表 / 连接路径 可用度量
SALES project_product_info ⋈ project_order_info(→project_id、order_code、partner_code)⋈ project_info(→customer_code、agent_code) SALES_AMOUNT_WITH_TAX、SALES_AMOUNT_WITHOUT_TAX、SALES_QTY
PURCHASE oms_purchase_order_item ⋈ oms_purchase_order PURCHASE_QTY、PURCHASE_AMOUNT、PURCHASE_TAX
STOCK oms_inventory_info IN_STOCK_QTY、OUT_STOCK_QTY、INNER_AMOUNT
FINANCE_AR oms_receivable_bill RECEIVABLE_WITH_TAX、RECEIVED_WITH_TAX、UNRECEIVED_WITH_TAX、INVOICED_WITH_TAX、UNINVOICED_WITH_TAX
FINANCE_AP oms_payable_bill PAYABLE_WITH_TAX、PAID_WITH_TAX、UNPAID_WITH_TAX、TICKETED_WITH_TAX、UNTICKETED_WITH_TAX

典型问题映射:

  • "客户 × 产品 × 月 的销售额" → dimensions=[CUSTOMER, PRODUCT, MONTH]、metrics=[SALES_AMOUNT_WITH_TAX]、链路 SALES;
  • "代表处 × 月 的未收款" → dimensions=[AGENT, MONTH]、metrics=[UNRECEIVED_WITH_TAX]、链路 FINANCE_AR。

护栏(必须):

  1. time_range 必填(否则拒绝),跨度 ≤ 36 个月 → 保证走时间区间下推而非全表;
  2. 至少一个维度有索引支撑;否则要求 allow_full_scan=true 显式确认(默认拒绝);
  3. 度量跨链路混用直接报错(如同时要 SALES_AMOUNT_WITH_TAX + UNRECEIVED_WITH_TAX);如需,须分两次调用;
  4. 分组数上限 max_groups=1000,超出报错并提示收窄;
  5. mode=SUMMARY(默认) 返回 Top-N + 全局合计;mode=LIST 走游标分页,且维度必须与索引顺序一致(沿用 15.2 规则);
  6. 结果必须带 metadata.lineage(说明本结果来自哪条链路与哪些表),避免 Agent 误读口径。

成本提示:SALES 链路是 3 表 join + 分组,是本方案最重的查询;实测规模下预计 数百 ms 级,在百万行级需要 project_order_info(order_code)(已有)与 project_product_info(project_id)(已有)支撑;时间维度需 P2-25。文档中必须标注"该工具为兜底能力,优先使用域内专用工具"。

16.3 业务口径:从"待确认"改为"可配置化落地"(解决 15.7 / 15.8 阻塞)

⚠️ 2026-09-23 状态更新:本节设计已作废(不予采用)。曾按本节实现 mcp.inventory.stock-basis / price-basis 配置项(application.yml + @ConfigurationProperties + 3 个聚合查询的 <choose> 分支 + 工具参数覆盖),经确认不引入配置机制,已全部回滚。当前实际状态:口径写死在 SQL 与 metadata 声明中,业务确认后需改代码(见第十二章遗留 9/10)。本节以下内容仅作为历史设计记录保留。

原则:口径不确定时不阻塞编码,而是做成配置项 + 参数 + 校验 SQL,业务确认后改配置即可,不改代码。

口径 配置项(application.yml) 工具参数(可选覆盖) 元数据行为 业务校验 SQL(可直接执行)
在库(15.7) mcp.inventory.stock-basis: IN_STOCK_ONLY(默认) / EXCLUDE_DELIVERED stock_basis metadata.aggregation_rule 写明当前口径与"是否扣除已发货占用" ① select count(*) from oms_inventory_info where inventory_status='0';② 扣除占用:... and product_sn not in (select d.product_sn from oms_inventory_delivery_detail d join oms_inventory_delivery m on m.id=d.delivery_id where m.delivery_status in ('0','1')) → 两者差值即"占用量",据此判断口径
价格含税(15.8) mcp.inventory.price-basis: UNKNOWN(默认) / WITH_TAX / WITHOUT_TAX — 仅当配置为 WITH_TAX/WITHOUT_TAX 时,元数据才声明含税/未税;UNKNOWN 时字段注释只写"入库价/出库价" 取同一 SN 与采购单明细比对:select i.product_sn, i.inner_price, pi.amount_total/pi.quantity as po_unit_price, pi.tax_rate from oms_inventory_info i join oms_purchase_order_item pi on pi.product_code=i.product_code limit 20 → 若 inner_price ≈ po_unit_price 则为含税,若 inner_price ≈ po_unit_price/(1+tax_rate) 则为未税

这两条把"业务确认"从阻塞项降级为配置项,可立即进入编码;上线前由业务跑一次校验 SQL 决定配置值。

16.4 索引 DDL 执行方案(执行项 1)

执行顺序与窗口

阶段 内容 窗口 影响
预检 在生产/正式测试库执行 SHOW INDEX FROM <table> 复核 2.5 节结论;确认无同名索引 任意 只读
第 1 步 P0 两条(oms_inventory_outer.outer_code、oms_inventory_outer_detail.outer_code) 低峰 表小(734/750 行),秒级
第 2 步 P1-1、P1-2(oms_inventory_inner.order_code、oms_purchase_order.vendor_id) 低峰 表小,秒级
第 3 步 P1-3 覆盖索引(oms_inventory_info 6.5 万行) 低峰,避开入库高峰 在线 DDL,ALGORITHM=INPLACE, LOCK=NONE;需评估写入放大
第 4 步 P2 系列 按触发条件(表 > 10 万行)再执行 —

统一 DDL 形态(含在线参数)

ALTER TABLE <table> ADD INDEX <idx_name> (<cols>), ALGORITHM=INPLACE, LOCK=NONE;

体积与耗时估算方法(执行前评估,不靠猜)

  • 索引体积 ≈ 行数 × (键长 + 主键长 + 约 15 字节开销)。P1-3 键长约 product_code(≤255, 实测样例 8~12 字符) + inventory_status(1) + 2×decimal(10,2) ≈ 60–80 字节 → 6.5 万行约 5–8 MB;百万行约 80–120 MB(需与 DBA 确认磁盘)。
  • 在线加索引耗时 ≈ 全表扫描 + 排序一次的量级;oms_inventory_info 单次全表聚合实测 ~40ms,故预计 秒级~分钟级。

验证与回滚

  • 验证:SHOW INDEX FROM <table> WHERE Key_name='<idx>' + 对目标语句 EXPLAIN 确认 key 命中且 type 非 ALL;
  • 回滚:ALTER TABLE <table> DROP INDEX <idx_name>, ALGORITHM=INPLACE, LOCK=NONE;(随时可执行)。

16.5 时间维度区间的可配置策略(执行项 2)

配置项 默认 说明
mcp.aggregate.time-range.default-months 12 未传 time_range 时的默认窗口
mcp.aggregate.time-range.max-months 36 请求上限,超出报 INVALID_PARAMS
mcp.aggregate.time-range.large-window-threshold 24 超过该值且涉及大表时,要求显式 allow_large_scan=true

结论:不放开默认区间。若业务确需 3 年以上历史,两条路径①先加 P2-12/P2-10 时间索引(推荐)②在加了索引前以 allow_large_scan=true 承担扫描成本。

16.6 从库路由实现方案(执行项 3)

项目已具备能力(实测确认):DynamicDataSource、DataSourceType、@DataSource、DataSourceAspect、DynamicDataSourceContextHolder;application-dev.yml 中 slave.enabled=false(默认关闭)。

实现方式(推荐:工具内显式切换,而非依赖 AOP)

  • 原因:@DataSource 是 AOP 注解,只在 Spring Bean 方法调用上生效;MCP 工具直接调 Service/Mapper,用注解易失效;
  • 做法:在公共基类中按配置决定是否切换:
if (readonlyRouteEnabled(toolName)) {
    DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE);
    try { ...执行查询... }
    finally { DynamicDataSourceContextHolder.clearDataSourceType(); }   // 必须清理,线程池复用会串库
}

路由白/黑名单(关键约束)

允许走从库 禁止走从库
inventory_sn_trace、inventory_flow、inventory_stock_aggregate、warehouse_list、purchase_*、project_list、master_data_list 所有财务类工具(finance_*、finance_order_position、finance_balance_aggregate、cross_domain_aggregate 的 FINANCE_* 链路)—— 金额不允许受主从延迟影响
  • 配置:mcp.datasource.readonly-route.tools: <逗号分隔白名单>,enabled: false 默认全走主库;
  • 前置条件:需 DBA 提供可用从库并确认延迟指标(建议延迟 > 1s 时自动降级回主库,通过定时探活实现);
  • 收益/代价:把"统计类聚合"从主库剥离;代价是读到的数据可能滞后,需在 metadata 标注 data_source: SLAVE。

16.7 RAG 工具路由(补上 prompt.md 未实现部分)

现状问题:tools/list 全量下发 12 个工具(10 新 + 2 现),每轮 schema token 高、模型选错率上升。

实现方案(新增 3 个类,放在 com.ruoyi.sip.llm)

类 职责
ToolEmbedding toolName / description / embedding(double[])
ToolRetriever @PostConstruct 时从 McpToolRegistry.list() 建内存索引;提供 retrieve(query, topK)
ToolRouter 接收用户问题 → 调 retriever → 返回候选工具集(不返回全量,默认 topK = 5)

中文场景下的向量化(关键,不能用英文分词照搬)

  • 分词:中文用字符 2-gram("库存汇总" → 库存/存汇/汇总)+ 英文/编码按空格与驼峰切分;
  • 加权:关键词命中加权(同义词表)+ TF-IDF 余弦相似度;不依赖外部 embedding 服务(满足 prompt.md 的"保证可运行"要求);
  • 同义词表配置化(mcp.router.synonyms,映射"词 → 工具名"),便于新增工具时零改码:
    • 库存/存货/在库/结存/备货 → inventory_stock_aggregate、inventory_sn_trace
    • 发货/物流/签收 → warehouse_list(entity=ORDER_DELIVERY)
    • 欠款/未收/未付/账龄/超期 → finance_balance_aggregate
    • 项目/立项/POC/会审/报价 → project_list
    • 主数据/编码名称/客户/供应商 → master_data_list

接入点(向后兼容,不破坏现有客户端)

位置 行为
tools/list 若请求带 params.query → 只返回 topK;不带则仍返回全量(现有客户端不受影响)
tools/list 新增 params.detail=false → 只返回 name + description(精简 schema),需要完整 inputSchema 时置 true
tools/call 未指定 name 时 → 自动路由;若 top1 分数 < min-score(默认 0.15)→ 返回候选列表并报 INVALID_PARAMS,而不是乱选
新增 tools/route 只做路由不做执行,便于调试与观测

质量要求:每个工具的 description 必须含"领域词 + 业务词 + 别名"(如 inventory_stock_aggregate 描述需出现"库存、存货、在库、结存"),否则检索命中率上不去。

验收:准备 20 条中文真实问题,要求路由命中率 ≥ 80%,且 tools/list 带 query 时返回的 schema token 下降 ≥ 50%。

16.7.1 路由配置示例(含中文 2-gram 同义词表)

放在 application.yml,新增工具只改配置、不改代码。值支持两种写法:tool_name 或 tool_name#entity;后者会在路由结果里作为 suggested_args 返回(例如命中"签收" → warehouse_list + entity=ORDER_DELIVERY)。

# ===== MCP 工具路由(RAG)配置 =====
mcp:
  router:
    enabled: true
    top-k: 5                 # 只返回候选工具,不返回全量
    min-score: 0.15          # 低于该分数不硬选:返回候选并报 INVALID_PARAMS

    # ---------- 检索算法参数 ----------
    text:
      char-ngram: 2          # 中文按字符 2-gram 切分:"库存汇总" -> 库存/存汇/汇总
      lowercase: true        # 英文/编码统一小写(编码实际为 utf8mb4_unicode_ci,大小写不敏感)
      split-camel: true      # 驼峰切分:inventoryStockAggregate -> inventory/stock/aggregate
      token-min-length: 2

    weight:                  # 命中加权(乘到 TF-IDF 得分上)
      alias: 3.0             # tool-aliases 命中(人工别名,最可信)
      synonym: 2.5           # synonyms 命中
      tool-name: 2.0         # 工具名命中
      description: 1.0       # 描述 TF-IDF 基础权重

    # ---------- 停止词(不参与向量化) ----------
    stopwords: [的, 了, 和, 与, 及, 或者, 是, 在, 有, 我, 你, 他, 请, 帮, 帮我, 查, 查询, 看,
                看一下, 多少, 几个, 哪些, 什么, 怎么, 如何, 以及, the, a, an, of, for, to, and]

    # ---------- 工具别名(tool -> 词,直接拼进该工具的检索文本) ----------
    tool-aliases:
      project_order_info: [订单, 合同, 合同编号, 项目订单, 订单台账, 下单, 订单状态, 归档]
      product_info: [产品, 物料, 型号, 产品编码, 目录价, 指导折扣]
      inventory_sn_trace: [序列号, SN, 条码, 机身码, 单件, 追溯, 在库, 已出库, 入库价, 出库价]
      inventory_flow: [货流, 流转, 单据链, 这条货走到哪, 入出库关联, 物流轨迹, 备货状态]
      finance_order_position: [一单到底, 全链路, 收付票, 这单钱到哪一步, 核销情况, 计收, 毛利]
      inventory_stock_aggregate: [库存, 存货, 在库, 结存, 库存量, 库存汇总, 库存排行, 占用]
      purchase_arrival_aggregate: [采购汇总, 到货率, 到货及时率, 未入库, 在途采购, 采购金额]
      finance_balance_aggregate: [欠款, 未收, 未付, 余额, 账龄, 超期, 应收未收, 应付未付, 未开票, 未收票]
      warehouse_list: [入库单, 出库单, 发货单, 物流, 签收, 撤单, 撤回记录, 备货]
      purchase_list: [采购单, 采购订单, 采购明细, 供应商, 制造商, 采购变更, 采购历史]
      finance_list: [应收单, 应付单, 收款单, 付款单, 发票, 收票, 核销单, 计收单, 单据明细]
      master_data_list: [主数据, 编码转名称, 名称对照, 客户, 进货商, 代理商, 代表处, 办事处, 系统用户]
      project_list: [项目, 立项, 项目进度, POC, 试点, 会审, 报价, 报价单, 项目清单, 把握度]
      cross_domain_aggregate: [交叉分析, 透视, 组合分析, 客户产品, 按客户按产品, 按代表处, 多维]

    # ---------- 同义词表(用户口语 -> 工具/实体,用于加权) ----------
    synonyms:
      # ===== 仓储 =====
      库存: [inventory_stock_aggregate, inventory_sn_trace]
      存货: [inventory_stock_aggregate]
      在库: [inventory_stock_aggregate, inventory_sn_trace]
      结存: [inventory_stock_aggregate]
      占用量: [inventory_stock_aggregate]
      条码: [inventory_sn_trace]
      序列号: [inventory_sn_trace]
      SN: [inventory_sn_trace]
      扫码: [inventory_sn_trace]
      入库单: [warehouse_list#INNER, inventory_flow]
      到货入库: [warehouse_list#INNER, purchase_arrival_aggregate]
      出库单: [warehouse_list#OUTER, inventory_flow]
      发货单: [warehouse_list#DELIVERY, inventory_flow]
      签收: [warehouse_list#ORDER_DELIVERY]
      收货: [warehouse_list#ORDER_DELIVERY]
      物流: [warehouse_list#ORDER_DELIVERY, warehouse_list#DELIVERY]
      快递单号: [warehouse_list#ORDER_DELIVERY]
      撤回: [warehouse_list#RECALL, warehouse_list#DELIVERY]
      撤单: [warehouse_list#RECALL]
      备货: [warehouse_list#STOCK, inventory_flow]
      货流: [inventory_flow]
      流转: [inventory_flow, warehouse_list]

      # ===== 采购 =====
      采购单: [purchase_list#ORDER, purchase_arrival_aggregate]
      采购订单: [purchase_list#ORDER]
      采购明细: [purchase_list#ITEM]
      采购变更: [purchase_list#HISTORY]
      采购历史: [purchase_list#HISTORY]
      到货率: [purchase_arrival_aggregate]
      到货及时: [purchase_arrival_aggregate]
      未入库: [purchase_arrival_aggregate, purchase_list#ORDER]
      在途采购: [purchase_arrival_aggregate]
      供应商: [master_data_list#VENDOR, purchase_list#ORDER]
      制造商: [master_data_list#VENDOR, purchase_list#ORDER]
      账期: [master_data_list#VENDOR]

      # ===== 财务 =====
      应收: [finance_list#RECEIVABLE, finance_balance_aggregate]
      应付: [finance_list#PAYABLE, finance_balance_aggregate]
      收款: [finance_list#RECEIPT]
      回款: [finance_list#RECEIPT, finance_balance_aggregate]
      付款: [finance_list#PAYMENT]
      开票: [finance_list#INVOICE]
      发票: [finance_list#INVOICE]
      收票: [finance_list#TICKET]
      核销: [finance_list#RECEIPT, finance_list#PAYMENT, finance_order_position]
      计收: [finance_list#CHARGE, finance_order_position]
      毛利: [finance_order_position, finance_list#CHARGE]
      欠款: [finance_balance_aggregate]
      未收: [finance_balance_aggregate]
      未付: [finance_balance_aggregate]
      账龄: [finance_balance_aggregate]
      超期: [finance_balance_aggregate]
      未开票: [finance_balance_aggregate]
      未收票: [finance_balance_aggregate]

      # ===== 项目 / 报价 =====
      项目: [project_list#PROJECT]
      立项: [project_list#PROJECT]
      项目进度: [project_list#PROGRESS]
      把握度: [project_list#PROJECT]
      试点: [project_list#POC]
      POC: [project_list#POC]
      会审: [project_list#PROJECT, project_list#POC]
      报价: [project_list#QUOTATION]
      报价单: [project_list#QUOTATION]

      # ===== 主数据 =====
      客户: [master_data_list#CUSTOMER, master_data_list#PARTNER]
      进货商: [master_data_list#PARTNER]
      代理商: [master_data_list#PARTNER]
      代表处: [master_data_list#AGENT]
      办事处: [master_data_list#AGENT]
      产品: [master_data_list#PRODUCT, product_info]
      型号: [master_data_list#PRODUCT, product_info]
      编码转名称: [master_data_list]
      名称对照: [master_data_list]
      系统用户: [master_data_list#USER]

      # ===== 跨域 / 组合分析 =====
      透视: [cross_domain_aggregate]
      组合分析: [cross_domain_aggregate]
      按客户按产品: [cross_domain_aggregate]
      交叉分析: [cross_domain_aggregate]
      多维: [cross_domain_aggregate]

      # ===== 订单(现有工具) =====
      订单: [project_order_info]
      合同: [project_order_info]
      合同编号: [project_order_info]

    # ---------- 路由回归用例(自动化验收:命中率 ≥ 80%) ----------
    test-cases:
      - { q: 这批货走到哪了,出库了吗,            expect: inventory_flow }
      - { q: 这个SN现在在库还是已经出库,          expect: inventory_sn_trace }
      - { q: 某产品还有多少库存在哪个仓库,        expect: inventory_stock_aggregate }
      - { q: 这个月有哪些采购单还没入库,          expect: purchase_list#ORDER }
      - { q: 上个月采购到货率怎么样,              expect: purchase_arrival_aggregate }
      - { q: 哪些订单还欠钱,账龄超过90天的,      expect: finance_balance_aggregate }
      - { q: 这单收了多少款、开了多少票、计收了没, expect: finance_order_position }
      - { q: 本月开了多少发票、收了多少款,        expect: finance_list#INVOICE }
      - { q: 这个月发货单有哪些没签收,            expect: warehouse_list#ORDER_DELIVERY }
      - { q: 这个项目的POC到哪一步了,             expect: project_list#POC }
      - { q: 报价单有哪些产品,                    expect: project_list#QUOTATION }
      - { q: 这些客户编码分别叫什么名字,          expect: master_data_list#CUSTOMER }
      - { q: 按客户和产品统计每个月的销售额,      expect: cross_domain_aggregate }
      - { q: 合同 ZGXV-20260313GDS001 的订单信息, expect: project_order_info }
      - { q: 9801H0BC 这个产品的目录价是多少,     expect: master_data_list#PRODUCT }

配置消费方式(Java 侧绑定)

@Component
@ConfigurationProperties(prefix = "mcp.router")
public class McpRouterProperties {
    private boolean enabled = true;
    private int topK = 5;
    private double minScore = 0.15;
    private Text text = new Text();
    private Map<String, Double> weight = new HashMap<>();
    private List<String> stopwords = new ArrayList<>();
    private Map<String, List<String>> toolAliases = new HashMap<>();
    private Map<String, List<String>> synonyms = new HashMap<>();
    private List<TestCase> testCases = new ArrayList<>();
    // getter/setter 省略;RuoYi 常用写法,与 @ConfigurationProperties 一致
}

检索流程:query → 去停止词 → 字符 2-gram + 英文分词 → 与「工具名 + 描述 + tool-aliases」的 TF-IDF 向量做余弦 → 叠加 synonyms 命中加权(按 weight.synonym)→ 取 topK;tool#entity 命中时在结果中附 suggested_args。

冲突与兜底规则

  1. 一个词命中多个工具时按累计权重排序,取 topK(默认 5);
  2. top1 分数 < min-score → 不硬选,返回候选列表并报 INVALID_PARAMS;
  3. #entity 只作参数建议,不替代 cross_domain_aggregate 等工具的必填参数校验(仍需工具自身护栏拦截)。

维护约定:新增工具时,只需在 tool-aliases 与 synonyms 各补一行,并加 1–2 条 test-cases;回归用例随 CI 跑,命中率跌破 80% 即告警。

16.9 剩余缺口的影响评估与闭合方案(v12 分析 / v13 已全部采纳)

v13 状态:#1 审批待办/已办、#2 账龄分桶、#3 财务历史时点余额 已全部纳入(见 4.3、9 章、A.15);#4 附件元数据亦已纳入(finance_list(entity=ATTACHMENT))。下表保留作为决策依据与实现口径。

先纠正一处会误导的地方:14.1 / 14.2 的 ❌ 是 v6 快照;v7–v11 已闭合其中 13 项,加上 v13 的 4 项,当前三大域已无实质缺口。

# 剩余缺口 不补的影响 闭合方案(实测) 成本 建议
1 审批待办 / 已办(bu_todo 61、bu_todo_completed 5,876) 答不了"我还有哪些单要审""现在卡在谁那儿""为什么被驳回""审批耗了多久"。现有 approve_status/approve_node 只给状态与节点,给不出审批人与意见 实测发现:两张业务表已冗余所需字段,无需碰 Flowable 的 act_*(2.2 万行、结构风险):bu_todo 有 business_key(业务主键)、process_key、task_name(节点)、approve_user_name(审批人)、apply_user_name(发起人)、apply_time;bu_todo_completed 另有 approve_time、approve_opinion(审批意见)、approve_status(3=通过 / 2=驳回)、all_approve_user_name。已覆盖流程:order_approve_online/offline、purchase_order_online、finance_payment、fianance_ticket(原文拼写)、order_reback、outer_reback —— 正好覆盖采购、财务付款/收票、仓储撤回。建议新增工具 approval_list(entity = TODO / DONE),按 approve_user(当前登录人) + process_key + business_key 过滤,游标分页 低(索引:bu_todo 仅主键但仅 61 行;bu_todo_completed 有 idx_business_key,5,876 行全表扫亦可接受) 建议纳入
2 账龄分桶(0-30 / 31-60 / 61-90 / 90+) "超 90 天未收有多少"无法直接统计;只有 OVERDUE_DAYS 度量,Agent 得自己绕 finance_balance_aggregate 增加 group_by=OVERDUE_BUCKET(基于 plan_receipt_date 与今天的差额分桶;仅对 unreceived_amount > 0 的行计数) 零 建议纳入
3 财务历史时点余额(原运营报表能力) 答不了"上月末未收款 vs 本月"的对比;finance_balance_aggregate 只给当前值(冗余列不支持时间点回溯) 可按明细重算:应收总额(截至T) = Σ receivable_bill.total_price_with_tax where create_time ≤ T;已收(截至T) = Σ receipt_detail.receipt_amount where receipt_time ≤ T → 时点未收 = 两者差。参数 as_of_date;须在 metadata 标注"重算口径,与冗余列当前值口径不同" 中(receipt_detail.receipt_time 无索引,大表需补 P2) 视业务是否需要"历史时点"
4 财务附件 答不了"这笔付款的凭证/发票影像是什么" 只提供附件元数据:fileName/fileSize/fileType/priceWithTax/relatedBillType + 关联单据;不提供文件内容与下载(MCP 返回文本,不做二进制传输)。实测需 过滤 del_flag='0'(80/82 有效),且 related_bill_type 实际值是 payment(59) / ticket(23),与列注释写的 PAYMENT_BILL_RECEIPT 等不符,翻译须按实测值 低 可选

另有 2 项不是缺口,而是主动决策:

项 说明 将来若需要
OWNER(负责人/销售维度) 因无索引 + 业务价值未确认而移除 补 oms_purchase_order.owner_name、project_order_info.duty_name 索引后即可放开,成本低
报表导出(Excel) MCP 只返回 JSON,不产出文件 可先支持 format=csv_text(返回可粘贴的文本表格);真正的文件导出另立项

其余"不做"项的影响(已在 14.8 声明):审计日志(project_operate_log 3,834 行)→ 答不了"谁改过这单",如需要也可低成本只读;维保入库(0 行)无影响。

影响分级结论

级别 项 说明
影响大、建议补 #1 审批待办/已办、#2 账龄分桶 分别是"业务办理"与"财务催收"的高频问法,且成本都低;#1 会让工具数 12 → 13(有 RAG 路由后 token 可控)
影响中、需业务确认 #3 历史时点余额 取决于是否需要"期末对比";可按明细重算实现,但属不同口径
影响小 / 主动不做 #4 附件元数据、OWNER、报表导出、审计日志 均为边缘场景

16.8 工具清单最终收敛(v13:13 个)

类 数量 工具
A 标识符点查 3 inventory_sn_trace、inventory_flow、finance_order_position
B 聚合(SUMMARY 默认) 3 inventory_stock_aggregate、purchase_arrival_aggregate、finance_balance_aggregate
C 列表 / 范围 5 warehouse_list、purchase_list、finance_list、master_data_list、approval_list(v13)
D 扩展 2 project_list、cross_domain_aggregate

token 控制:13 个工具下,RAG 路由(16.7)从"建议"升级为"必须";否则每轮 schema 体积不可接受。落地顺序上,tools/list 的 query 过滤与精简描述应与 D 类工具同期上线。


附录 A、字段字典(字段注释)

说明:本附录是字段注释的唯一权威来源,实现时须逐字段同步到 metadata.item_fields(中文),写法对齐现有 ProjectOrderInfoToolProvider#buildItemFieldMetadata()。 命名约定:入参 snake_case,返回 camelCase。 取值翻译分两类来源,已逐字段标注:①字典表(DictUtils.getDictLabel(dictType, code));②Java 枚举(XxxEnum#getValue())。 本附录字段均取自实际 domain / Mapper XML;凡未逐值核实的一律标注"待同步",不臆造。

A.0 通用入参(所有分页工具共有)

入参 类型 必填 中文注释
page_size int 否 每页条数;聚合类默认 20 / 上限 200,明细类默认 20 / 上限 100
cursor string 否 上一页返回的 next_cursor;首页不传;与 page 互斥
page int 否 页码(兼容用,内部转 OFFSET,仅数据不变时稳定,不推荐)
include_total bool 否 是否统计总条数;默认 false;true 时受 count_cap=50000 限制

A.1 inventory_sn_trace(SN 明细·点查)

入参

入参 类型 必填 中文注释
product_sn_list array<string> 三选一 产品序列号/条码列表,≤50(命中唯一索引 unq_idx_sn)
inner_code_list array<string> 三选一 入库单号列表,≤20
outer_code_list array<string> 三选一 出库单号列表,≤20
inventory_status string 否 库存状态:0=在库,1=出库
warehouse_id int 否 仓库ID(低选择性,须与上述条件并用)

返回 items(主表 oms_inventory_info)

返回字段 类型 中文注释 来源列 翻译
productSn string 产品序列号/条码 product_sn —
productCode string 产品BOM编码 product_code —
model string 产品型号 关联 product_info.model —
productDesc string 产品描述 关联 product_info.description —
inventoryStatus string 库存状态编码 inventory_status —
inventoryStatusName string 库存状态名称 — 枚举 InventoryInfo.InventoryStatusEnum:0=入库,1=出库
innerCode string 入库单号 inner_code —
outerCode string 出库单号 outer_code —
orderCode string 合同编号 order_code 注意:SN 未出库时为空(实测为空的行数恰等于在库数量),按订单查在库货须经 inner_code → oms_inventory_inner.order_code
purchaseNo string 采购单号 purchase_no —
warehouseId int 仓库ID warehouse_id —
warehouseName string 仓库名称 关联 oms_warehouse_info.warehouse_name —
innerPrice decimal 入库价(含税口径待确认,见 15.8) inner_price —
outerPrice decimal 出库价(含税口径待确认,见 15.8) outer_price —
taxRate decimal 税率(实测存在 NULL,未税换算按 0 兜底) tax_rate —
payableBillCode string 对应应付单号 payable_bill_code —
createTime datetime 创建时间 create_time —
updateTime datetime 更新时间 update_time —

A.2 inventory_flow(单据流转链·点查)

入参:outer_code 或 order_code(二选一,必填)

返回:items[0] 为一条链,含 5 个分组:

① inner(入库单,oms_inventory_inner)

返回字段 中文注释 来源列
innerCode 入库单号 inner_code
purchaseNo 采购单号 purchase_no
productCode / productType / model 产品BOM编码 / 产品类型 / 型号 product_code / product_type / model
quantity 入库数量 quantity
vendorCode / vendorName 制造商编码 / 名称 vendor_code / 关联 oms_vendor_info
warehouseId / warehouseName / warehouseType 仓库ID / 名称 / 类型 warehouse_id / 关联 / warehouse_type
totalAmount / taxRate / taxTotal 入库含税总额 / 税率 / 税额 total_amount / tax_rate / tax_total
orderCode 合同编号 order_code
createTime 入库时间 create_time

② outer(出库单,oms_inventory_outer)

返回字段 中文注释 来源列
outerCode 出库单号 outer_code
orderCode 合同编号 order_code
productCode / model 产品BOM编码 / 型号 product_code / model
quantity 应发数量 quantity
deliveryTime 发货时间 delivery_time
outerStatus / outerStatusName 出库状态编码 / 名称 outer_status / 枚举 InventoryOuter.OuterStatusEnum:1=待确认,2=已确认,3=已接收,4=已退回
deliveryStatus / deliveryStatusName 发货状态编码 / 名称 delivery_status / 枚举 InventoryOuter.DeliveryStatusEnum:0=未发货,1=部分发货,2=全部发货,3=已撤回
receivableBillCode 对应应收单号 receivable_bill_code
createTime 创建时间 create_time

③ outerDetails(出库明细,oms_inventory_outer_detail)

返回字段 中文注释 来源列
outerCode 出库单号 outer_code
warehouseId / warehouseName 仓库ID / 名称 warehouse_id / 关联
quantity 出库数量 quantity
outerStatus 出库状态 outer_status

④ deliveries(发货单,oms_inventory_delivery + _detail)

返回字段 中文注释 来源列 / 翻译
outerCode 出库单号 outer_code
warehouseId / warehouseName 仓库ID / 名称 warehouse_id / 关联
logisticsCompany / logisticsCode 物流公司 / 物流单号 logistics_company / logistics_code
deliveryType / deliveryTypeName 发货方式 / 名称 delivery_type:1=快递,2=物流,3=自提
deliveryTime 发货时间 delivery_time
deliveryStatus / deliveryStatusName 发货状态编码 / 名称 delivery_status / 枚举 InventoryDelivery.DeliveryStatusEnum:0=待发货,1=已发货,2=撤回
approveStatus 撤回审批状态 approve_status
quantity 发货数量 quantity
createByName 发货人 create_by 关联 sys_user
detailCount SN 明细条数 oms_inventory_delivery_detail 计数
productSns SN 列表 oms_inventory_delivery_detail.product_sn

⑤ snDetails(SN 明细,oms_inventory_info):字段同 A.1。

⑥ stock(备货状态,oms_stock_info,v7 新增)

返回字段 中文注释 来源列 / 翻译
orderCode 订单编码(合同编号) order_code
stockStatus / stockStatusName 备货状态编码 / 名称 stock_status / 枚举 OmsStockInfo:0=未备货,1=已备货
onceInStock 是否一次备齐 once_in_stock
createTime 创建时间 create_time
deliveryTime 要求到货时间 关联 project_order_info.delivery_time
projectCode / projectName 项目编号 / 名称 关联 project_order_info → project_info
allQuantity 应备货总量 关联 project_product_info 汇总(非本表列)
notifier / notifierPhone / notifierAddress 通知人 / 电话 / 地址 关联 project_order_info

A.3 finance_order_position(订单财务全景·点查)

入参:order_code(必填)

分组 返回字段(中文注释) 来源表
receivable receivableBillCode应收单号、orderCode合同编号、inventoryCode出库/入库单号、partnerCode/partnerName客户编码/名称、productType/productCode产品类型/编码、totalPriceWithTax含税总价、totalPriceWithoutTax未税总价、taxRate税率、taxAmount税额、receivedAmount已收款金额、unreceivedAmount未收款金额、invoicedAmount已开票金额、uninvoicedAmount未开票金额、projectCode/projectName项目编号/名称 oms_receivable_bill
receiptPlans planReceiptDate计划收款日期、planAmount计划收款金额、planRate计划收款比例 oms_receivable_receipt_plan
invoicePlans 计划开票日期/金额/比例(字段名待同步 OmsReceivableInvoicePlan) oms_receivable_invoice_plan
receipts receiptBillCode收款单号、receiptStatus/receiptStatusName收款状态(枚举 OmsReceiptBill.ReceiptStatusEnum:-1=已退款,1=未付款,2=已付款,3=未退款)、actualReceiptTime实际收款时间、totalPriceWithTax含税金额、writeOffAmount核销金额、partnerName进货商、receiptMethod收款方式 oms_receipt_bill
receiptWriteOffs writeOffCode核销单号、writeOffType核销方式(AUTO=自动/USER=人工)、receiptBillCode收款单号、receivableBillCode应收单号、writeOffAmount核销含税金额、writeOffAmountWithoutTax核销未税金额、writeOffTaxAmount核销税额、writeOffTime核销时间 oms_receivable_write_off
invoices invoiceBillCode开票单号、invoiceStatus/invoiceStatusName开票状态(枚举 OmsInvoiceBill.InvoiceStatusEnum:-1=已红冲,1=未开票,2=已开票,3=未红冲)、actualInvoiceTime实际开票时间、invoicePriceWithTax开票含税金额、partnerName客户、approveStatus审批状态 oms_invoice_bill
payable payableBillCode应付单号、orderCode合同编号、inventoryCode入库/出库单号、vendorCode/vendorName制造商编码/名称、totalPriceWithTax含税总价、totalPriceWithoutTax未税总价、taxRate税率、taxAmount税额、paidPaymentAmount已付款金额、unpaidPaymentAmount未付款金额、receivedTicketAmount已收票金额、unreceivedTicketAmount未收票金额、planPaymentDate计划付款日期、planTicketDate计划收票日期 oms_payable_bill
paymentPlans / ticketPlans planPaymentDate/planAmount/planRate、planTicketDate/planAmount/planRate oms_payable_payment_plan / oms_payable_ticket_plan
payments paymentBillCode付款单号、paymentStatus/paymentStatusName付款状态(待同步 OmsPaymentBill.PaymentStatusEnum)、actualPaymentTime实际付款时间、totalPriceWithTax含税金额、writeOffAmount核销金额、payType付款类型(INNER_PAY/OUTER_PAY)、preResidueAmount预付单剩余额度 oms_payment_bill
tickets ticketBillCode收票单号、ticketStatus/ticketStatusName收票状态(枚举 OmsTicketBill.TicketStatusEnum:-1=已红冲,1=未收票,2=已收票,3=未红冲)、actualTicketTime实际收票时间、totalPriceWithTax含税金额、taxRate税率 oms_ticket_bill
paymentWriteOffs / ticketWriteOffs 同 receiptWriteOffs 结构(writeOffCode/writeOffType/金额三件套/writeOffTime) oms_payable_write_off / oms_payable_ticket_write_off
charge(v7 新增) orderCode合同编号、chargeStatus/chargeStatusName计收状态(枚举 OmsFinanceCharge.ChargeStatusEnum:0=等待收款,1=可申请计收,2=已申请计收,3=已完成计收)、bizChargeDate业务计收时间、financeChargeDate财务计收时间、incomeWithTaxTotal/incomeWithoutTaxTotal收入含税/未税、costSoftwareWithTax/WithoutTax软件成本、costHardwareWithTax/WithoutTax硬件成本、costSoftwareMaintWithTax/WithoutTax软件维保成本、costHardwareMaintWithTax/WithoutTax硬件维保成本、costProvinceServiceWithTax/WithoutTax省服务成本、costOtherWithTax/WithoutTax其他成本、grossProfit毛利(派生)、grossProfitRate毛利率(派生)、allCostWithoutTax成本合计(派生)、orderChannel下单通路、supplier供货商、partnerCode/partnerName进货商 oms_finance_charge(projectCode/projectName 为关联字段,表内无 project_code 列)

子列表(receipts/payments/invoices/tickets/writeOffs)超单页上限时返回该子列表的 sub_cursor,仅供该子列表翻页。

A.4 inventory_stock_aggregate(库存汇总·分页)

入参:product_code_list(≤20,可选,用于收窄)、inventory_status(可选)、include_warehouse_breakdown(bool,默认 true)

返回 items(主表 oms_inventory_info 聚合)

返回字段 类型 中文注释 口径
productCode string 产品BOM编码 product_code
productName string 产品名称 关联 product_info.product_name
inStockQty long 在库数量 count(*) where inventory_status='0'
outStockQty long 已出库数量 count(*) where inventory_status='1'
innerAmount decimal 入库金额合计(含税) sum(inner_price),NULL 计 0
outerAmount decimal 出库金额合计(含税) sum(outer_price),NULL 计 0
warehouses[] array 仓库拆分 见下

warehouses[] 子项:warehouseId仓库ID、warehouseName仓库名称、inStockQty该仓在库数量、outStockQty该仓已出库数量。

A.5 purchase_arrival_aggregate(采购到货汇总·分页)

入参:purchase_no_list(≤20,可选)、status/approve_status/confirm_status(可选)、vendor_id(可选,依赖 P1-2 索引)

返回 items(oms_purchase_order ⋈ oms_purchase_order_item)

返回字段 类型 中文注释 来源 / 翻译
purchaseNo string 采购单号 purchase_no
buyerName string 采购方名称 buyer_name
vendorId / vendorName long / string 制造商ID / 名称 vendor_id / 关联 oms_vendor_info.vendor_name
warehouseId / warehouseName long / string 入库仓库ID / 名称 warehouse_id / 关联(注意 resultMap 未映射,需工具内自行补齐)
purchaserName string 采购员 purchaser_name
ownerName string 汇智负责人 owner_name
purchaseDate date 采购日期 purchase_date
status / statusName string 采购状态编码 / 名称 status:0=待入库,1=部分入库,2=已完成
approveStatus / approveStatusName string 审批状态编码 / 名称 approve_status:0=草稿,1=审批中,2=已通过,3=驳回
confirmStatus / confirmStatusName string 供应商确认状态编码 / 名称 confirm_status:0=待确认,1=已确认,2=已驳回
payMethod / payMethodName string 付款方式 / 名称 pay_method:0=入库付款,1=出库付款
flowType string 线上/线下 flow_type:online/offline
currency string 币别 currency
totalAmount decimal 采购含税总金额 total_amount
purchaseQty decimal 采购数量合计 sum(item.quantity)
innerQty decimal 已入库数量合计 sum(item.inner_quantity)
pendingQty decimal 未入库数量合计 purchaseQty - innerQty
arrivalRate decimal 到货率(%) innerQty / purchaseQty,HALF_UP 2 位;分母 0 返回 0
amountTotal / taxTotal decimal 明细含税金额合计 / 税额合计 sum(item.amount_total) / sum(item.tax_total)

A.6 finance_balance_aggregate(财务余额聚合:SUMMARY 默认 / LIST 分页)

入参:order_code_list(≤20,可选)、include_zero(bool,默认 false,是否返回全零行)

返回 items(oms_receivable_bill / oms_payable_bill 按 order_code 聚合)

返回字段 类型 中文注释 口径(直接 sum 冗余列,不重算)
orderCode string 合同编号 order_code
projectCode / projectName string 项目编号 / 名称 关联 project_order_info → project_info
receivableWithTax decimal 应收含税总额 sum(total_price_with_tax)
receivableWithoutTax decimal 应收未税总额 sum(total_price_without_tax)
receivableTax decimal 应收税额 sum(tax_amount)
receivedWithTax decimal 已收含税金额 sum(received_amount)
unreceivedWithTax decimal 未收含税金额 sum(unreceived_amount)
invoicedWithTax decimal 已开票金额 sum(invoiced_amount)
uninvoicedWithTax decimal 未开票金额 sum(uninvoiced_amount)
payableWithTax decimal 应付含税总额 sum(total_price_with_tax)
payableWithoutTax decimal 应付未税总额 sum(total_price_without_tax)
payableTax decimal 应付税额 sum(tax_amount)
paidWithTax decimal 已付含税金额 sum(paid_payment_amount)
unpaidWithTax decimal 未付含税金额 sum(unpaid_payment_amount)
ticketedWithTax decimal 已收票金额 sum(received_ticket_amount)
unticketedWithTax decimal 未收票金额 sum(unreceived_ticket_amount)
overdueBucket(v13) string 账龄分桶:0-30/31-60/61-90/90+ bucket(datediff(今天, plan_receipt_date)),仅 group_by=OVERDUE_BUCKET 时返回
inBucketQty / inBucketAmount(v13) long / decimal 该桶内的单据数 / 未收金额合计 按桶聚合
asOfDate / basis(v13) date / string 时点日期 / 口径标记 传 as_of_date 时为 RECALCULATED@<date>(按明细重算);否则 CURRENT(冗余列当前值)

A.7 purchase_order_detail(采购单明细·分页)

v7 变更:该能力已合并入 purchase_list,调用方式为 entity=ORDER + code_list=[purchase_no...] + include_detail=true。本节字段字典继续作为 purchase_list(entity=ORDER) 的权威来源。

入参:purchase_no_list(≤20,必填)、status/approve_status/confirm_status(可选)

返回 items[0](单头,oms_purchase_order)

purchaseNo采购单号、buyerName/buyerAddress采购方名称/地址、vendorId/vendorCode/vendorName/vendorAddress制造商ID/编码/名称/地址、warehouseId/warehouseName入库仓库、purchaserName/purchaserMobile采购员/手机、ownerName汇智负责人、payMethod/payMethodName付款方式、currency币别、totalAmount含税总金额、taxRate税率、status/statusName采购状态、approveStatus/approveStatusName审批状态、approveTime审批时间、approveNode当前审批节点、confirmStatus/confirmStatusName供应商确认状态、purchaseDate采购日期、flowType线上线下、isVirtual是否虚拟单、version版本号、productCode/productModel(查询条件回显)。

返回 items[0].items(明细,oms_purchase_order_item)

返回字段 中文注释 来源列
purchaseId 采购单ID purchase_id
productCode / productModel / productType / productDescription 产品编码 / 型号 / 类型 / 描述 product_code / 关联 product_info
quantity 采购数量 quantity
innerQuantity 已入库数量 inner_quantity
pendingQuantity 未入库数量 quantity - inner_quantity
price 单价 price
taxRate 税率(%) tax_rate
taxTotal 税额 tax_total
amountTotal 含税金额 amount_total
deliveryDate 交货日期 delivery_date

A.8 finance_bill_detail(财务单据明细·分页)

v7 变更:该能力已合并入 finance_list,调用方式为 entity=<RECEIVABLE|PAYABLE|RECEIPT|PAYMENT|INVOICE|TICKET> + code_list=[bill_code...] + include_detail=true。本节字段字典继续作为 finance_list 的权威来源。

入参:bill_type(必填,枚举 RECEIVABLE/PAYABLE/RECEIPT/PAYMENT/INVOICE/TICKET)+ bill_code_list(≤20,必填)

单头字段(按 bill_type 选用)

bill_type 单头字段(中文注释) 主表
RECEIVABLE 同 A.3 的 receivable 分组全部字段 oms_receivable_bill
PAYABLE 同 A.3 的 payable 分组全部字段 oms_payable_bill
RECEIPT 同 A.3 的 receipts 分组全部字段 + receiptBillType收款单类型、remainingAmount剩余金额、receiptAccountName/receiptBankNumber收款账户 oms_receipt_bill
PAYMENT 同 A.3 的 payments 分组全部字段 + paymentBillType付款单类型、paymentMethod付款方式、refundedAmount/remainingRefundAmount退款金额、payableBillCode关联应付单号 oms_payment_bill
INVOICE 同 A.3 的 invoices 分组全部字段 + invoiceType票据类型、invoiceBillType开票单类型、partnerCode客户编码 oms_invoice_bill
TICKET 同 A.3 的 tickets 分组全部字段 + ticketType票据类型、ticketBillType收票单类型、vendorCode/vendorName制造商 oms_ticket_bill

明细/计划子表

bill_type 子表与关键字段
RECEIVABLE receiptPlans(planReceiptDate/planAmount/planRate)、receiptDetails(receiptTime收款时间、receiptAmount收款金额、receiptRate比例、receiptBillCode收款单号、receivableDetailType类型:1=正常收款/2=预收核销/3=退款、receiptAmountWithoutTax/receiptAmountTax)、invoiceDetails(invoiceTime/invoiceAmount/invoiceRate/invoiceBillCode/receivableDetailType:1=正常开票/3=红冲)
PAYABLE paymentPlans、paymentDetails(paymentTime/paymentAmount/paymentRate/paymentBillCode/payableDetailType)、ticketPlans、ticketDetails(actualTicketTime/paymentAmount/ticketBillCode/paymentAmountTax)
RECEIPT 经 oms_receivable_receipt_detail 关联的应收单号与核销金额;核销经 write_off_id → oms_receivable_write_off
PAYMENT 经 oms_payable_payment_detail 关联的应付单号与核销金额;核销经 write_off_id → oms_payable_write_off
INVOICE oms_receivable_invoice_detail(应收单关联)、oms_receivable_invoice_detail_item(开票商品行:productCode/productName/productModel/quantity/price/allPrice/taxAmount/taxRate)
TICKET 经 oms_payable_ticket_detail 关联的应付单号与核销金额;核销经 write_off_id → oms_payable_ticket_write_off

A.9 warehouse_list(仓储列表 / 范围查询,v7 新增)

通用入参:entity(必填)、code_list、status_list、time_range(begin/end)、warehouse_id_list、product_code_list、order_code、include_detail、page_size、cursor

entity 主表 返回字段
INNER oms_inventory_inner 同 A.2① 字段 + createByName入库人、remark备注;明细来自 oms_inventory_info(按 inner_code)——oms_inventory_inner_detail 实测仅 1 行、不采用(见 4.3)
OUTER oms_inventory_outer 同 A.2② 字段 + contactPerson/contactPhone/contactAddress、deliveryTimeType、versionCode
DELIVERY oms_inventory_delivery 同 A.2④ 字段(SN 列表在 include_detail=true 时返回)
ORDER_DELIVERY(v8 新增) order_delivery deliveryCode发货单号(唯一)、orderId关联合同ID、deliveryDate发货日期、deliveryType发货方式(1=快递,2=物流,3=自提)、logisticsCompany物流公司、logisticsCode物流单号、receiverName/receiverPhone/receiverAddress收货人/电话/地址、deliveryStatus/deliveryStatusName发货状态(1=待发货,2=已发货,3=已签收)、signTime签收时间、remark备注、createdAt/updatedAt;v11 新增 join 字段(orderId → order_info):orderCode合同编号、orderName合同名称、customerCode/customerName客户、orderAgentCode代表处、orderPartnerCode代理商;明细 include_detail=true → delivery_list 的 productCode/serialNumber(过滤 deleted_at is null)
STOCK oms_stock_info 同 A.2⑥ 字段
SN oms_inventory_info 同 A.1 字段(必须给出索引键,见 4.3 护栏)
RECALL(v8 新增) project_order_info_recall orderCode合同编号、versionCode版本号、operationVersion操作版本、createTime更新时间、createBy创建人ID(撤回历史追溯)

提示:order_delivery 表存在软删除列 deleted_at 与 status(数据状态),工具内必须默认过滤 deleted_at is null;customer_info 没有 partner_code 列,不能与 partner_info 直接关联。

include_detail=true 时追加:INNER → oms_inventory_inner_detail 产品行(见 4.3);OUTER → outerDetails(同 A.2③);DELIVERY → productSns(同 A.2④);ORDER_DELIVERY → delivery_list 的 SN;STOCK / SN / RECALL → 无明细。

A.10 purchase_list(采购列表 / 范围查询,v7 新增)

通用入参:entity(必填)、code_list、status_list、approve_status_list、confirm_status_list、time_range、vendor_id/vendor_code_list、product_code_list、include_detail、page_size、cursor

entity 主表 返回字段
ORDER oms_purchase_order 同 A.7 单头字段(include_detail=true 时附 A.7 明细行)
ITEM oms_purchase_order_item 同 A.7 明细字段 + purchaseNo 采购单号
ORDER_BIND oms_purchase_order_map orderId订单ID、purchaseId采购单ID、productCode产品编码、bindNum绑定数量(需 P2-1/P2-2 索引)
HISTORY oms_purchase_order_history(+_item_history) purchaseId原始采购单ID、purchaseNo、version版本号、status/approveStatus/confirmStatus(含 Name)、totalAmount含税金额、vendorName、ownerName、purchaserName、warehouseId、flowType、isVirtual、createTime/updateTime;明细:productCode/quantity/price/taxRate/taxTotal/amountTotal/deliveryDate/innerStatus

v8 变更:原 entity=VENDOR(供应商主数据)已移入 master_data_list(entity=VENDOR)(见 A.12),避免与主数据工具重复。

A.11 finance_list(财务列表 / 范围查询,v7 新增)

通用入参:entity(必填)、code_list、status_list、approve_status_list、time_range、partner_code_list/vendor_code_list、order_code、include_detail、page_size、cursor

entity 主表 返回字段
RECEIVABLE oms_receivable_bill 同 A.3 的 receivable 组
PAYABLE oms_payable_bill 同 A.3 的 payable 组
RECEIPT oms_receipt_bill 同 A.3 的 receipts 组(+ A.8 中 RECEIPT 的补充字段)
PAYMENT oms_payment_bill 同 A.3 的 payments 组(+ A.8 中 PAYMENT 的补充字段)
INVOICE oms_invoice_bill 同 A.3 的 invoices 组(+ A.8 中 INVOICE 的补充字段)
TICKET oms_ticket_bill 同 A.3 的 tickets 组(+ A.8 中 TICKET 的补充字段)
CHARGE oms_finance_charge 同 A.3 的 charge 组
ATTACHMENT(v13 新增) oms_fin_attachment(82) 仅元数据:fileName原始文件名、fileSize文件大小(字节)、fileTypeMIME类型、relatedBillId关联单据ID、relatedBillType单据类型(实测取值 payment(59)/ticket(23),与列注释不符)、priceWithTax/priceWithoutTax附件金额、createBy/createTime、remark;必须过滤 del_flag='0'(80/82 有效);不返回 filePath 与文件内容(不做下载)

include_detail=true 时按 entity 返回对应计划 / 明细 / 核销子表(字段见 A.8 的"明细/计划子表")。

A.12 master_data_list(主数据查询 / 批量编码翻译,v8 新增)

通用入参:entity(必填)、code_list(批量,≤200)、name_like、status_list、page_size、cursor

entity 主表 返回字段
PARTNER partner_info partnerCode进货商编码、partnerName进货商名称、level进货商等级(字典 identify_level)、systemUserId绑定系统用户ID
CUSTOMER customer_info customerCode客户编码、customerName客户名称(注意:本表无 partner_code 列,不能与 PARTNER 直接关联)
AGENT agent_info agentCode办事处编码、agentName办事处名称、province所在省、city所在市
VENDOR oms_vendor_info 同原 purchase_list(entity=VENDOR) 字段:vendorId/vendorCode/vendorName/vendorAddress/vendorUser/vendorPhone/vendorEmail、vendorStatus/vendorStatusName合作状态(0=正常合作,1=暂停合作)、warehouseId/warehouseName/ownWarehouseId、payType/payConfigDay付款方式与账期、payName/payBankNumber/payBankOpenAddress/bankNumber、socialCredit、province/city/generatedAddress
PRODUCT product_info productCode、productName、model型号、type/typeName产品类型、vendorCode/vendorName厂商、hzCode、cataloguePrice目录价、guidanceDiscount指导折扣、availableCount可用库存、cumulativeCount累计出货
USER sys_user userId、loginName登录名、userName姓名、deptId/deptName部门、email、phonenumber手机
WAREHOUSE(v10 新增) oms_warehouse_info(14 行) warehouseId仓库ID、warehouseCode仓库编码、warehouseName仓库名称、warehouseType/warehouseTypeName仓库类型(0=实体仓,1=虚拟仓)、warehouseStatus/warehouseStatusName状态(0=正常,1=停用)、address详细地址、managerName管理员、managerPhone管理员电话、managerEmail管理员邮箱、remark备注。默认只返回正常仓(与页面 selectOmsWarehouseInfoList 行为一致),需含停用仓时置 include_disabled=true(走 listAll)
COMPANY(v10 新增) oms_company_info(当前 0 行) id、companyCode公司编码、companyName公司名称、companyUser联系用户、companyEmail联系邮箱、companyPhone联系电话、companyAddress公司地址、payName账户名称、payBankNumber银行卡号、payBankOpenAddress银行开户行、bankNumber银行行号、socialCredit统一社会信用代码(己方主体信息,用于合同/财务场景)

VENDOR 只取 oms_vendor_info:库中另有一张同名近似的 vendor_info(5 行,仅含 P001/P002 且字段为精简版),经确认为历史/冗余表,明确不纳入(已加入 15.12 排除清单)。若出现 oms_vendor_info 查不到的编码,按"无此厂商"处理,不得回退查 vendor_info。

核心用途:批量编码 → 名称翻译。Agent 从其他工具拿到 partnerCode / vendorCode / productCode 列表后,一次调用取回名称与属性,避免逐个查询(此前是缺口:现有 product_info 工具只支持模糊单值匹配)。

索引支撑:partner_info.idx_partner_code、customer_info.idx_code(customer_code)、agent_info.idx_agent_code、product_info.uk_product_code(product_code,hz_code);oms_vendor_info(17 行)、sys_user(119 行) 可全表扫。按厂商过滤需 P2-20。

A.13 字典与枚举取值汇总

① 字典表(DictUtils.getDictLabel)

字典类型 dictType 用途 状态
order_status 订单状态 现有工具已在用
bg_type BG 属性 现有工具已在用
bg_hysy / bg_yys 行业(运营商/非运营商) 现有工具已在用
project_stage 项目阶段 现有工具已在用
currency_type 币种 现有工具已在用
identify_level 进货商类型 现有工具已在用

② Java 枚举(本轮已逐值核实)

枚举 取值
InventoryInfo.InventoryStatusEnum 0=入库,1=出库
InventoryOuter.OuterStatusEnum 1=待确认,2=已确认,3=已接收,4=已退回
InventoryOuter.DeliveryStatusEnum 0=未发货,1=部分发货,2=全部发货,3=已撤回
InventoryDelivery.DeliveryStatusEnum 0=待发货,1=已发货,2=撤回
InventoryDelivery.deliveryType 1=快递,2=物流,3=自提
OmsWarehouseInfo.WarehouseTypeEnum 0=实体仓,1=虚拟仓
OmsWarehouseInfo.WarehouseStatusEnum 0=正常,1=停用
OmsPurchaseOrder status / approveStatus / confirmStatus / payMethod / flowType 见 A.5 逐行
OmsReceiptBill.ReceiptStatusEnum -1=已退款,1=未付款,2=已付款,3=未退款
OmsInvoiceBill.InvoiceStatusEnum -1=已红冲,1=未开票,2=已开票,3=未红冲
OmsTicketBill.TicketStatusEnum -1=已红冲,1=未收票,2=已收票,3=未红冲
OmsReceivableReceiptDetail.receivableDetailType 1=正常收款,2=预收核销,3=退款
OmsReceivableInvoiceDetail.receivableDetailType 1=正常开票,3=红冲
OmsReceivableWriteOff.writeOffType / OmsPayableWriteOff.writeOffType AUTO=自动核销,USER=人工核销
OmsPaymentBill.payType INNER_PAY / OUTER_PAY
OmsStockInfo.stockStatus 0=未备货,1=已备货(v7 新增)
OmsFinanceCharge.ChargeStatusEnum 0=等待收款,1=可申请计收,2=已申请计收,3=已完成计收(v7 新增)
VendorInfo.vendorStatus 0=正常合作,1=暂停合作(v7 新增)

③ 待实现时从枚举类补齐(本轮未逐值核实,不得臆造)

OmsPaymentBill.PaymentStatusEnum、OmsPayablePaymentDetail.PayableDetailTypeEnum、OmsPayableTicketDetail.PayableDetailTypeEnum、OmsPayableTicketWriteOff.writeOffType、OmsInvoiceBill.invoiceBillType、OmsReceiptBill.receiptBillType、OmsPaymentBill.paymentBillType、OmsTicketBill.ticketBillType、OmsReceivableInvoicePlan 字段名。

A.13 字段注释的落地要求

  1. 每个工具的 metadata.item_fields 内容必须取自本附录,不允许遗漏或改写含义;
  2. 嵌套结构(outerDetails / deliveries / snDetails / warehouses / 各 subItems)在 metadata 中以下划线分隔的扁平键声明,例如 deliveries[].logisticsCode = "物流单号";
  3. 枚举/字典字段一律成对输出:xxx(编码)+ xxxName(名称),并在 metadata.dict_fields 声明来源;
  4. 金额字段统一 decimal,日期统一 yyyy-MM-dd,时间统一 yyyy-MM-dd HH:mm:ss(与现有工具一致)。

A.15 approval_list(审批待办 / 已办,v13 新增)

入参:entity(必填:TODO / DONE)、approve_user(默认当前登录用户ID)、process_key_list、business_key_list、time_range、page_size、cursor

entity=TODO(bu_todo,61 行)

返回字段 中文注释 来源列
todoId 流程ID todo_id
processInstanceId 流程实例ID process_instance_id
taskId 任务ID task_id
businessKey 业务主键(合同编号 / 采购单号等) business_key
processKey 流程KEY process_key
processName 流程名称 process_name
taskName 当前节点名称 task_name
approveUser / approveUserName 审批人ID / 姓名 approve_user / approve_user_name
applyUserName 发起人姓名 apply_user_name
applyTime 发起时间 apply_time
formKey 节点表单KEY form_key
extendField1/2/3 扩展字段 extend_field1/2/3

entity=DONE(bu_todo_completed,5,876 行):包含 TODO 的全部字段,另有:

返回字段 中文注释 来源列
approveTime 审批时间 approve_time
approveOpinion 审批意见 approve_opinion
approveStatus / approveStatusName 审批结果(3=通过,2=驳回) approve_status
allApproveUserName 所有审批人 all_approve_user_name

已覆盖的流程(实测 process_key 分布)

process_key 含义 待办 已办
order_approve_online 订单审批(线上) 37 2,978
order_approve_offline 订单审批(线下) 6 1,967
purchase_order_online 采购单审批 15 530
finance_payment 付款审批 3 429
fianance_ticket 收票审批 — 47
order_reback 订单撤回 — 29
outer_reback 出库撤回 — 18

⚠️ 两处实测异常,勿按常规推断:① fianance_ticket 是源码/数据里的拼写错误(少一个 n),匹配时必须按原样;② approve_status 的语义方向与常见枚举相反(3 才是通过、2 是驳回)。


十七、变更记录

版本 主要变化
v16(补齐 15.4 与 15.11 两处"设计有、代码无") ① 局部游标 sub_cursor 落地(此前只有 truncated_sub_lists 截断标注、无从续页 → 子列表超限即数据缺失):新增支持类 McpSubPage(协议/校验/切片/sub_page_info),inventory_flow(outerDetails/snDetails/deliveries)与 finance_order_position(receiptPlans/receiptDetails/invoicePlans/paymentPlans/paymentDetails/ticketPlans)各自实现 sub_list + sub_parent + sub_cursor + page_size;truncated_sub_lists 由"路径字符串"升级为 {list, parent, total, returned, next_cursor},调用方可直接回传续页。实测修正 3 处:(a)max_pages 由 20 提到 200——20×100=2000 行 < 实测单出库单 2682 条 SN 明细,原值会导致"截断 + 游标也取不完";(b)子列表统一在内存按主键 id 升序定序——部分子表 SQL 无 order by,否则跨调用会重复/漏数据;(c)修复"从 sub_parent 起翻时下一页游标丢父实体"缺陷(McpSubPage.page 现接收已解析的 parent)。② 查询超时 + 限流落地:新增 McpQueryTimeout(线程上下文,*_aggregate 3s / 其余 5s)与 McpQueryTimeoutInterceptor(MyBatis StatementHandler.prepare 插件 → setQueryTimeout,项目自定义了 SqlSessionFactory,故显式 addInterceptor);新增 McpRateLimiter(进程内滑动窗口 60 次/分,偏离原文的 Redis 方案,因项目未引入 Redis);错误码新增 -32002 rate_limit_error 与 -32003 query_timeout。实测验证:C-20260715001(2682 条 SN 明细)主调 500 条 + 22 页 = 2682 条、漏 0/重复 0;从 sub_parent 起翻 27 页同样取满 2682;财务 receiptPlans 3 页 6 条不重不漏;7 项游标错误路径(未知子列表名/缺 sub_parent/与 cursor 互斥/跨子列表串用/父实体不存在/page_size 超限)均返回 -32602;限流第 61 次准确触发 -32002;超时用"表写锁制造阻塞"实测聚合 3.3s、非聚合 5.8s 均返回 -32003。
v1 按表划分 10 个工具(inventory_* / purchase_* / finance_*),提出批量预加载、限流、字段裁剪
v2 前置验证后修正:① 否决 oms_finance_operate_report 物化表数据源;② 过滤键改为对齐实测索引;③ 撤销无索引支撑的时间范围过滤;④ 补库存数量聚合能力
v3 ① 工具改为"面向问题",收敛为 8 个;② 引入游标分页协议与 Agent 翻页指令;③ 新增 3 个聚合工具与口径定义;④ 输出索引清单(P0/P1/P2/不可加)并完成列存在性验证;⑤ 补错误契约、权限指纹、验收指标与遗留项
v4 ① 新增 3.8「与现有 MCP 工具的一致性基线」与 3.9「有意偏离(4 处)」;② 响应契约对齐现有工具(保留 data.total,仅新增 page_info,新增 dict_fields);③ 新增附录 A 字段字典:8 个工具的逐字段中文注释、来源列、枚举取值;④ 明确"字典表 vs Java 枚举"两类翻译来源,并列出待同步项,杜绝臆造
v5 ① 实测证实"分析统计会退化为 Agent 驱动的多次全表扫描"(分页 34.4ms ≈ 全局汇总 38.1ms;6.5 万行 ≈ 648 页必被截断);② 新增第十三章 分析统计场景优化:mode=SUMMARY 一次算完、Top-N、覆盖索引、类型对齐防索引退化、IN 收窄、超时限流、从库路由、统计维度清单;③ 索引新增 P1-3 覆盖索引;④ 明确 SUMMARY 仍为 1 次 O(N) 的边界与"预聚合表需可靠定时任务"的教训
v6 ① 新增第十四章 覆盖度缺口分析与完善:三域覆盖度矩阵、统计维度矩阵、缺口清单;② 指出最严重缺口是"列表/范围查询整体缺失"(所有单据只能按单号点查,"本月有哪些采购单/多少票"无法回答);③ 实测修正规则:除 oms_inventory_info/_delivery_detail 外全部表 <1000 行,故把"无索引=不支持"改为按表规模分级,小表放开状态/时间/伙伴维度;④ 给出 A/B/C/D 四档完善建议;⑤ 明确"性能接近上限、覆盖不完备"的分层结论,并声明"完备 vs 轻量"的本质冲突
v7 把 v6 识别的缺口全部并入:① 工具由 8 → 9 个(合并 purchase_order_detail/finance_bill_detail 进 purchase_list/finance_list,避免重复功能导致选错);② A 档落地:group_by 增 STATUS/TIME_MONTH/TIME_QUARTER/PARTNER/VENDOR,度量增 ARRIVAL_DELAY_DAYS/OVERDUE_DAYS;③ B 档落地:新增 warehouse_list/purchase_list/finance_list 三个 entity 参数化列表工具,补齐"本月有哪些/多少"类基础问题;④ C 档落地:inventory_flow 增 stock 备货分组、finance_order_position 增 charge 计收分组;⑤ D 档落地:P2 条件索引 + 时间列索引策略改为按表规模分级;⑥ 权限补齐到"工具 × entity"粒度对照表;⑦ 附录 A 增三个列表工具字段字典与新增枚举;⑧ 新增实施批次划分
v8 用全库表枚举做严格比对(199 张表),修正自相矛盾之处并补齐规格空白:① 修正 3 处自身错误——入库明细表由"SN 明细"更正为 oms_inventory_inner_detail;撤销对 oms_inventory_info.inner_price/outer_price 的"含税"断言改为待确认;OWNER 维度因无索引支撑予以移除;② 新增第十五章逐条定义 9 处规格空白(时间维度字段、维度可用性矩阵、arrival_delay_days 语义、sub_cursor 协议、metrics 全枚举与参数冲突规则、include_zero 判定、在库口径、含税口径、多币种、collation 大小写、超时/限流/埋点/schema 版本、备份表白名单、测试与压测);③ 纳入一级缺口:order_delivery + delivery_list(manage 域发货单与 SN 明细,补上"签收"能力)、project_order_info_recall(撤回历史);④ 纳入二级缺口:新增 master_data_list(批量编码翻译),工具数 9 → 10;⑤ P2 索引扩至 20 条;⑥ 新增只读 SQL 扩至 16 项
v9 补齐最后 3 类遗留:① 覆盖缺口——新增 project_list(项目/项目产品/进度/POC/报价,5 个 entity)与 cross_domain_aggregate(受限跨域透视:维度/度量白名单 + 单链路约束 + time_range 必填 + 分组上限,实现"客户 × 产品 × 月";真正的任意 SQL 仍不开放),工具数 10 → 12;② 业务口径去阻塞——在库口径与价格含税口径改为配置项 + 工具参数 + 校验 SQL;③ 工程项落地——索引 DDL 执行方案、时间区间可配置策略、从库路由实现方案(财务类禁止走从库)、RAG 工具路由(ToolEmbedding/ToolRetriever/ToolRouter + 中文 2-gram + 同义词配置 + tools/list 的 query/detail 兼容扩展 + tools/route);④ P2 索引扩至 25 条,只读 SQL 扩至 20 项
v10 逐表复核后收口,含 2 处实测修正:① 实测修正 1——入库明细来源:v8 曾改为 oms_inventory_inner_detail,实测该表仅 1 行(未启用),而 oms_inventory_info 中 579/580 张入库单有 SN 明细 → 入库明细以 oms_inventory_info(按 inner_code)为准;② 实测修正 2——新增口径提醒:oms_inventory_info.order_code 在 SN 未出库时为空(实测空值数 15,532 恰等于在库数),不得用它反查"在库货属于哪个订单",须经 inner_code → oms_inventory_inner.order_code;③ master_data_list 新增 2 个 entity:WAREHOUSE(仓库主数据,默认只返回正常仓,include_disabled=true 含停用仓)与 COMPANY(己方公司主体 oms_company_info);④ 明确排除 vendor_info(5 行冗余表):供应商主数据只用 oms_vendor_info,查不到不回退;⑤ 排除清单同步补入 vendor_info 与 oms_inventory_inner_detail;⑥ P2-19 因表不采用而作废
v11 采纳方案 A:补齐 manage 域合同模型 + 3 处实测异常修正:① project_list 新增 2 个 entity——CONTRACT(order_info,366 行,uk_order_code(order_code,version_code) 可直接按合同号查)与 CONTRACT_PRODUCT(order_list,854 行,idx_order_id);② warehouse_list(entity=ORDER_DELIVERY) 补全 join:orderId 实测 355/355 指向 order_info,必须 join 才能输出合同编号/客户/代理商,并明确禁止按 id 关联 project_order_info(242 条假命中);③ 明确两套订单模型关系:manage 域与项目域只能按 order_code 部分对齐(330/366),工具须容忍关联不到且不得臆造;④ 新增 15.14 实测异常——order_info.order_code 有 14 行前导制表符脏数据(比对必须 trim())、order_type 实际值为 zq/dls(与列注释 1/2 不符);⑤ 只读 SQL 扩至 22 项
v12 修正"缺口矩阵误导"并评估剩余缺口:① 14.1/14.2 矩阵补 "现状(v11)"列——原 ❌ 是 v6 快照,实际已被 v7–v11 闭合 13 项;② 新增 16.9 剩余缺口的影响评估与闭合方案;③ 实测推翻一处旧结论:审批待办/已办无需读 Flowable act_*;④ 账龄分桶列为零成本可补;⑤ 财务历史时点余额给出"按明细重算"方案;⑥ 附件可降级为元数据查询
v15(认证态端到端验证完成) 用临时机器人凭证(绑定 admin,验证后已删除)对 13 个工具做真实数据 E2E,最终 34/34 用例全部通过(含 12 个子 entity、分页不重不漏、游标串用防护、错误路径)。过程中发现并修复 4 类实现缺陷:① java.time.LocalDateTime 序列化缺失 → SignTime 等字段导致 InvalidDefinitionException,被 McpController 外层 catch 吞掉后表现为 HTTP 200 + 空响应体;已注册 java.time 序列化器(日期 yyyy-MM-dd、时间 yyyy-MM-dd HH:mm:ss)并让该 catch 回写 error 响应而不再静默;② 未提供的入口仍发起查询 → where col in () 非法 SQL(inventory_sn_trace),已改为仅对非空列表查询;③ 既有 Transfer-Encoding 重复响应头(curl 丢响应体),已移除手工设置;④ 3 处 SQL 引用不存在的列(oms_inventory_outer.receivable_bill_code、oms_payable_bill.vendor_name、project_order_info.project_code/project_name),已分别删除该列/改经 oms_vendor_info 关联/改用 project_info。另新增系统性 SQL 列校验(77 条新增 select 的 别名.列 与 information_schema 全量比对 → 0 处不存在列)。索引实测:P0-1/P0-2/P1-1/P1-2 已存在(此前巡检查漏),仅 P1-3 覆盖索引缺失,已在 oms_test 执行并实测 141.8ms→66.6ms(Using index)。
v14(已实现) 按 v13 方案完成编码与验证,实现期共 5 处实测修正:① purchase_order_map.order_id 指向 project_order_info(1116/1123),不是 order_info(实测 SQL 验证)——与 order_delivery.order_id(→order_info,355/355)是两条不同链路,方案 16.1 原表述已修正;② MySQL TRIM() 不去除制表符:order_info.order_code 的 14 行脏数据为前导 \t,必须用 trim(replace(order_code,'\t','')) 才能命中(15.14 补充);③ order_delivery.delivery_status 实测值为 qs(324)/yf(31)(拼音缩写),不是列注释的 1/2/3 → 翻译按实测值映射并保留数字兜底(15.14 补充);④ oms_receivable_bill 无 plan_receipt_date 列(账龄分桶经 last_receipt_plan_id 关联收款计划实现);⑤ oms_inventory_info 确无 purchase_no 列,SN 的采购单号经 inner_code 关联 oms_inventory_inner 补齐。验证结论:编译通过(445 源文件);tools/list 返回 15 个工具(13 新 + 2 既有);RAG 路由 15 条中文问句 top3 命中 100%、top1 命中 73%,tools/list 带 query 后 schema 字节 下降 81%(23621→4586);无凭证调用返回 AUTH_ERROR(-32001) 而非空数据;tools/call 不传 name 可自动路由。改动规模:74 文件、+2620 行,仅 10 处删除(均为 McpService 的等价改写)。框架层最小改动:McpService 增加 query/detail/tools/route/自动路由(向后兼容),McpController 增加 McpToolException 错误码映射(原来无法返回 AUTH_ERROR)。
v13(已实现) 三项全部纳入,三大域封版:① 新增工具 approval_list(entity = TODO/DONE,工具数 12 → 13)——数据源 bu_todo(61)/bu_todo_completed(5,876),不碰 Flowable act_*;回答"我还有哪些单要审/卡在谁那儿/为什么被驳回/审批耗时";已覆盖 order_approve_*、purchase_order_online、finance_payment、fianance_ticket、order_reback、outer_reback;附录 A.15 给出完整字段字典与两处实测异常(fianance_ticket 拼写错误、approve_status 3=通过/2=驳回与常规相反);② 账龄分桶:finance_balance_aggregate 增 group_by=OVERDUE_BUCKET(0-30/31-60/61-90/90+,仅 unreceived_amount>0);③ 财务历史时点余额:增 as_of_date,按明细重算(Σ应收 create_time≤T − Σ已收 receipt_time≤T),并以 metadata.basis = RECALCULATED@<date> | CURRENT 标注口径差异;④ 附件元数据纳入 finance_list(entity=ATTACHMENT)(过滤 del_flag='0',不返回文件内容);⑤ P2 索引增 P2-26(bu_todo_completed(approve_user, approve_time))→ 编号 26 条、有效 23 条;只读 SQL 扩至 25 项;⑥ 15.2 维度矩阵与 15.5 冲突规则同步(OVERDUE_BUCKET 仅 SUMMARY)