ThreadingHTTPServer。| 组件 | 职责 |
|---|---|
app.py |
本地 GatewayApp、stdio/CLI 入口、12 个工具注册与参数转发 |
public_gateway.py |
公网 PublicGatewayApp、设备会话解析后的 scoped 调用、同组 12 个工具注册 |
public_server.py |
/mcp HTTP JSON-RPC、可信追踪号、审计上下文、限流和 /health |
mcp_protocol.py |
MCP 握手、tools/list、tools/call、旧/新结果分流和协议错误 |
services/api_client.py |
本地 token 上下文下的 ThinkPHP 工具请求与动态列表请求 |
services/scoped_api_client.py |
公网请求级 mcp_token 转发,避免进程级身份串用 |
services/gateway_session_store.py |
GWS_xxx 到服务端员工会话的 Redis 映射 |
services/output_presenter.py |
11 个安全工具的白名单 DTO、错误映射和文本渲染 |
tools/*.py |
工具名称、说明、输入 Schema、路由和调用封装 |
本地 stdio 流程:
GatewayApp 从 Redis 或开发用文件 store 读取本机 session。tools/list 和 tools/call 携带该 token 请求 fmsoperate。公网流程:
GWS_xxx。RequestContextParser 提取设备凭据,GatewaySessionStore 从 Redis 获取服务端 mcp_token。PublicGatewayApp 为单次请求创建 scoped API 调用上下文,不把 token 放进进程全局状态。会话缺失、过期或后端动态列表不可用时关闭访问。公网不得使用文件 token store、固定 FMS_SESSION_KEY 或共享进程级员工 token。
GatewayApp 与 PublicGatewayApp 的候选注册顺序和名称必须完全一致,当前各为 12 个。tools/list 调用 /mcp/tools/listEnabledTools,只返回本地注册与后端启用代码的交集。tools/call 重新读取启用集合,避免工具被禁用后继续调用。tools.listChanged=false,因此工具或 Schema 变更必须通过 Gateway 重启和客户端重连刷新。query_order 走旧协议分支,保留 columns/records/meta 和既有文本行为。OutputPresenter.SAFE_TOOLS 显式处理其余 11 个工具:
| 类型 | 工具 | DTO |
|---|---|---|
| 表格 | query_order_exact、query_track、query_customs_declaration_files、query_outbound_list |
headers + rows + pagination |
| 排舱详情 | query_outbound_detail |
summary + details,分页位于 details.pagination |
| 订单详情 | query_order_detail |
中文订单号、详情模块、固定概览分组或明细及分页 |
| 筛选项 | list_order_filter_options、list_outbound_filter_options、list_pending_outbound_export_filter_options |
value + label + code 三列 |
| 导出 | export_pending_outbound_orders、export_out_of_province_port_data |
files[].label + files[].url |
Presenter 对列定义、记录类型、详情汇总、附件结构和导出 URL 做白名单校验。未知工具、未允许列、畸形响应或未知异常返回安全 isError=true;真实异常仅记录在服务端日志。后端业务错误映射为固定消息,不透传原始 msg/data。
订单详情 Presenter 不依赖原始请求参数,而是校验后端规范 section、固定分组及每行完整键集合。“全部”响应校验概览和十个明细分组,并将每个明细分组的分页信息独立展示。状态和时间类型只接受已知枚举;入库重量根据后端固定模式显示为单箱重量或总重量。
rq_*;公网入口始终生成可信 rq_http_*,不采信调用方 X-Request-Id。X-Request-Id 只记录 SHA-256 短哈希 client_request_id_hash,用于关联客户端反馈。_meta 和结构化日志。initialize 与 tools/list 不进入限流器;tools/call 使用 gateway_session_id + tool_name 分桶,并通过 FMS_MAX_IN_FLIGHT_PER_TOOL 控制可释放的同时执行数。FMS_RATE_LIMIT_MAX_REQUESTS=0 时关闭累计窗口限制,超限统一返回 JSON-RPC -32029。FMS_RATE_LIMIT_ENABLED、FMS_RATE_LIMIT_MAX_REQUESTS、FMS_RATE_LIMIT_WINDOW_SECONDS;生产环境还应在反向代理层限流。query_outbound_list.outbound_status 必填,只暴露后台业务阶段;shipping_method 无默认值,未传表示全部运输方式。query_order_detail.order_number 必填;section 接受 11 个中文模块或“全部”,页码和每页数量均为 1 至 100。query_outbound_list.warehouse_id 接受正整数或 -1(客户仓),拒绝 0 与其他负数。list_outbound_filter_options.filter_type 使用七个中文枚举;仓库分支由 fmsoperate 复用 OrderModel::getSortWarehouse(),所有分支先校验 admin/Outbound/index。value/label/code 供跨工具传参,面向用户只展示中文标签。python -m unittest discover -s tests -p "test_*.py"
python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage report -m --fail-under=100
git diff --check
运行 coverage 会更新本地 .coverage 文件;只做文档治理时使用已有只读报告,不应制造覆盖率产物变更。