Files
xiaoqu_mcp_service/xiaoqu-agent-service/doc/agent-mcp-tools-reference.md
T

1094 lines
68 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# xiaoqu-agent-service MCP 工具接口文档
> 本文档是 MCP 服务**对外暴露工具**的接口契约。新增/修改工具时必须同步更新本文档(开发约定见 `agent-mcp-service-design.md` §12)。
> 文档中的入参与代码 `@ToolParam`、出参与工具实际返回保持一致;在线验证方式见文末附录。
## 0. 调用约定(所有工具共用)
| 项 | 说明 |
|---|---|
| 端点 | `POST http://<host>:8088/mcp`(MCP Streamable HTTP) |
| 认证 | Header `Authorization: Bearer xqak_xxx`,Token 由管理后台或受信任 Open API 获取;服务端保存 SHA-256 及加密密文 |
| 工具可见性 | `tools/list` 按 ak 作用域过滤,只返回该 ak 作用域并集内且启用状态的工具(2026-09-04 起,服务端 servlet 层重写响应实现);`tools/call` 另有强制校验兜底,越权返回可读拒绝信息并落审计(denied) |
| 行级数据权限 | 跟 ak 所属人走,照搬老系统 `getByIsAgent` 语义(见设计文档 §3.3),与作用域无关;**tenant_service 服务主体无用户上下文,按总部视角全量可见**(见 §0.3) |
| 分页 | 列表类工具统一 `page`(从 1 开始,默认 1)、`rows`(默认 20,**上限 100**,超出截断为 100 并在返回 `notice` 字段提示) |
| 分页出参 | 统一含 `total`(总数)、`page`、`rows`、`records`(本页记录)、`notice`(仅截断时出现) |
| 工具声明 | `tools/list` 中每个工具带 MCP annotations:只读(`readOnlyHint=true`)、幂等(`idempotentHint=true`)、非破坏性(`destructiveHint=false`)(REQ-20260911-0011 起) |
### 0.1 schema_version 双跑(REQ-20260911-0011 起)
- **默认(v1,兼容面)**:`tools/call` 返回的 `result.content[0].text` 即各工具原有 JSON,无 `structuredContent`,老客户端零改动。
- **显式声明新版本**:请求头加 `X-Mcp-Schema-Version: 2`,`tools/call` 响应额外写入 `result.structuredContent` 统一信封,`content` 文本保持不变:
```json
{ "schema_version": 2, "request_id": "<服务端生成 UUID>", "data": { "…": "各工具原有 JSON" } }
```
- 版本头只接受 `1`/`2`(缺省按 1);其他值在 `tools/call` 上直接返回 `SCHEMA_VERSION_UNSUPPORTED` 错误,不执行工具。
- v2 请求下 `tools/list` 的每个工具额外带 `outputSchema`(信封结构);业务字段契约仍以本文档各工具章节为准。
### 0.2 统一错误码(REQ-20260911-0011 起)
工具执行失败返回 `isError=true`,`content[0].text` 为结构化 JSON(不再是裸异常文本);v2 下同样的错误出现在 `structuredContent.error`:
```json
{ "error": { "code": "PERMISSION_DENIED", "message": "人类可读说明", "request_id": "…" } }
```
| code | 含义 |
|---|---|
| UNAUTHENTICATED | 缺少有效 ak 或鉴权上下文丢失 |
| PERMISSION_DENIED | token 作用域不含该工具或工具已停用 |
| NOT_FOUND_OR_FORBIDDEN | 资源不存在或无权查看(不区分,防探测) |
| RATE_LIMITED | 触发限流(预留) |
| UPSTREAM_UNAVAILABLE | 上游数据源(DB/Redis)暂时不可用 |
| SYNC_CURSOR_EXPIRED | 增量同步游标过期(预留) |
| SCHEMA_VERSION_UNSUPPORTED | 声明的 schema_version 不受支持 |
| DATA_INTEGRITY_ERROR | 数据不一致或未预期异常(兜底;细节只落服务端日志,凭 request_id 排查) |
日志与审计不记录 Token/ak,手机号一律脱敏为 `138****5678` 形式。
### 0.3 服务身份(tenant_service)在线查询(REQ-20260921-0004 起)
服务 Token(`principalType=service`,无登录身份)除 SYNC 变化流外,可绑定「允许服务主体」的查询作用域(`agent_scope.service_enabled=1`,如运营用 `ops-query`)调在线查询工具:
- **可见范围**:所有走行级权限的查询工具对服务主体统一按**总部视角全量可见、不做行过滤**(PermissionService 统一分支);用户身份调用行为完全不变。
- **签发**:`POST /api/open/token/ensure` 传 `principalType=service` + `serviceName`,scopeCode 必须是 service_enabled=1 的作用域,否则 403;服务密钥需事先绑定该作用域。
- **限流**:服务主体的在线查询走独立 SERVICE-QUERY 档位(`app.rate-limit.service-query-per-minute`,默认 300/分钟),与用户 QUERY、SYNC 档位互不占配额;调 `*.changes.list` 仍走 SYNC 档。
- **例外**:`warehouse.todo.list.query` 按"当前登录人"统计待办,服务身份无此语义,调用返回 PERMISSION_DENIED,不应绑入服务查询作用域。
- 审计照常落 `audit_log`(token_id + serviceName 可溯源),密钥泄露时在后台单独轮换/吊销。
---
## 1. agent.count
统计代理商总数(demo 工具)。
- 分类:system | 类型:读
- 数据来源:m0 库 `agent` 表,固定条件 `soft_delete = 0`
**入参**:无
**出参**:
| 字段 | 类型 | 说明 |
|---|---|---|
| count | number | 代理商总数 |
```json
{ "count": 62 }
```
---
## 2. agent.list.query
查询代理商列表(分页),对应老系统 `/agent/listByPage`。
- 分类:customer | 类型:读
- 数据来源:m0 库 `agent` 表
- 行级权限:权限组 `isagent=1`(代理商身份)只查自己名下直属下级(`supervisor_id = 自己agentId`);`isagent=0`(总部)看全部。传 `id` 精确查询时:代理商仅限自己或直属下级,总部可传任意合法 id,且其余过滤条件失效。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 否 | 代理商 id,精确查询;传入后忽略其他过滤条件 |
| name | string | 否 | 代理商名称,模糊匹配 |
| level | number | 否 | 级别:0=总部 1=一级 2=二级 |
| channelNo | string | 否 | 渠道编号:Z=总部 F=分公司 D=代理 |
| province | string | 否 | 省份,精确匹配,如 `北京市` |
| city | string | 否 | 城市,精确匹配,如 `北京市` |
| serialnumber | string | 否 | 代理商编号,精确匹配 |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100 |
**出参 records 字段**:
| 字段 | 说明 |
|---|---|
| id / name | 代理商 id / 名称 |
| level / channelNo / serialnumber | 级别 / 渠道编号 / 代理商编号 |
| supervisorName | 上级代理名称(无上级为 `-`,批量富化) |
| province / city / countyseat | 省 / 市 / 区县 |
| contact / phone | 联系人 / 电话 |
| agencyarea / memo | 代理区域 / 备注 |
| createTime | 创建时间,`yyyy-MM-dd HH:mm:ss` |
```json
{
"total": 62, "page": 1, "rows": 20,
"records": [ { "id": 19, "name": "北京D类代理", "level": 2, "channelNo": "D", "...": "..." } ]
}
```
---
## 3. property.list.query
查询物业(客户)列表(分页),对应老系统 `/property/listByPage`。
- 分类:customer | 类型:读
- 数据来源:m0 库 `properyt` 表
- 行级权限(完整版 `getByIsAgent` 语义):
- 总部(isagent=0):全部;
- 代理商(isagent=1):自己 + 直属下级代理 + `warehouse_agent_auth` 代管代理 名下物业,及 `agent_manage_district_auth` 授权代管的物业;
- 销售部门非负责人:自己创建的物业 ∪ 代管授权代理名下物业(`id IN (...)` 结构,与其余规则互斥,1:1 复刻老代码 saleFlag 分支)。
- **不含** enterprise_type=2 物业用户限自己物业的规则(物业用户不查此列表)。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 否 | 物业名称,模糊匹配 |
| agentId | number | 否 | 所属代理商 id,精确匹配 |
| serialnumber | string | 否 | 物业编号,精确匹配 |
| province | string | 否 | 省份,精确匹配 |
| city | string | 否 | 城市,精确匹配 |
| contact | string | 否 | 联系人,精确匹配 |
| phone | string | 否 | 联系电话,精确匹配 |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100 |
**出参 records 字段**:
| 字段 | 说明 |
|---|---|
| id / name / serialnumber | 物业 id / 名称 / 编号 |
| agentName | 所属代理商名称(查不到为 `未查询到`) |
| createName | 创建人/销售姓名(未分配为 `未分配`) |
| province / city / countyseat / address | 省 / 市 / 区县 / 详细地址 |
| contact / phone | 联系人 / 电话 |
| createTime | 创建时间,`yyyy-MM-dd HH:mm:ss` |
---
## 4. district.list.query
分页查询站点列表,对应老系统 `/district/districtlistByPage`,但使用固定轻量字段契约。
- 分类:customer | 类型:读
- 数据来源:m0 库 `district_district_strategy` 视图,固定 `soft_delete = 0`、`id DESC`
- 行级权限:总部看全部;代理商按自己、直属下级、代管代理及授权物业过滤;销售部门非负责人沿用老接口的互斥物业范围。筛选条件只能缩小权限结果。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 否 | 站点 id,正整数,精确匹配 |
| districtName | string | 否 | 站点名称,模糊匹配 |
| districtState | number | 否 | 0=测试 1=履约 2=演示 3=撤机 |
| province | string | 否 | 省份,模糊匹配 |
| city | string | 否 | 城市,模糊匹配 |
| agentId | number | 否 | 代理商 id,正整数,精确匹配 |
| propertyId | number | 否 | 物业 id,正整数,精确匹配 |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100 |
**出参 records 字段**:
`id`、`districtName`、`districtNumber`、`propertyId`、`propertyName`、`agentId`、`agentName`、`province`、`city`、`countyseat`、`address`、`districtState`、`onlineState`、`hardwareCount`、`isSmart`。
`propertyName`、`agentName` 均为批量富化,查不到时返回 `未查询到`。
---
## 5. district.detail.get
按站点 id 获取扩展详情。站点 id 与当前用户行权限在同一个数据库查询条件中组合。
- 分类:customer | 类型:读
- 入参:`id`(number,必填,正整数)
- 找到:`{ "found": true, "data": { ... } }`
- 不存在或无权限:`{ "found": false, "message": "站点不存在或无权访问" }`
**data 字段**:
| 分组 | 字段 |
|---|---|
| 归属 | id、districtName、districtNumber、propertyId、propertyName、agentId、agentName、salesId、salesName、serviceCompanyId、serviceCompany |
| 地址联系人 | province、city、countyseat、address、contact、phone、mail |
| 站点属性 | businessFormat、attributeId、attributeName、installType、isSmart、lnfluenceLevel、buildCount、hardwareCount、airdetectorFlag |
| 状态周期 | districtState、onlineState、testDate、honourDate、weaningDate、testDays |
| 设备耗材 | deviceSalesType、paperSalesType、consumablesType、posDevicePaperNorm、toiletDevicePaperNorm |
| 功能开关 | pointState、assignmentWorkState、airState、messageFlag、reportFlag、lockMachineFlag、eventRepairFlag、rfidUseFlag、calibratedFlag、illegalPaperDisable |
| 语音配置 | lowerPaperState、noPaperState、powerWarning、waitTimes |
| 报警配置 | warning、warning2、warning3、warningTime、paperPercentWarn、maintainOrderWarning、foamCountWarning、foamPowerWarning |
| 业务配置 | pointDeduction、effectiveUseTime、consumablesPrice、billDate、billType、billTypeStr、fineMoney |
| 运行配置 | districtRefresh(Redis 未配置默认 5)、forbidFunction(Redis 未配置默认 0) |
| 审计信息 | createId、createName、createTime、modifyTime、memo |
固定不返回 `softDelete`、`enterpriseId`、`modifyId`、内部服务 URL、重复 `districtId`、语义未确认的 `supervisorId` 和 SKU 列表。
---
## 6. sys.ping
服务自检(demo 工具,证明链路连通)。
- 分类:system | 类型:读
**入参**:无
**出参**:
| 字段 | 说明 |
|---|---|
| service | 服务名(spring.application.name) |
| version | 服务版本 |
| time | 服务器当前时间 |
| redis | `PONG` 表示 Redis 连通;否则为 `ERROR: ...` |
---
## 7. mall.order.list.query
查询商城订单列表(分页),对应老系统 `POST /mallGoodsOrder/listByPage`。
- 分类:customer | 类型:读
- 数据来源:m1 商城库(`xiaoqu_mall_d`)`mall_goods_order` 表
- 行级权限(`PermissionService.applyOrderRowFilter`,移植老接口 `WrapperUtils.getSaleCompany` 语义):
- 总部(isagent=0):全部;
- 代理商(isagent=1):`sale_company_id IN (自己 + 直属下级 + warehouse_agent_auth 代管代理)`,有代管物业授权时再 `OR property_id IN (agent_manage_district_auth 代管物业)`;
- 销售部门非负责人:`property_id IN (自己创建的物业 + 代管代理名下物业)`,与上一条互斥。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderNo | string | 否 | 订单编号,模糊匹配 |
| agentId | number | 否 | 代理商 id,精确匹配 |
| propertyId | number | 否 | 物业 id,精确匹配 |
| orderType | number | 否 | 订单类型:0=代理 1=物业 |
| orderState | number | 否 | 订单状态(单选):0=待付款 1=待出库 2=待发货 3=已发货 4=已签收 5=已收货 6=已取消 7=已删除 |
| payState | number | 否 | 回款状态:0=待付款 1=已付款 2=付款中 |
| startOrderTime | string | 否 | 下单时间起,`yyyy-MM-dd` |
| endOrderTime | string | 否 | 下单时间止,`yyyy-MM-dd`(含当天) |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100(超出截断为 100 并在 notice 提示) |
**出参 records 字段**(按 id 倒序):
| 字段 | 说明 |
|---|---|
| id / orderNo / orderTime | 订单 id / 编号 / 下单时间(`yyyy-MM-dd HH:mm:ss`) |
| orderType / agentName / propertyName | 订单类型 / 代理名称 / 物业名称 |
| saleCompany | 销售公司 |
| payMoney / paidMoney / unPayMoney | 应付 / 已付 / 未付金额 |
| orderState / payState / warehouseState | 订单状态 / 回款状态 / 出库状态(0=未出库 1=已出库 2=部分出库) |
| customerName | 客户名称:orderType=0 取代理金蝶名称,否则取物业金蝶名称(批量富化,同老接口) |
---
## 8. mall.order.detail.query
按订单号查询商城订单详情(订单 + 商品明细 + 收货地址一次返回,合并老系统三个接口)。
- 分类:customer | 类型:读
- 数据来源:m1 库 `mall_goods_order` + `mall_order_details` + `mall_shipping_address`
- 行级权限:与 `mall.order.list.query` 一致;订单不存在或无权查看时 `record=null` 并在 notice 说明(两者不区分,不泄露订单是否存在)。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderNo | string | 是 | 订单编号 |
**出参**:
- `record`:订单关键字段(金额类 payMoney/paidMoney/unPayMoney/totalMoney/goodsMoney/postageMoney/discountMoney,状态类 orderState/payState/orderPayState/warehouseState/orderInvoiceState,时间类 orderTime/payTime/receiptTime/signTime/createTime,及 accountsType/paymentType/goodsType/saleCompany/payNo/memo 等)+ 收货信息(reveiveName/reveiveNumber/province/city/countyseat/address,由 `address_id` 关联 `mall_shipping_address`)。
- `items`:商品明细列表,每行含 spuNo/spuName/skuNo/count/price/payMoney/payState/warehouseState。
---
## 9. mall.order.logistics.query
按订单号查询商城订单物流(发货信息 + 物流轨迹),对应老系统 `GET /mallGoodsOrder/getMallLogistics`。
- 分类:customer | 类型:读
- 数据来源:m1 库 `mall_order_logistics` + `mall_order_logistics_detai`
- 行级权限:与 `mall.order.list.query` 一致;订单不存在或无权查看时 `logistics=null` 并在 notice 说明(两者不区分)。
- **注意**:轨迹表 `logistics_order_no` 字段老系统写入时塞的是订单号而非快递单号,故按 orderNo 查询(照搬老系统语义)。物流轨迹为系统内业务节点手工写入的记录,非实时快递查询,可能不全(菜鸟定时同步已停用)。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderNo | string | 是 | 订单编号 |
**出参**:
- `logistics`(无物流记录时为 null):logisticsName(快递公司)/ logisticsOrderNo(快递单号)/ sendMode(0=快递 1=自送)/ state(0=已出单 1=已出库 2=已发货 3=已揽件 4=运输中 5=待取件 6=派送中 7=已签收)/ deliverName / deliverNumber / sendTime / receivingGoodsName / reveiveName / reveiveNumber / address。
- `tracks`:轨迹列表,按时间正序,每行含 memo(轨迹描述)/ state / picList / createTime。
---
## 10. hardware.list.query
分页查询出纸机列表,对应老系统 `POST /hardware/listByPage`(HardwareViewController)。
- 分类:hardware | 类型:读
- 数据来源:m0 库 `hardware_view` 视图,固定 `soft_delete = 0`、`id DESC`
- 行级权限(`PermissionService.applyHardwareRowFilter`,与站点同一 `getByIsAgent(propertyFlag=true, className="property_id")` 变体):
- 总部(isagent=0):全部;
- 代理商(isagent=1):`agent_id IN (自己 + 直属下级 + warehouse_agent_auth 代管代理)`,有代管物业授权时再 `OR property_id IN (agent_manage_district_auth 代管物业)`;
- 销售部门非负责人:`property_id IN (自己创建的物业 + 代管代理名下物业)`,与上一条互斥。
- 富化(修正老接口 N+1,按页批量):`agentName`/`propertyName`/`districtName` 走 Redis key `name:Agent:{id}` / `name:Properyt:{id}` / `name:District:{id}` multiGet;断纸状态 `piStatus` 批量读 `Assignment:{snCode}:29`(无 key 返回 0);本页非空 `imsi` 一次性 IN 查 `networkcard` 表。返回 `hardware_view` 全字段,不做字段级权限裁剪。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| snCode | string | 否 | sn码,模糊匹配 |
| deviceType | number | 否 | 设备类型:0=厕纸机 1=擦手纸机 |
| status | number | 否 | 设备状态:1=在线 2=故障 3=离线 |
| online | number | 否 | 上下线状态:1=上线 2=下线 |
| lockMachineState | number | 否 | 锁机状态:0=未锁机 1=已锁机 |
| agentId | number | 否 | 代理商 id,正整数,精确匹配 |
| propertyId | number | 否 | 物业 id,正整数,精确匹配 |
| districtId | number | 否 | 站点 id,正整数,精确匹配 |
| buildingId | number | 否 | 楼 id,正整数,精确匹配 |
| floor | string | 否 | 楼层,精确匹配 |
| imsi | string | 否 | imsi码,精确匹配 |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100(超出截断为 100 并在 notice 提示) |
**出参 records 字段**:`hardware_view` 全字段(snCode、deviceType、status/online/lockMachineState 状态四元组、sysversion/firmwareVersion/voiceVersion/advertiseVersion、imsi、电量/纸量/电池族、各功能开关、memo 等,时间格式 `yyyy-MM-dd HH:mm:ss`)+ 富化字段 `agentName`/`propertyName`/`districtName`/`piStatus` + 物联网卡字段 `factoryType`/`carrier`/`dataBalance`/`dataUsage`/`iccid`/`msisdn`(按 imsi 匹配,无卡为 null)。
---
## 11. hardware.detail.query
按 snCode 或 id 查询出纸机详情(二选一必填),行级权限与 `hardware.list.query` 一致。
- 分类:hardware | 类型:读
- 数据来源:m0 库 `hardware_view` + `district_district_strategy`(站点信息)+ `networkcard`(物联网卡)
- 设备不存在或无权查看时 `record=null` 并在 notice 说明(两者不区分,不泄露设备是否存在)。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| snCode | string | 条件必填 | sn码,精确匹配;与 id 二选一 |
| id | number | 条件必填 | 设备 id,正整数,精确匹配;与 snCode 二选一 |
**出参**:
- `record`:hardware_view 全字段 + 与列表同一套富化(agentName/propertyName/districtName、piStatus、物联网卡 factoryType/carrier/dataBalance/dataUsage/iccid/msisdn)。
- `record.site`(关联站点,district 表现有字段):`districtId`、`districtName`、`province`、`city`、`countyseat`、`address`、`contact`、`phone`、`buildingId`、`floor`。注意 district 表无经纬度和楼名字段,楼座/楼层取 hardware_view。
---
## 12. contract.list.query
分页查询合同列表,对应老系统 `/contract/listByPage` 合同列表页(权限库 `webpage` id=394 `contractList`,页面字段定义 `ContractListOutput`)。
- 分类:customer | 类型:读
- 数据来源:m2 库 `contract_new` 表,固定 `soft_delete = 0`、`id DESC`
- 行级权限(`PermissionService.applyContractRowFilter`,与站点/出纸机同一 `getByIsAgent(propertyFlag=true)` 变体,agent 维度落 `contract_agent_id`,property 维度落 `property_id`):
- 总部(isagent=0):全部;
- 代理商(isagent=1):`contract_agent_id IN (自己 + 直属下级 + warehouse_agent_auth 代管代理)`,有代管物业授权时再 `OR property_id IN (agent_manage_district_auth 代管物业)`;
- 销售部门非负责人:`property_id IN (自己创建的物业 + 代管代理名下物业)`,与上一条互斥。
- 富化(按页批量 IN 查询,消除 N+1):`contractAgentName`/`customerAgentName` 查 m0 `agent`,`createName` 查 m0 `user`;`saleName`/`propertyName`/`customerName`/`customerNo` 为合同表冗余快照,直接返回。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| contractNo | string | 否 | 合同编号,模糊匹配 |
| contractName | string | 否 | 合同名称,模糊匹配 |
| propertyName | string | 否 | 物业名称,模糊匹配 |
| customerName | string | 否 | 客户名称(金蝶客户名称快照),模糊匹配 |
| customerNo | string | 否 | 客户编号(=物业 serialnumber),精确匹配 |
| contractAgentId | number | 否 | 合同所属公司 id,精确匹配 |
| customerAgentId | number | 否 | 客户所属公司 id,精确匹配 |
| propertyId | number | 否 | 所属物业 id,精确匹配 |
| contractStatus | number | 否 | 合同状态:0=待审 1=驳回 2=正常 3=过期 4=终止 |
| expireRemind | number | 否 | 到期提醒开关:0=关 1=开 |
| endDateFrom | string | 否 | 合同结束日期起,格式 yyyy-MM-dd(查即将到期合同用) |
| endDateTo | string | 否 | 合同结束日期止,格式 yyyy-MM-dd(含当天) |
| page | number | 否 | 页码,从 1 开始,默认 1 |
| rows | number | 否 | 每页条数,默认 20,上限 100(超出截断为 100 并在 notice 提示) |
**出参 records 字段**:`contract_new` 主要字段(id、contractNo、contractName、contractAgentId/customerAgentId、propertyId/propertyName、customerNo/customerName、amount、contact、startDate/endDate(`yyyy-MM-dd`)、expireRemind/remindDays/remindTime/reminded、saleId/saleName、contractStatus、stopReasonType/stopReason、createId、createTime)+ 富化字段 `contractAgentName`/`customerAgentName`/`createName` + 状态文案 `contractStatusText`。
---
## 13. contract.detail.query
按 id 或 contractNo 查询合同详情(二选一必填),行级权限与 `contract.list.query` 一致。
- 分类:customer | 类型:读
- 数据来源:m2 库 `contract_new` + `contract_file`(附件)+ `contract_process`(流程/待办/操作记录)
- 合同不存在或无权查看时 `record=null` 并在 notice 说明(两者不区分,不泄露合同是否存在);id/contractNo 都未传时 notice 提示"必须传一个"。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 条件必填 | 合同 id;与 contractNo 二选一 |
| contractNo | string | 条件必填 | 合同编号,精确匹配;与 id 二选一 |
**出参**:
- `record`:与列表同一套字段(含富化和 `contractStatusText`)。
- `files`:合同附件列表,`id`/`fileName`/`fileUrl`/`createTime`,按 id 正序。
- `processes`:审批流程记录,按 id 正序,字段 `nodeType`(0=创建 1=部门内审核 2=层级审核 3=终止 4=抄送)、`auditLevel`、`operationType`(0=创建 1=审核通过 2=审核驳回 3=终止 4=修改再提交 5=抄送)、`userId`/`userName`、`scheduleState`(0=无需待办 1=待办 2=已办)、`remark`、`operateTime`,均附 `nodeTypeText`/`operationTypeText`/`scheduleStateText` 文案。
---
## 14. integration.context.get
身份与能力自检(REQ-20260911-0012):返回当前 Token 的集成上下文。所有数据每次调用实时取库,不缓存权限。
- 分类:integration | 类型:读
- 数据来源:svc 库 `agent_api_key` / `token_scope` / `agent_scope` + ToolGuard 实时能力计算(与 tools/call 强制校验同源)
**入参**:无
**出参**:
| 字段 | 类型 | 说明 |
|---|---|---|
| tenant_id | string | Token 所属数据域标识 = 启用状态的作用域编码(排序后逗号拼接;当前签发规则为一作用域一 Token) |
| principal_type | string | `user_delegated`(用户委托)/ `tenant_service`(服务主体)。当前按 token 绑定用户推导;`agent_api_key.principal_type` 列由后续需求(5629)补充后切换为读列 |
| subject_id | number | 用户 ID 或服务主体 ID |
| scopes | string[] | 该 Token 允许调用的工具名集合(排序),与 tools/call 强制校验结果严格一致 |
| data_scope_version | number | 数据权限版本:token_scope 绑定集合最大 modify_time(空取 create_time)的 epoch 毫秒;无绑定为 0 |
| capability_levels | string[] | `QUERY` / `SYNC`:能力集合中含 `*.changes.list` 工具时具备 SYNC(数据驱动);无能力时为空数组 |
| server_time | string | 带时区的服务器时间(ISO_OFFSET_DATE_TIME) |
---
## 15. warehouse.out.list.query
分页查询出库单,参考旧系统 `POST /myWarehouseOut/allListByPage`,但修复其出库日期误映射为申请日期、
部分渠道未过滤和逐单查询明细的问题。
- 分类:warehouse | 类型:读
- 数据来源:m2 `warehouse_out`、`warehouse_out_details`、`warehouse_out_audit_process`
- 行级权限:总部看全部;代理身份可看主单所属代理为自己/直属下级/授权代管代理的出库单,或明细涉及
上述代理所属/负责管理仓库的出库单,或本人参与审核/处理的出库单。三类条件取并集;筛选条件只能缩小结果。
- 列表关联数据按本页主单 ID 批量查询,不逐单 N+1 查询。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| outOrderNo / allocationOrderNo / mallOrderNo | string | 否 | 出库/调拨/商城单号,模糊匹配 |
| deliveryOrderNo / installOrderNo / pickupOrderNo | string | 否 | 送货/装机/取货工单号,模糊匹配 |
| agentId / warehouseId / warehouseTypeId / applyUserId | number | 否 | 所属代理/涉及仓库/出库类型/申请人 ID,精确匹配 |
| orderState | number | 否 | 0=待审核 1=被驳回 2=待出库 3=已完成 4=已撤销 5=作废 |
| sendMode | number | 否 | 0=快递公司 1=自行派送 |
| outSynchronizeState | number | 否 | 0=未同步 1=已同步 2=无需同步 |
| assetSynchronizeState | number | 否 | -1=无需同步 0=未同步 1=已同步 |
| useCustomerType / useCustomerId | number | 否 | 客户类型(0内部/1渠道商/2直营)和客户 ID |
| useCustomerName | string | 否 | 客户名称,模糊匹配 |
| outDateFrom / outDateTo | string | 否 | 出库日期范围,`yyyy-MM-dd`,结束日期含当天 |
| applyDateFrom / applyDateTo | string | 否 | 申请日期范围,`yyyy-MM-dd`,结束日期含当天 |
| page / rows | number | 否 | 页码默认 1;每页默认 20、上限 100 |
**出参 records 字段**:主单 `id/outOrderNo/allocationOrderNo/mallOrderNo`、`sourceType`
(NORMAL/ALLOCATION/MALL)、代理和出库类型、`warehouseIds/warehouseNames`、出库/商品数量、状态码与文案、
申请人/时间、客户、`schedulePeople`、`warehouseOutPeople/warehouseOutTimes`、发货物流、同步状态、
关联送货/装机/取货单号、备注及创建/修改时间。
---
## 16. warehouse.out.detail.query
按主键或出库单号查询完整出库单详情;调拨出库单可直接查询,以 `sourceType=ALLOCATION` 标识。
- 分类:warehouse | 类型:读
- 行级权限与 `warehouse.out.list.query` 完全一致,并加入同一个主表查询。
- 不存在或无权访问统一返回 `NOT_FOUND_OR_FORBIDDEN`,不泄露记录是否存在。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 条件必填 | 出库单主键;与 outOrderNo 至少传一个 |
| outOrderNo | string | 条件必填 | 出库单号;与 id 至少传一个;同时传入时必须匹配同一条记录 |
| includeSerials | boolean | 否 | 是否返回 SN 子行,默认 false |
| serialLimit | number | 否 | SN 返回上限,默认 100、最大 500 |
**出参**:
- `record`:主单、来源类型、状态文案、客户、同步状态及关联工单号。
- `warehouses`:物料汇总行涉及的仓库去重集合。
- `items`:`parent_id=0` 物料汇总行,含物料、仓库、数量、价格和出库确认信息。
- `serials/serialTotal/serialTruncated`:可选 SN 子行、完整总数和截断标记;未请求 SN 时
`serials=[]`、`serialTruncated=false`。
- `processes`:已处理记录(`schedule_state=2`),按操作时间、ID 正序,状态码与文案同时返回。
- `address/logistics`:m1 收货地址及发货物流;无地址时 `address=null`。
- `files.application/files.confirmation`:申请/确认图片,返回 `id/fileName/bucket/objectKey`;服务不持有
OSS 签名密钥,不生成临时访问 URL。
- `certificate`:出库凭证备注及凭证文件;均不存在时为 null。
详细设计与取舍见 `warehouse-out-mcp-tools-design.md`。
---
## 17. warehouse.in.list.query
分页查询入库单,参考旧系统 `POST /myWarehouseIn/allListByPage`,但将入库日期与申请日期分别过滤,
并按本页主单 ID 批量加载仓库和待办信息。
- 分类:warehouse | 类型:读
- 数据来源:m2 `warehouse_in`、`warehouse_in_details`、`warehouse_in_audit_process`
- 行级权限:总部看全部;代理身份可看所属代理范围、所属或负责仓库涉及、或本人参与处理的入库单。
- 单号支持入库/调拨/关联出库/商城/送货/装机/取货/撤机;另支持代理、仓库、入库类型、申请人、
状态、同步状态、物流和入库/申请日期范围筛选。
- 返回 `total/page/rows/records`;每条记录含 `sourceType`(ALLOCATION/OUTBOUND_LINKED/MALL/NORMAL)、
状态文案、仓库集合、待办人、实际入库人和入库日期。
## 18. warehouse.in.detail.query
按 `id` 或 `inOrderNo` 查询详情,两者至少传一个;同时传入时必须匹配同一条可见记录。调拨入库单可直接查询。
- 行级权限与列表完全一致;不存在和无权统一返回 `NOT_FOUND_OR_FORBIDDEN`。
- 返回 `record/sceneType/warehouses/items/processes/files/relatedOutOrder`。
- `items` 按物料定义、物料索引和仓库汇总;`includeSerials` 默认 false,`serialLimit` 默认 100、上限 500,
同时返回 `serialTotal/serialTruncated`。
- `files.application/files.confirmation/files.allocationAudit` 仅返回 `id/fileName/bucket/objectKey`。
调拨派生的入库单其申请附件与审批流图片挂在调拨单维度(`aggregate_id=调拨单id, in_out_allocation_type=2`),
已按 `allocationOrderNo` 自动回查合并:申请附件并入 `application`,审批流图片(fileType=3)在 `allocationAudit`。
- `relatedOutOrder` 仅给出关联出库单号和查询工具名,不内嵌出库单内容。
详细设计见 `warehouse-in-mcp-tools-design.md`。
---
## 19. warehouse.todo.list.query
统一查询当前登录人的仓库待办,参考老系统 `/myWarehouseOut/needToBeDealtWith`、
`/myWarehouseIn/needToBeDealtWith`、`/warehouseAllocation/warehouseAllocationProcessListByPage` 三个待办接口。
- 分类:warehouse | 类型:读
- 数据来源:m2 流程视图 `warehouse_out_process` / `warehouse_in_process` / `warehouse_allocation_process`
- 权限:待办是"人"的语义,只按流程表 `user_id = 当前登录人` 过滤,不叠加代理商行级权限。
- 待办判定:`user_id = 当前登录人 AND soft_delete = 0`;出入库视图排除
`operation_type=3`(纯日志),调拨视图不过滤 `operation_type`(其 3=本人修改是真实待办)。
`todoOnly` 默认 true 再限定 `schedule_state=1` 真待办,且该条件下推到第二阶段组装查询,
代表行不串到已办审批行。
- 同一笔调拨最多以调拨审批/确认、出库执行、入库执行 3 个视角出现,不去重;
派生自调拨的真实出入库单带 `derivedFrom="allocation"`,可按 `allocationOrderNo` 归并。
- 分页实现:先全量查符合条件流程行按单据 id 内存去重 → 内存分页 → 按页 id 批量查视图行组装,防 N+1。
**入参**:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| docType | string | 否 | out=出库单 in=入库单 allocation=调拨/采购/划转单 all=全部,默认 all |
| todoOnly | boolean | 否 | true=只返回真待办(默认);false=当前用户参与过的全部单据 |
| orderState | number | 否 | 出库/入库单 0待审核 1被驳回 2待出库或待入库 3已完成 4已撤销 5作废;调拨类 0待审核 1被驳回 2待出库 3待入库 4已完成 5已撤销 6作废 |
| orderNo | string | 否 | 单据主单号模糊匹配 |
| page / rows | number | 否 | 页码默认 1;每页默认 20、上限 100,超出截断并在 `notice` 提示 |
**出参 records 字段**:`docType/docTypeText`(出库单/入库单/调拨单/采购单/划转单,
按调拨视图 `op_type` 区分)、`docId/docNo`、`orderState/orderStateText`、
`scheduleState/scheduleStateText`、`operationType/operationTypeText`(当前待办环节)、
仓库名(allocation 为转出/转入仓库)、`agentId/agentName`、申请人/申请时间、
`allocationOrderNo` 等关联单号、派生单 `derivedFrom="allocation"`;
调拨类另含 `opType/opTypeText/allocationCount/nextUserName/inUserName/outUserName`。
按申请时间倒序排列。
详细设计与老系统差异见 `warehouse-todo-mcp-tool-design.md`。
---
## 20. delivery.order.list.query
分页查询送货工单,对应旧系统 `POST /delivery/deliveryListByPage`。
- 分类:workorder | 类型:读
- 数据来源:m2 `delivery_order`。
- 入参:`deliveryOrderNo`、`sourceOrderNo`、`agentId`、`districtName`、`orderType`、
`orderState`、`sendMode`、`applyUserName`、申请/完成日期范围及 `page/rows`。
- `orderType`:0=装机单送货,1=出库单送货;`orderState`:0=派送中,1=已完成,2=已取消;
`sendMode`:0=快递公司,1=自行派送。
- 申请日期按业务字段 `apply_time` 查询,不沿用旧接口的 `create_time`;申请与完成日期可同时过滤。
- 行级权限:总部看全部;代理身份可看申请代理或所属代理在自己、授权代管范围内的工单(不含直属下级),
以及授权代管物业和本人参与流程的工单;销售非负责人按其可见物业范围过滤。
- 返回 `total/page/rows/records`,固定按 ID 倒序;记录同时返回状态、类型、配送方式的码值与文案。
## 21. delivery.order.detail.query
按 `id` 或 `deliveryOrderNo` 查询详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
- 行级权限与列表完全一致;不存在和无权统一返回 `NOT_FOUND_OR_FORBIDDEN`。
- `record`:送货主单和文案字段。
- `logistics`:物流公司/单号或派送员信息、物流状态;无物流记录时为 null。
- `goods`:m2 `install_distribute_details` 配发物品,含物料、SN、数量、出入仓库和商城订单号。
- `shippingFiles`:m1 `mall_logistics_pic` 发货图片。
- `processes`:`schedule_state=2` 的流程记录及 m2 `warehouse_in_out_pic` 签收附件,按操作时间、ID 正序。
- `relatedDocuments`:m2 `install_to_out_warehouse` 关联的出库、入库、调拨或划转单。
- 所有附件仅返回 `id/fileName/bucket/objectKey`,不拼接公开 URL。
详细设计见 `delivery-order-mcp-tools-design.md`。
---
## 22. install.order.list.query
分页查询装机工单,对应旧系统 `POST /installOrder/pageList`。
- 分类:workorder | 类型:读
- 数据来源:m2 `install_order`、`install_order_details`。
- 入参:`installOrderNo`、`mallOrderNo`、`scheduleTransferOrder`、`deliveryOrderNo`、
`pickupOrderNo`、`installType`、`orderState`、`agentId`、`districtName`、`applyUserName`、
`maintenanceUserName`、`cooperateUserName`、`applyTimeFrom/applyTimeTo`、`page/rows`。
- 时间范围首版只保留申请日期,格式 `yyyy-MM-dd`,结束日期含当天;不接受自由排序,固定
`apply_time DESC, id DESC`。
- 行级权限:总部看全部;普通代理可看自己、授权代管公司、授权代管物业及本人参与的工单,
不包含直属下级代理;销售非负责人只看自己创建及授权代管公司名下物业的工单。
- 返回 `total/page/rows/records`;每条记录含 `installTypeText`、`orderStateText` 和
`totalInstallCount`(本单设备明细安装数量合计)。
## 23. install.order.detail.query
按 `id` 或 `installOrderNo` 查询装机工单详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
`includeProcess` 默认 true,可关闭流程聚合。
- 行级权限与列表完全一致;不存在与无权访问统一返回 `record=null`,防止枚举。
- `record/details/totalInstallCount`:主单、设备明细及安装数量合计。
- `maintenanceUsers/cooperateUsers`:当前装机运维和协同人员。
- `address`:m1 收货地址;`mallOrder`:m1 商城订单及商品明细聚合。
- `processes`:已办理且非抄送的流程,按操作时间、ID 正序;附流程人员、配发物品/SN、
出入库关联单及图片凭证 URL。
- `processLimit` 默认 100、最大 500;同时返回 `processTotal/processTruncated`。
详细设计见 `install-order-mcp-tool-design.md`。
---
## 24. pickup.order.list.query
分页查询取货工单,对应旧系统 `POST /pickup/pickupListByPage`。
- 分类:workorder | 类型:读
- 数据来源:m2 `pickup_order`,物流来自 m1 `mall_order_logistics`。
- 入参:`pickupOrderNo`、`installOrderNo`、`withdrawOrderNo`、`agentId`、`districtName`、
`type`、`orderState`、`sendMode`、`applyUserName`、申请/取货/完成日期范围及 `page/rows`。
- `type`:0=装机发起,1=人工发起,2=撤机发起;`orderState`:-2=被驳回,-1=待审核,
0=待取货,1=待入库,2=已完成,3=已取消;`sendMode`:0=快递公司,1=自行派送。
- 申请日期按 `apply_time` 查询;三组日期范围可同时过滤,结束日期含当天。
- 行级权限:总部看全部;代理身份可看申请代理或所属代理在自己、授权代管范围内的工单,
以及授权代管物业和本人参与流程的工单;销售非负责人按其可见物业及本人参与流程过滤。
- 返回 `total/page/rows/records`,固定按 ID 倒序;物流按本页单号一次批量查询。
## 25. pickup.order.detail.query
按 `id` 或 `pickupOrderNo` 查询详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
- 行级权限与列表完全一致;不存在和无权统一返回 `NOT_FOUND_OR_FORBIDDEN`。
- `record/logistics`:取货主单、状态/来源文案及物流信息。
- `pickupGoods`:m2 `pickup_order_details` 中 `type IN (0,2)` 的原取货物品。
- `serialChanges`:同表 `type=1` 的取货后录入或调整 SN 明细。
- `shippingFiles`:m1 `mall_logistics_pic` 物流取货图片。
- `processes`:`schedule_state=2` 的流程记录及 m2 `warehouse_in_out_pic` 取货/入库凭证。
- `relatedDocuments`:m2 `install_to_out_warehouse` 关联的出库、入库、调拨或划转单。
- 同时返回关联装机单、撤机单及维修工单号;附件仅返回 `id/fileName/bucket/objectKey`。
详细设计见 `pickup-order-mcp-tools-design.md`。
---
## 26. relocation.order.list.query
分页查询移机工单,对应旧系统 `POST /relocationOrder/pageList`。
- 分类:workorder | 类型:读
- 数据来源:m2 `relocation_order`。
- 入参:`relocationOrderNo`、`agentId`、`districtName`、`applyUserName`、
`maintenanceUserName`、`cooperateUserName`、`orderState`,申请/审核/调度/完成日期范围及 `page/rows`。
- 状态:0=待提交、1=待审核、2=被驳回、3=调度中、4=作业中、5=已完成、6=已取消。
- 日期格式为 `yyyy-MM-dd`,结束日期含当天;分页默认 1/20、上限 100,固定按 ID 倒序。
- 行级权限:总部看全部;代理身份按本代理、授权代管代理/物业过滤;销售非负责人按自己创建及代管代理物业过滤;
流程参与者和 `install_order_user.type=2` 的移机/协同人员也可查看。
## 27. relocation.order.detail.query
按 `id` 或 `relocationOrderNo` 查询详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
- 行级权限与列表完全一致;不存在和无权统一返回 `NOT_FOUND_OR_FORBIDDEN`。
- `record`:移机主单及状态文案。
- `relocationDetails`:设备、源/目标位置、卫生间类型、执行状态和备注。
- `maintenanceUsers/cooperateUsers`:当前移机和协同人员。
- `processes`:`order_type=4`、`schedule_state=2` 且非抄送的流程,按操作时间、ID 正序,并附流程人员。
- `completionCertificates`:结单流程的 `warehouse_in_out_pic` 凭证,限定
`in_out_allocation_type=15`,仅返回 `id/fileName/bucket/objectKey`。
详细设计见 `relocation-order-mcp-tools-design.md`。
## 28. withdraw.order.list.query
分页查询撤机工单,对应旧系统 `POST /withdrawOrder/pageList`。
- 分类:workorder | 类型:读
- 数据来源:m2 `withdraw_order`。
- 入参:`type`、`orderState`、`withdrawOrderNo`、`pickupOrderNo`、`agentId`、`districtName`、
`applyUserName`、`maintenanceUserName`、`cooperateUserName`,申请/审核/调度/取货/完成日期范围及 `page/rows`。
- 类型:0=部分撤机、1=全部撤机;状态:0=待审核、1=被驳回、2=调度中、3=作业中、4=待取回、5=已完成、6=已取消。
- 日期格式为 `yyyy-MM-dd`,结束日期含当天;分页默认 1/20、上限 100,固定按 ID 倒序。
老系统 `finishDate` 过滤误生成不存在的 `finish_date` 列(查询报错),MCP 侧已修正映射到 `finish_time`。
- 行级权限:总部看全部;代理身份按申请代理/所属代理(本代理+授权代管)、授权代管物业过滤,
且本人出现在 `order_process(order_type=1)` 的工单可见;销售非负责人按自己创建及代管代理物业过滤。
## 29. withdraw.order.detail.query
按 `id` 或 `withdrawOrderNo` 查询详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
- 行级权限与列表完全一致(老系统 lookDetails 无权限校验,MCP 侧已补齐);不存在和无权统一返回 `NOT_FOUND_OR_FORBIDDEN`。
- `record`:撤机主单(期望/实际撤机日期、撤机理由、联系人、项目地址等)及状态/类型文案。
- `withdrawDetails`:`withdraw_order_details` 中 `in_count>0` 的实际取回物品明细。
- `maintenanceUsers/cooperateUsers`:`install_order_user type=1` 的运维/协同人员。
- `processes`:`order_type=1`、`schedule_state=2` 且非抄送(theme=12)的流程,按操作时间、ID 正序,主题/类型给 code+文案。
- `certificates`:凭证附件,两个来源——取货/入库签收凭证查派生取货工单的
`pickup_order_process`(operation_theme 1/2)再按 `aggregate_id + in_out_allocation_type=12`
查 `warehouse_in_out_pic`(`source=pickup_process`);发货附件查 m1
`mall_logistics_pic(order_no=取货单号)`(`source=logistics`)。仅返回 `id/fileName/bucket/objectKey`。
- 撤机结单后自动生成取货工单(`pickup_order.type=2`),可用 `pickup.order.list.query` 的 `withdrawOrderNo` 参数串联查取货/入库进度。
详细设计见 `withdraw-order-mcp-tools-design.md`。
---
## 30. district.paper.statistics.query
客户用纸统计,1:1 复刻老系统 `POST /MonthlyStatistics/getMonthlyStatisticsMap`(MonthlyStatisticsController),
分主体统计(DB)与换纸卷数(ES)两部分。
- 分类:customer | 类型:读
- 主体数据来源:m0 `monthly_statistics`(XXL-Job 预聚合的站点日汇总,每站点每厕位/厕每天一行),
`statistics_time` 范围 [startDate 00:00:00, endDate 23:59:59],可选 `buildingId/floor/toiletId/deviceType` 精确过滤。
- 入参:`districtId`(必填正整数)、`startDate`/`endDate`(必填 yyyy-MM-dd,startDate≤endDate≤今天)、
`positionMode`(position=仅厕位/wall=仅厕墙,只作用于 ES 换纸卷数口径)、`deviceType`(0/1)、`buildingId`、`floor`、`toiletId`。
- 聚合维度 gender(0男1女2母婴3残卫4第三5其他99未知):
`paperLengthM`=Σpaper_length/100(米,3 位小数)、`useCount`=Σuse_count(仅厕位模式 runl_mode=0 的厕位行累计,厕墙行恒 0)、
`deviceDayCount`=按日 distinct 厕位/厕数之和(老系统 perPositionsCount,输出改名)、
`perUseLengthM`=paperLengthM/useCount(useCount=0 为 null);另输出按日明细(date/gender/paperLengthM/useCount/deviceDayCount)。
- 换纸卷数 `rfidCount`(ES):读 `district_strategy.rfid_use_flag`(行 id=站点 id)——
=1 时 state∈{2上纸,4撤纸} 合并后 rfid 去重数(terms 聚合 size 10000 统计 bucket 数,超出部分不计);
≠1 或记录缺失时取 state=2 精确 count(不含撤纸、不去重)。仅对 DB 结果中出现过的 gender 逐组查询。
- ES 口径:索引按 startDate 距今 90 天分冷热(`paperuselog` / `paperuselog_all`,老 isHotTime 硬编码);
集群按 Redis `Dictionary.LocalEsDate`(yyyy-MM-dd)分界——分界日 > endDate 走本地集群,
startDate < 分界日 < endDate 拒绝查询;`Dictionary.StatisticsDays` 限制查询天数上限;字典缺失 fail fast 抛 `IllegalStateException`。
endDate=今天时 ES 上界取今天 00:00(当天数据未跑完),否则 23:59:59(含边界)。
- 行级权限:站点可见性沿用 `district.detail.get` 模式(id + soft_delete=0 + applyDistrictRowFilter),
查不到统一抛 `站点不存在或无权访问`。
- 与老系统的差异(详见 `district-paper-statistics-design.md`):ES 异常不静默为 0 而是抛错;
rfid 去重用 terms 聚合替代 scroll 拉全量;district_strategy 缺失/空 rfid_use_flag 按"未使用 rfid"口径而非 NPE。
---
## 31. battery.fault.list.query
分页查询电池故障记录,对应旧系统 `POST /batteryFault/batteryFaultLogList`。
- 分类:hardware | 类型:读
- 数据来源:m0 `battery_fault_log`,固定 `soft_delete=0`。
- 入参:`districtId/propertyId/agentId`(正整数)、`snCode`(模糊匹配)、`deviceType`(0 厕纸机/出纸机、1 擦手纸机)、`faultType`(0 温度、1 电压、2 电流、3 充电次数)、`faultState`(0 未恢复、1 已恢复)、`faultDateFrom/faultDateTo`(yyyy-MM-dd,结束日期含当天)、`page/rows`。
- 分页默认 1/20、上限 100,固定按 `fault_start_time DESC, id DESC` 稳定排序。
- 行级权限与 `hardware.list.query` 一致:总部查看全部;代理查看自己、直属下级、代管代理及代管物业;销售非负责人限自己创建及授权范围内物业。服务身份仍由 MCP scope 控制。
- 返回 `id/snCode/deviceType/deviceTypeText/faultType/faultTypeText/faultState/faultStateText/peakValue/standardValue/faultStartTime/faultAddTime/faultRecoveryTime/installPosition/agentId/agentName/propertyId/propertyName/districtId/districtName`。时间按 Asia/Shanghai 输出;峰值和标准值保留数据库原始整数,不推断单位。
详细设计见 `battery-fault-mcp-tool-design.md`。
---
## 32. hardware.electricity.message.list.query
分页查询设备电量/信号/温度/充电循环次数/电压/电流消息记录,对应旧系统
`POST /batteryAnalyse/batteryAnalyseList`(BatteryAnalyseController)的数据查询语义,并修掉其坑。
- 分类:hardware | 类型:读
- 数据来源:本地 ES 集群 `electricitymessage_yyyyMM` 月索引(索引名 = electricitymessage_ + startTime 月份 yyyyMM);
**只走 esLocal 客户端**(索引只在本地集群,不走 esCloud);**无任何权限校验**(对齐老系统,用户拍板 2026-09-21)。
- 入参:`snCode`(必填非空)、`startTime`/`endTime`(必填,严格 `yyyy-MM-dd HH:mm:ss`,
**服务端强校验两者同一天,跨天直接拒绝**——老系统跨月范围静默丢数据、只按开始时间月份选索引;
startTime 不得晚于 endTime)、`messageType`(可选 0-5,0 电量/1 信号/2 温度/3 充电循环次数/4 电压/5 电流,
**不传=查全部类型**——老系统默认值 0 把"不传"歧义成"电量",此处为刻意的显式设计差异)、
`page`/`rows`(默认 1/500,rows 上限 1000,超出截断为 1000 并返回 notice)。
- 查询构造:`snCode` 用 **match_phrase**(老系统 matchQuery 经 standard analyzer 会把 AB-123 切成 ab/123 误配);
messageType 传了才加 term;createTime 闭区间 range;createTime ASC 排序;from+size 分页(老系统 scroll 拉全量后内存抽样,本工具全量分页、不做抽样)。
- 深分页防御:from+size 超 ES `max_result_window`(10000) 时截断为窗口内可用范围并在 notice 说明(整页越过窗口截断为最后可用窗口;部分越界截断本页条数)。
- 返回 `snCode/index/total/page/rows/records`;record 含 `createTime`(yyyy-MM-dd HH:mm:ss,
epoch 毫秒形态归一为 Asia/Shanghai 文本)、`messageType`、`messageTypeText`(服务端映射)、`value`(原始 Integer,不加单位标注)。
- 与老系统的差异(详见 `electricity-message-mcp-tool-design.md`):ES 异常不静默为返回空,抛错 fail fast。
---
## 33. district.paper.position.detail.query
客户用纸详情,复刻老系统 `POST /MonthlyStatistics/getMonthlyStatisticsPerPositionMap`
(MonthlyStatisticsController :404-512),按 位置(厕位/厕墙)× 日期 输出用纸明细。
- 分类:district | 类型:读
- 数据来源:m0 `monthly_statistics`(XXL-Job 预聚合日汇总),**纯 MySQL 不查 ES**;
`statistics_time` 左闭右开 [startDate 00:00:00, endDate+1 00:00:00),可选 `buildingId/floor/toiletId/deviceType` 精确过滤,
`positionMode`(position=仅厕位 `position_id>0` / wall=仅厕墙)对齐 statistics 工具语义,不沿用老 detail 接口 positionId 0/1 的混乱口径。
- 入参:`districtId`(必填正整数)、`startDate`/`endDate`(必填 yyyy-MM-dd,startDate≤endDate≤今天);
日期区间硬限 31 天(一个月,2026-09-24 拍板),且受 Redis `Dictionary.StatisticsDays` 限制,字典缺失 fail fast。
- 返回扁平 `rows[]`:`date/locationType(position|wall)/positionId/toiletId/locationName/gender/genderText/`
`snCodes/paperLengthMeters/useCount/perUseLengthMeters`;排序 date 升序、locationType、positionId/toiletId 升序。
- 指标口径:`paperLengthMeters`=Σpaper_length/100(米,2 位小数,**全程 Double**——修老 :452 合并分支整数除法
逐次截断 bug);`useCount`=Σuse_count **仅厕位行累计**(厕墙恒 0);`perUseLengthMeters`=paperLengthMeters/useCount
(useCount=0 为 null,厕墙不适用而非老系统的硬编码 0.0);`snCodes`=组内 distinct sn_code 去重排序。
- 维度字段恒在、不适用为 null:厕墙行 `positionId=null`;`gender` 透传库内原值,
genderText 映射 0男卫/1女卫/2母婴/3残卫/4第三/5其他,99 及未知编码为 null。
- 位置命名:直查 m0 `positions.memo`(回退 name)/ `toilet.no`(回退 name)批量富化(新增极简实体+Mapper,
不读 Redis 实体缓存),查不到给"未知";无数据的 日期×位置 不出行、不补 0。
- 行级权限:站点可见性沿用 `district.paper.statistics.query` 模式(id + soft_delete=0 + applyDistrictRowFilter),
查不到统一抛 `站点不存在或无权访问`(老 detail 接口无行级权限,MCP 版补齐)。
详细设计见 `paper-position-detail-mcp-design.md`。
---
## 34. district.paper.percapita.query
查询站点人均用纸统计,覆盖老 `StatisticsReportController` 的五个接口。
- 分类:customer | 类型:读
- 数据来源:m0 `monthly_statistics` 日汇总表,固定 `soft_delete=0`、指定站点及日期范围;不查 ES。
- 入参:`districtId`、`startDate`、`endDate`、`groupBy`(gender/floor/floorGender),以及可选
`daily`、`deviceType`、`idStrList`。`floorGender+daily=true` 会报参数错误。
- `idStrList` 每项严格使用 `buildingId_floor_toiletId` 格式;building、floor、toilet 三列分别独立 IN 过滤,
保留老系统语义。
- 口径:`paperLengthMeters=Σpaper_length/100`,`perCapita=paperLengthMeters/Σuse_count`,保留两位小数;
useCount=0 时 perCapita 取 paperLengthMeters。
- 返回固定为扁平 `rows`:每行包含 `date/floor/gender/paperLengthMeters/useCount/perCapita`,
未参与的维度为 null;无数据日期或分组不补零。`daily=false` 另返回区间 `totalPerCapita`。
- gender 文案:0男卫、1女卫、2母婴室、3无障碍、4第三卫、5其他。
- 行级权限:先用 `applyDistrictRowFilter` 校验站点可见性;不可见统一返回“站点不存在或无权访问”。
- 日期不得晚于今天;查询区间硬限 31 天(一个月,2026-09-24 拍板),且受 Redis `Dictionary.StatisticsDays` 限制,两者取更严。
- scope:仅绑定 `service_enabled=0` 的用户委托作用域,不向服务身份作用域开放。
详细设计见 `paper-percapita-statistics-mcp-design.md`。
---
## 35. district.paper.device_average.query
查询站点设备日均用纸统计,覆盖老 `StatisticsReportController` 的五个硬件统计接口。
- 分类:customer | 类型:读
- 数据来源:m0 `monthly_statistics`,固定 `soft_delete=0`、指定站点和日期范围,在数据库中完成聚合;不查 ES。
- 入参:`districtId`、`startDate`、`endDate`、`view`,以及可选的 `deviceType`、`buildingIds`、
`floors`、`toiletIds`。位置列表内部使用 IN,三个位置维度之间使用 AND。
- `view` 固定为:`gender`(卫生间类型)、`daily_gender`(日期×卫生间类型)、`floor`(楼层)、
`daily_floor`(日期×楼层)、`floor_gender`(楼层×卫生间类型)。
- 口径:`paperLengthMeters=SUM(COALESCE(paper_length,0))/100`,
`recordCount=COUNT(*)`,`averagePaperMeters=paperLengthMeters/recordCount`,最终按 HALF_UP 保留两位小数。
`use_count` 不参与该指标计算。
- 返回 `summary` 和统一的 `rows`。每行包含
`date/floor/genderCode/gender/paperLengthMeters/recordCount/averagePaperMeters`,未参与当前 view 的维度为 null;
`daily_*` 不补缺失日期。无数据时 `rows=[]`,summary 平均值为 null,并在 notice 说明。
- gender 文案:0男卫、1女卫、2母婴室、3无障碍、4第三卫、5其他;null 为未配置,其他整数显示 `未知(code)`。
- 行级权限:先用 `applyDistrictRowFilter` 校验站点可见性;不可见统一返回 `NOT_FOUND_OR_FORBIDDEN`。
tenant_service 仅可通过 `ops-query` 调用,按总部视角查询。
- 日期不得晚于上海时区当天;查询区间硬限 31 天(一个月,2026-09-24 拍板),且受 Redis `Dictionary.StatisticsDays` 限制,两者取更严。
示例响应:
```json
{
"districtId": 12610,
"districtName": "望京SOHO",
"dateRange": {"startDate": "2026-09-01", "endDate": "2026-09-21", "days": 21},
"view": "gender",
"unit": "米/设备日统计记录",
"summary": {"paperLengthMeters": 250.00, "recordCount": 200, "averagePaperMeters": 1.25},
"total": 1,
"rows": [
{"date": null, "floor": null, "genderCode": 0, "gender": "男卫",
"paperLengthMeters": 250.00, "recordCount": 200, "averagePaperMeters": 1.25}
],
"notice": "统计分母为 monthly_statistics 记录数 COUNT(*),不是使用人次 SUM(use_count)"
}
```
---
## 36. hardware.device.lifecycle.query
按 SN 查询设备生命周期当前状态,复刻老系统 `/lifeCycle/lifeCycleListByPage`。
完整设计见 [device-lifecycle-mcp-tool-design.md](device-lifecycle-mcp-tool-design.md)。
- 分类:hardware | 类型:读
- 数据来源:m0 `device_life_cycle`,固定 `soft_delete=0`,`sn_code` 模糊匹配,id 升序分页。
- 入参:`snCode`(必填,非空,模糊匹配);`page`(默认 1);`rows`(默认 20,上限 100,超出截断并在 notice 说明)。
- 返回五组状态及中文文案:流转(exchangeState,含上一步 upExchangeState)、在途(roadState)、
在库(inventoryState)、联网(networkState)、资产(assetState);
另有生产/出厂/首用/报废/重工日期、归属公司(assetOwner)、最近流转人与时间、
最近在线时间与经纬度、金蝶同步状态。
- 在途设备(roadState=1 且 roadDate 非空)返回 `roadDays`(在途天数)与 `roadDaysFlag`(超期标记);
阈值取 Redis Hash `Dictionary.MaxRoadDays`,缺失或非法时 fail fast 抛错,不静默兜底。
- roadState/inventoryState 含 `-1=初始化`;未知枚举值返回 `未知(code)`,不抛错。
- 与老系统差异:不做任何行/字段权限过滤;不复制老系统 `exchange_state∈(0,1)` 的排除,全状态可见。
## 37. hardware.device.lifecycle.detail.query
按 SN 查询设备生命周期事件时间线,复刻老系统 `/assetDepreciationSet/lookDetail`。
- 分类:hardware | 类型:读
- 入参:`snCode`(必填,非空,精确匹配,自动去空格)。
- 事件来源(全部固定 `soft_delete=0`):
- 未生产:m0 `hardware_sncode` 生产档案(订单号/批次/物料/NFC/软硬件版本,时间取建档时间);
- 生产中:`hardware_sncode.unbing_time`(解绑时间);
- 生产完成:m0 `hardware_check_record`(时间取 modify_time;老系统标题「生成完成」系笔误,改为「生产完成」);
- 入库/重工:m2 `warehouse_in_details`(`order_state=3` 已确认;仓库号 `1000000998` 为重工仓,标题改为「重工」);
- 出库/出厂:m2 `warehouse_out_details`(`order_state=3`;仓库号 `BJ001` 为出厂仓,标题改为「出厂」),
附发货信息(优先 `mall_order_no` 查 m1 `mall_order_logistics`:sendMode=0 快递返回物流公司/单号,
1 自行派送返回派送人,其余不返回;物流公司名直接用冗余列 `logistics_name`,不 join mall_logistics);
- 安装/上线/下线:m0 `hardware_install_log`(logType 0/1/2);
- 备用:ES 云集群 `hardwarelog` 索引(term snCode + term logType=备用机操作日志,id DESC,上限 1000;
低阶 RestClient 实现,生产 ES 7.7.1 与高阶客户端不兼容;ES 异常 fail fast)。
- 事件统一为 `{type,title,time,fields}`,按操作时间倒序;备用机日志时间文本解析回时间参与排序,解析失败沉底。
- 代理/物业/站点/仓库/操作人名称批量 `selectBatchIds` 解析(避免老代码逐条查询的 N+1),解析不到返回 null 不抛错。
- SN 在 Redis Set `SpecialSncode` 中跳过备用机日志查询(对齐老系统)。
- SN 不存在时返回 `found=false`、`events=[]` 并在 notice 说明,不报错。
- 与老系统差异:不做代理角色限制;发货图片不返回。
---
## 38. district.traffic.statistics.query
查询站点人流量统计(人次,SUM(use_count)),复刻老 `StatisticsReportController.trafficStatistics`(饼图)
与 `trafficStatisticsDetail`(折线图)。
- 分类:customer | 类型:读
- 数据来源:m0 `monthly_statistics` 日汇总表,固定 `soft_delete=0`、指定站点和日期范围,DB 完成聚合;不查 ES。
- 入参:`districtId`、`startDate`、`endDate`、`view`(gender=人流量占比、daily_gender=按日趋势),以及可选
`deviceType`(非负整数透传,不限枚举)、`buildingIds`/`floors`/`toiletIds`(列表内 IN,维度间 AND)。
- `view=gender` 返回 `totalCount` 与 `rows[]`(genderCode/gender/useCount/percent,percent 两位小数 HALF_UP,
totalCount=0 时 percent=0)。
- `view=daily_gender` 返回 `dates[]`(区间全日期)与 `series[]`(genderCode/gender/useCount/values),
values 与 dates 等长零填充——修正了老系统首次分组插入错位的 bug。
- gender 文案:0男卫、1女卫、2母婴室、3无障碍、4第三卫、5其他;null 为「未配置」,其他整数 `未知(code)`。
- 行级权限:`applyDistrictRowFilter` 校验站点可见性,不可见返回「站点不存在或无权访问」;
tenant_service 按总部视角查询。
- 日期不得晚于今天;查询区间硬限 31 天(一个月,2026-09-24 拍板),超出报错提示修改日期,
不依赖 Redis StatisticsDays。
- 无数据时 `totalCount=0`、rows/series 为空,并在 notice 说明。
详细设计见 [traffic-statistics-mcp-tool-design](traffic-statistics-mcp-tool-design.md)。
---
## 39. district.paper.trace.list.query
客户换纸记录分页查询(ES paperuselog 热索引 / paperuselog_all 冷索引),复刻老
`/MonthlyStatistics/getRfidTraceList` 的数据查询语义但**出分页明细**(不复刻轨迹展开)。
- 分类:district | 类型:读
- 入参:`districtId`、`startDate`、`endDate` 必填(yyyy-MM-dd 闭区间,endDate 不得晚于今天,区间硬限 31 天);
可选 `snCode`/`rfid`(**.keyword 精确匹配**)、`state`(2=上纸/4=撤纸,不传=查所有)、`deviceType`(0/1)、
`page`/`rows`(默认 1/20,上限 100 截断 + notice)。
- 返回 `districtId/districtName/index/total/page/rows/records`,record 含 createTime、state+stateText、rfid、
snCode、**lengthCm**(原始长度,单位厘米)、paperlengthCm、positionsId(0=厕所级)、toiletId、gender+genderText、
deviceType、isAssignment(m0 rfid_info assignment_flag≥1);按 createTime ASC 排序。
- 行级权限:`applyDistrictRowFilter` 校验站点可见性,不可见返回「站点不存在或无权访问」(老系统此接口无鉴权,属历史遗漏,MCP 收紧)。
- LocalEsDate 集群分界与冷热索引路由复用 district.paper.statistics.query 同款;跨界拒绝、字典缺失 fail fast。
- v1 不返回满卷标记(newPaperFlag),由 agent 按原始 lengthCm 自行判断。
详细设计见 [paper-trace-list-mcp-design](paper-trace-list-mcp-design.md)。
---
## 40. toilet.airquality.list.query
空感(空气质量检测器)设备列表分页查询,复刻老 `/toiletAirQuality/listByPage`。
- 分类:toilet | 类型:读
- 数据来源:m0 `toilet_airquality` 主档,固定 `soft_delete=0`;airmac 模糊匹配,其余过滤精确匹配;
固定 id DESC 稳定排序(老接口无 order by);selectCount+LIMIT 物理分页,rows 上限 100 截断 + notice。
- 入参:`airmac`(模糊)、`agentId`/`propertyId`/`districtId`/`buildingId`、`floor`、`state`(0=异常 1=正常)、
`runState`(0=离线 1=在线)、`onlineState`(0=初始化 1=上线 2=下线)、`hardware`/`firmware`/`protocol`、
`imsi`、`muteFlag`/`shockTimeFlag`/`pilotLightFlag`(0=关闭 1=开启)、`page`/`rows`。
- 返回主档固定字段集(不搬老系统 webId 字段级裁剪),枚举字段配 `*Text` 文案;
pm10 即老页面「烟感」、tvoc 即「异味」(不另起 smoke/odour 别名)。
- 富化批量修 N+1:agentName/propertyName/districtName 走 Redis multiGet;
物联网卡按页 IN 查 networkcard 并覆盖主档 factoryType(对齐老 :153 语义)。
- 行级权限:`PermissionService.applyToiletAirqualityRowFilter`(镜像 hardware 同款过滤组合);
tenant_service 按总部视角全量。
## 41. toilet.airquality.detail.query
按 `id` 或 `airmac`(二选一必填,airmac 精确)查询空感设备详情。
- 分类:toilet | 类型:读
- record 同 list 单条,内嵌 `site` 站点块(district 表 districtName/省市区县/address/contact/phone
+ buildingId/floor)。
- 不存在或无权访问统一返回 `record=null` + notice(不区分,防探测)。
## 42. toilet.airquality.metrics.query
单台空感设备指标上报明细(ES **云集群** `toilet_airquality_detail_*` 通配索引),
对应老 `/toiletAirQualityLog/listByPage`。
- 分类:toilet | 类型:读
- 入参:`airmac` 必填(**match_phrase** 精确短语,修老系统 matchQuery 分词误配);
`startDate`/`endDate` **非必填** yyyy-MM-dd 闭区间,**缺省=当天**(与老系统一致,王恒 2026-10-12 拍板;
只填一个时另一个取同一天),endDate 不得晚于今天,区间硬限 31 天;`page`/`rows` 默认 1/20 上限 100。
- updateTime range 发 epoch 毫秒闭区间;按 updateTime DESC;from+size + track_total_hits,
超 max_result_window(10000) 截断 + notice。
- record:updateTime(epoch/ISO 双形态归一为 Asia/Shanghai `yyyy-MM-dd HH:mm:ss`)+ 十项指标原值
(temp/hum/pm2d5/pm10/pm1d0/co2/tvoc/ammonia/hydrogensulfide/formaldehyde,数值/文本双形态归一)。
- 权限:先经主档行过滤校验 airmac 可见性(补老接口无鉴权漏洞),不可见抛 `NOT_FOUND_OR_FORBIDDEN`。
- ES 低阶 `esCloudRestClient` 实现(生产 ES 7.7.1 与高阶客户端不兼容);ES 异常 fail fast 抛 IllegalStateException。
## 43. toilet.airquality.statelog.list.query
单台空感设备状态变更日志(m0 `toilet_airquality_log`),对应老 `/toiletAirQualityLog/logListByPage`。
- 分类:toilet | 类型:读
- 入参:`airmac` 必填(精确);`startUpdateTime`/`endUpdateTime` 可选 yyyy-MM-dd 闭区间(缺省不过滤,
与老系统一致);`state` 可选(0=异常 1=正常 2=离线 3=在线);`page`/`rows` 默认 1/20 上限 100。
- 返回 updateTime+state+stateText,按 updateTime DESC、id DESC。
- 权限同 metrics:先经主档行过滤校验 airmac,不可见抛 `NOT_FOUND_OR_FORBIDDEN`。
> 故障日志 `toilet.airquality.fault.list.query` 本期不做(王恒 2026-10-12 拍板),契约见设计文档 §5.5。
详细设计见 [toilet-airquality-mcp-tool-design](toilet-airquality-mcp-tool-design.md)。
---
## 44. agent.user.list.query
员工查询(代理人员查询):代理角色用户分页查询(m0 `user`,强制 `enterprise_type=1` + `soft_delete=0`),对应老 `/user/listByPage` 的代理用户子集。
- 分类:agent | 类型:读
- 入参(全部可选):`agentId`(精确,必须在当前身份可见范围内)、`name`/`mobile`/`no`(模糊)、
`authorityId`(精确)、`deptId`(精确,查 `dept_user` 关联)、`workStatus`(精确);`page`/`rows` 默认 1/20 上限 100。
- 返回 `total/page/rows/[notice]/records`,固定 `id DESC`。record 含 id/name/no/loginAccount/mobile(**原样不脱敏**)/
agentId+agentName/authorityId+authorityName/enterpriseType/approle/roleType/workStatus 及各自 `*Text`/
deptId+deptName+postName(主岗)/`deptList`(dept_user 全量岗位明细:deptId/deptName/postId/postName/isManager/isKey)/
districtId+districtName/lastLoginTime/createTime。
- 枚举文案:enterpriseType 1=代理 2=物业;approle 1=运维 2=保洁 3=富士康 4=老化 5=物业 6=代理;
roleType 0=管理 1=普通;workStatus 0=上班 1=下班 2=请假 3=休息。
- 行级权限:总部(isagent=0)看全部;代理商(isagent=1)`agent_id IN visibleAgentIds`(自己+直属下级+代管),
传范围外 agentId 抛 `IllegalStateException("无权访问该代理商")`;服务主体不放行(仅绑 prod scope 1)。
- 白名单不含 idcard/password/weixin_openid/weixin_name/ding_user_id,不返回位置字段(省市区/地址)。
详细设计见 [agent-user-list-mcp-tool-design](agent-user-list-mcp-tool-design.md)。
---
---
## 附录:在线查看工具清单
```bash
AK=xqak_xxx; H=http://localhost:8088/mcp
# 1) initialize,从响应头取 mcp-session-id
# 2) 带 mcp-session-id 发 notifications/initialized
# 3) 带 mcp-session-id 调用 tools/list
curl -X POST $H -H "Authorization: Bearer $AK" -H "mcp-session-id: $SID" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
```
也可在管理后台「工具管理」页查看全量注册工具(不受作用域过滤)。