# `export_pallet_data` 号码筛选调整设计 **日期:** 2026-09-08 **范围:** `mcp` Gateway、`fmsoperate` MCP 业务链路、`settlement_tests` 合同测试及集中项目文档 ## 1. 背景与目标 当前 `export_pallet_data` 使用“入仓单号数组”或“海外仓入库时间起止”二选一。此次调整移除入仓单号,增加两类明确的物流号码筛选: - 柜号数组 `container_codes` - 提单号数组 `bl_numbers` 入库时间筛选继续保留。调用方必须明确号码类型;只给出号码但未说明是柜号还是提单号时,Gateway 的工具说明必须引导用户先确认,不能按格式猜测、并行试查或跨字段重试。 ## 2. 输入合同 `export_pallet_data` 的输入为以下三种筛选方式严格三选一: 1. `container_codes` 2. `bl_numbers` 3. `inbound_time_start` + `inbound_time_end` 号码数组先去除首尾空白、空值和重复值,再校验: - 至少 1 个,最多 200 个; - 每项必须是字符串; - 每项长度不超过 100 个字符。 时间边界继续接受 `YYYY-MM-DD` 或 `YYYY-MM-DD HH:MM:SS`。只填日期时,开始边界展开为当天 `00:00:00`,结束边界展开为当天 `23:59:59`;起止日期部分差不超过 30 天,即最多 31 个日历日。 以下输入必须拒绝: - `container_codes` 与 `bl_numbers` 同时非空; - 任一号码数组与时间范围混用; - 只提供一侧时间边界; - 三种筛选方式均未提供; - 使用 `inbound_numbers`、内部 ID、订单号或打托批次号。 参数错误沿用 `MCP_1401` 安全错误,不创建下载任务。 ## 3. 调用链与数据范围 ### Gateway - 工具 Schema 只暴露 `container_codes`、`bl_numbers`、`inbound_time_start`、`inbound_time_end`。 - 保持 `additionalProperties=false`,不保留 `inbound_numbers` 兼容别名。 - CLI 增加柜号和提单号数组参数,移除入仓单号 CLI 参数。 - 工具描述明确“柜号 / 提单号 / 入库时间三选一”,并写明号码类型不明确时必须先询问。 - 本地与公网工具注册、路由、异步任务展示合同不变。 ### fmsoperate `McpPalletDataExportLogic` 负责归一化、三选一校验、权限检查和任务提交;`McpPalletDataExportModel` 只负责取数: - 柜号筛选只按当前公司的海外仓关联排舱记录匹配 `fms_outbound.container_code`; - 提单号筛选只按当前公司的海外仓关联排舱记录匹配 `fms_booking_detail.bl_number`; - 时间筛选继续按当前公司、未删除的海外仓入库明细 `inbound_time` 闭区间匹配; - 所有路径最终都沿用“海外仓入库记录 ID → 订单 ID → 打托批次 ID”的批量解析链路; - 结果只允许当前 `company_id` 的打托批次,原有海外仓菜单和导出菜单权限不变。 柜号和提单号必须在不同的查询分支中使用索引友好的 `IN` 条件,不能合并成跨字段 `OR`。不按号码逐个查询,不增加无必要的总数查询。 队列任务中的筛选白名单、消费者参数校验和导出 Service 入参同步改为新合同;新接口不再接受或重新解释 `inbound_numbers`。 ## 4. 输出与异步行为 以下行为保持不变: - 仍只提交异步任务并立即返回签名 `task_ref`; - 仍投递 `fmsoperate:inside` 的独立 `McpPalletDataExport` 消费者; - 仍生成后台同口径的 15 列 Excel; - 有成功下载的附件时按 `pallet_batch` 分目录生成 zip; - 单个 OSS 附件失败时跳过该附件,全部附件失败时仍只上传 Excel; - 通过 `query_export_task` 查询任务状态和下载链接; - 没有匹配的打托批次返回 `MCP_1601`,不创建空任务。 ## 5. 性能与安全约束 - 200 个号码一次性批量传入并在数据库层 `IN` 处理,禁止 PHP 循环逐号查库。 - 柜号和提单号使用独立查询分支,防止跨字段 OR 造成不可控扫描。 - 保留当前的分阶段 ID 查询和批量补齐,不回退到逐订单查询。 - 查询和队列消费均重新绑定公司、员工和任务来源身份,不信任请求传入的身份字段。 - 不在公共响应、工具说明或日志中暴露 SQL、内部 ID、异常栈或任务内部信息。 ## 6. 测试计划 ### Gateway Python - Schema 只包含新字段,拒绝 `inbound_numbers`、内部 ID 和分页字段; - 柜号数组、提单号数组可正常去重并转发; - 柜号与提单号混用、号码与时间混用、空筛选、单侧时间、超过 200 个或单项超长均拒绝; - 时间最多 31 个日历日,日期边界展开保持现有行为; - 工具描述覆盖明确导出、号码类型询问、禁止猜测和异步查询; - CLI 新参数正确转发,本地/公网注册仍一致。 ### fmsoperate PHP - Validate、Logic、Model 和队列消费者全部使用新筛选字段; - 覆盖三选一、200 上限、每项长度、时间范围和禁止旧参数; - 覆盖权限失败、无可导出数据不建任务、可信公司/员工身份和异步任务引用; - 覆盖柜号/提单号查询只使用对应字段,不产生跨字段 OR 或逐号查询; - 保留公司隔离、15 列产物、独立消费者和任务终态安全合同。 ### 文档与交付验证 - 运行 Python 全量单测及严格覆盖率检查; - 运行 `settlement_tests` 的 fmsoperate 定向合同测试和 PHP 语法检查; - 运行受影响仓库的 `git diff --check`、Markdown 链接检查和最终状态检查; - 不执行注册 SQL,不重启 Gateway 或 Worker,不据代码变更推断目标环境已上线。 ## 7. 非目标 - 不修改后台海外仓页面、原有后台导出路由或打托文件列定义。 - 不改变任务签名、任务去重、权限菜单、公司隔离和附件处理策略。 - 不增加柜号/提单号模糊匹配、格式推断或自动试查。