export_pending_outbound_orders 和 export_out_of_province_port_data 当前都在一次
MCP tools/call 内同步完成查询、文件生成、上传并返回下载链接。未排舱导出固定
is_sync=1;省外进港资料同步调用 Support 模板导出。数据量较大时整个请求可能
持续一分钟以上。
MCP Gateway 调用 fmsoperate 的默认 HTTP 超时为 10 秒,外层反向代理和 Workbuddy 也可能提前关闭长连接。单纯增加超时无法保证整条链路稳定,而且会长期占用 Gateway 并发名额和 PHP 请求进程。
本设计将两个导出工具改成短请求提交任务,并新增独立状态查询工具。文件生成继续复用
fmsoperate 现有导出算法、Redis 队列、st_download_list 和文件存储,不把业务逻辑
迁移到 Gateway。
本设计替代
2026-07-14-mcp-export-pending-outbound-orders-design.md 中关于同步等待、直接返回
文件 URL 和“不改造成任务轮询协议”的结论;原文件只作为当时设计记录保留。
query_export_task,通过新的短 MCP 调用查询等待、执行、完成或失败状态。st_download_list.id、内部异常、队列载荷或原始失败备注。st_download_list。两个导出工具一律创建任务并返回任务引用,调用方稍后单独调用
query_export_task。该方案使每次 HTTP 请求都保持短时、可重试,并且不依赖数据量
阈值或调用端连接时长。
导出耗时不仅取决于行数,还取决于关联查询、模板服务、文件写入和上传。预估阈值不稳定, 会让同一个工具出现两套时序和输出分支,增加调用方及测试复杂度。
Gateway、PHP、反向代理和 Workbuddy 任一层仍可能先超时。长请求还会占用每工具并发 配额,超时重试可能重复生成文件,因此只可作为临时排障手段。
保留两个现有工具名及输入 Schema:
export_pending_outbound_ordersexport_out_of_province_port_data成功提交后统一返回:
{
"code": "MCP_0000",
"msg": "导出任务已提交",
"data": {
"task_ref": "mexp_xxx",
"status": "queued",
"retry_after_seconds": 10
},
"meta": {
"request_id": "rq_xxx"
}
}
task_ref 是带完整性校验的不可枚举引用,不是数据库主键。retry_after_seconds
只是调用建议,不代表精确完成时间,也不允许 Gateway 在原请求内等待。
提交阶段仍完成以下同步检查:
因此参数错误、无权限、目标不存在或无可导出数据仍立即返回现有安全业务错误,不创建 队列任务。
query_export_task新增工具:
query_export_taskPOST /mcp/tools/queryExportTasktask_ref,字符串,最长 512 字符返回状态:
| 内部状态 | 对外状态 | 对外数据 |
|---|---|---|
st_download_list.status=1 |
queued |
task_ref、建议重试秒数 |
status=4 |
running |
task_ref、建议重试秒数 |
status=2 |
completed |
task_ref、files[].label/url |
status=3 |
failed |
task_ref、固定安全失败提示 |
任务不存在、引用签名无效、任务不属于当前员工或公司、来源工具权限失效时,统一返回
“导出任务不可用”,不区分具体原因,避免枚举和越权探测。不得返回
st_download_list.remark。
query_export_task 自身先经过动态注册校验。Logic 再根据任务中记录的来源工具执行该
导出的菜单权限检查;只有任务所属员工、公司和来源权限均仍有效时才返回状态或文件链接。
两个提交工具的 Presenter 白名单改为:
task_refstatus=queuedretry_after_secondsquery_export_task 的 Presenter 只接受四个已知状态。完成时复用现有导出 URL 和主机
白名单校验;其他状态不得携带文件链接。未知字段、未知状态、畸形 URL 或未知错误继续
关闭失败。
工具说明明确:
queued 后应告知用户任务已提交。query_export_task。CLI 保留 fmsoperate 原始 code/msg/data/meta 信封。
task_ref 使用 mexp_ 前缀和 URL-safe Base64 编码,载荷至少包含:
task_idcreate_user_idcompany_idsource_toolissued_at载荷使用从 fmsoperate 专用 MCP_EXPORT.TASK_REF_SECRET 分别派生的 AES-256-CBC 加密密钥和 HMAC-SHA256 认证密钥,按 encrypt-then-MAC 生成不暴露内部 ID 的引用。密钥少于 32 字节、为空、解密失败或引用验签失败时
关闭访问。验签使用恒定时间比较;解析失败不得记录完整引用。引用有效期固定为 7 天,
超过有效期后要求用户重新提交导出。
签名只防止引用伪造,不替代数据库授权。查询时仍必须按以下条件读取:
id=task_idcreate_user_id=当前员工system_id=当前系统export_params.company_id=当前公司export_params.mcp_source_tool=source_tool日志只记录任务数据库 ID 或引用短哈希中的一种内部关联值,不记录完整 task_ref。
McpPendingOutboundExportLogic 保留当前权限检查、参数归一化和单行探测。export_type=EXPORT_PEND_OUTBOUND_ORDER、is_sync=0、
当前员工和公司上下文及 mcp_source_tool。st_download_list 等待任务。RedisAction::EXPORT_PEND_OUTBOUND_ORDER 和
ExportPendOutboundOrder Handler 入队。ExportPendOutboundOrderLogic,按现有约定将状态从等待更新为执行中、
完成或失败。task_ref。McpOutOfProvincePortExportLogic 保留四类号码严格四选一、全部匹配和全部授权。mcp_source_tool 写入任务参数。st_download_list 等待任务并推送新的专用 Redis Action/Handler。ExportLogic::shangHaiExport() 抽出的可复用
模板导出方法。task_id 作为 down_record_id,不得再次创建第二条
st_download_list 记录;后台现有 shangHaiExport() 对外行为保持不变。省外进港 Worker 只接收提交阶段已经全部授权并固定下来的排舱 ID,不重新根据原始号码做 模糊匹配。状态查询和链接交付阶段再次验证当前员工、公司及来源菜单权限。
创建任务与入队不能组成单个数据库事务,因此采用补偿:
st_mcp_access_log 保留,公共响应不返回异常。提交前基于以下内容生成稳定 SHA-256 指纹:
使用 Redis 原子 SET NX EX 建立 60 秒提交锁,并把已创建的任务 ID 保存为值。同一指纹
在锁有效期内再次提交时返回同一个 task_ref,不重复插入或入队。入队失败时释放锁。
去重只覆盖客户端超时或快速重试,不缓存导出结果,不跨员工复用文件,也不改变用户在 60 秒后主动再次导出的能力。
task_id、company_id、operator_id、is_super 或队列参数。st_mcp_access_log 继续记录每次提交和查询的独立 request ID 与安全响应。新增 query_export_task 后,本地和公网 Gateway 候选工具由 12 个变为 13 个。需要同步:
status=0,重复执行保留管理员现有状态。部署顺序:
fmsoperate:inside Worker。tools.listChanged=false 不会热更新 Schema。不执行新的业务表 DDL。队列 Worker、Redis 和文件存储不可用时不得启用新合同。
queued -> running -> completed/failed。remark、异常或内部 ID。queued 响应。query_export_task 和两个导出工具,阻止产生新合同调用。st_download_list 记录。