设计稿11「进港移库」经确认归属国内进港移库(DomImpMove后端8接口齐全), 此前误判为国际进港并预接了不存在的IntImpMove模块(问题清单#31作废)。 - 删除module_gjj的IntImpMove全套实现(3页面+Bean+Api+菜单+路由+权限串) - GnjMoveStashListActivity双形态适配:待移库/已移库Tab走search/searchMoved 双端点,Tab角标、筛选弹层4项、全选/批量移库复用平板既有链路 - 新增手机专属页:运单详情(Intent传bean零请求)+拍照上传(detail回填 已有照片,modify最小体三字段提交,与平板移交编辑页同链路) - 手机首页入口改挂国内Tab(GnViewModel补通用route跳转,权限串AppDomImpMove) - 内网真实数据双端验证通过;Proxyman A/B:wbNo/awbType生效, fdate/fno/spCode被后端静默忽略(#32)、opId/opDate恒null(#33) - 问题清单更正#31+新增#32~#34,CLAUDE.md/CHANGELOG/api-doc.md同步 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
161 lines
13 KiB
Markdown
161 lines
13 KiB
Markdown
# api-doc MCP:接口定义核对流程(阶段 2 必读)
|
||
|
||
适配页面前,**所有用到的接口都要在 api-doc(AgentFox)里核对一遍入参/出参**,
|
||
不要靠平板代码或猜测推断参数名——出库交接就因为猜了 `hoState`(实际是 `handoverState`)
|
||
白白折腾了一轮实机排查。
|
||
|
||
## 连接方式
|
||
|
||
项目 `.mcp.json` 已配置 `api-doc` 远程 HTTP MCP。若会话内 MCP 工具可直接调用
|
||
(`mcp__api-doc__*`),直接用;**若工具没有注入会话**(ToolSearch 搜不到),用 curl 走
|
||
MCP 协议手工调用,三步:
|
||
|
||
```bash
|
||
URL=$(python3 -c "import json;print(json.load(open('.mcp.json'))['mcpServers']['api-doc']['url'])")
|
||
AUTH="Authorization: $(python3 -c "import json;print(json.load(open('.mcp.json'))['mcpServers']['api-doc']['headers']['Authorization'])")"
|
||
|
||
# 1) initialize:从响应头 mcp-session-id 取会话 id
|
||
curl -s -D /tmp/mcp_headers.txt -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||
-H "Accept: application/json, text/event-stream" \
|
||
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-code","version":"1.0"}}}' > /dev/null
|
||
SID=$(grep -i 'mcp-session-id' /tmp/mcp_headers.txt | tr -d '\r' | awk '{print $2}')
|
||
|
||
# 2) initialized 通知(必须发,否则 tools/* 报 session 未初始化)
|
||
curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||
-H "Accept: application/json, text/event-stream" -H "mcp-session-id: $SID" \
|
||
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}' > /dev/null
|
||
|
||
# 3) tools/call(响应是 SSE,`sed 's/^data: //'` 后按 JSON 解析)
|
||
curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||
-H "Accept: application/json, text/event-stream" -H "mcp-session-id: $SID" \
|
||
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_endpoints","arguments":{"keyword":"IntExpMove"}}}'
|
||
```
|
||
|
||
## 可用工具(5 个)
|
||
|
||
| 工具 | 用途 |
|
||
|------|------|
|
||
| `get_project_overview` | 项目概览 + 模块清单(首次调用) |
|
||
| `list_modules` | 全部模块含描述 |
|
||
| `list_endpoints` | 单模块内接口列表(分页,`moduleId` + `cursor`) |
|
||
| `search_endpoints` | 按关键字搜接口(`keyword` 用控制器前缀最准,如 `IntExpOutHandover`) |
|
||
| `get_endpoint_detail` | **核心**:单接口完整契约,schema 已拍平成字段表(`endpointId`) |
|
||
|
||
## 核对清单(每个页面做一遍)
|
||
|
||
对 ViewModel 里每个 `NetApply.api.xxx` 调用:
|
||
|
||
1. 从 `module_base/.../http/net/Api.kt` 拿到 `@POST` 路径 → `search_endpoints` 找到接口 → `get_endpoint_detail`
|
||
2. **入参**:前端传的每个 key 必须能在文档字段表里找到**同名字段**;找不到 = 传了也白传(后端静默忽略,不报错)
|
||
3. **类型**:状态类字段(`moveState`/`handoverState`/`outState`)文档均为 integer,前端传 `"0"` 字符串靠
|
||
Jackson 隐式转换(实测可用);新代码尽量传数字
|
||
4. **出参**:设计稿卡片/详情页的每个展示字段,都要能对上出参字段;对不上就是后端缺口
|
||
5. **结论落两处**:
|
||
- 后端缺口(缺入参/缺出参/数据不回填)→ 记入 `documents/后端接口对接问题清单.xlsx`
|
||
(按「模块 → 页面 → 接口定义 → 接口地址」格式,含前端现状与期望,状态标 ⏳/🔍)
|
||
- 前端可自行解决的(参数名写错、字段绑错)→ 直接改代码,别记成后端问题
|
||
|
||
## 已核对过的语义(别再猜)
|
||
|
||
- `IntExpOutHandover`:交接状态入参 = `handoverState`(不是 `hoState`),**2026-07-29 内网实测过滤生效**
|
||
(传 0/1 分别返回纯净的待/已交接集合,字符串 `"0"`/`"1"` 同样被接受);`ldId` 是**组装人**不是交接人;
|
||
交接人姓名出参 `hoUserName`,编码 `hoId`;查询入参**没有**交接人筛选(后端缺口 #4)
|
||
- `IntExpOutHandover/pageQueryTotal`:**出参恒 0**(任何条件下 totalPc/totalWeight/wbNumber 均为 0,
|
||
与同条件 pageQuery 不一致,后端缺口 #6)——依赖它的 Tab 角标/底部统计显示 0 不是前端问题
|
||
- `IntExpMove`:出参无航班字段(缺口 #1)、无操作人姓名(缺口 #2);`opId`/`opdate`/`businessType`
|
||
文档有定义但实测恒 null(缺口 #3);`pageQueryTotal` 正常(与 pageQuery 口径一致,`moveState` 过滤生效)
|
||
- `typeCode/getSpecialCodeByParam`:`flag=1` 国际,`ieFlag` 必须传空串(传 `"E"` 返回空数组)
|
||
- `eqm/uld`(ULD信息,6 个端点):查询入参仅 `uld`/`status`/`uldSuffix`(uldSuffix 名义是
|
||
"uld后两位",实际就是**按所属航司筛选**,配 `DictUtils.getIntCarrierList`);`status` 仅两态
|
||
0 正常/1 故障(三态需求=缺口 #7);无「来源」字段(#8)、无 `checkInDate`/`ifNo` 筛选入参(#9)、
|
||
无批量删除(#10,`deleteUld` 是 `@Query` 单条);出参 `ifNo`/`efNo`/`checkInDate`/`checkOutDate`
|
||
文档无描述,按命名推断进/出港航班号与出入库时间(#11 待确认)
|
||
- `IntExpArrive`(出港运抵,2026-07-30 内网实测):`declareStatus`(申报状态)只在出参,
|
||
`pageQuery`/`pageQueryTotal` 入参均无此字段(缺口 #20,待运抵/已运抵 Tab 靠客户端拆分);
|
||
`declareStatus`/`GjcHaWb.arrivalStatus` 三色编码:`01`=正常(绿)、`W`=靛蓝态、其余非空=琥珀异常态;
|
||
`haWbList[].response`/`arrivalOpDate` 是回执文本与对应时间,实测确认真实存在非空数据;
|
||
分单补充收发货人信息**后端完全没有对应接口**(`search_endpoints` 对 `IntExpArrive`/`HaWb`
|
||
关键字均搜不到分单更新接口,只有国际进港的 `IntImpAirManifest/complete`),
|
||
实机调用拟定路径 `IntExpArrive/haWb/supplement` 确认 404(缺口 #19)
|
||
- `IntExpSearch`(出港查询,2026-07-31 内网实测):查询入参 `outState`/`fno`/`dest`/`spCode`/`awbType`/
|
||
`businessType`/`goodsCn`/`beginDate`/`endDate`/`agentCode`/`wbNo` 全部真实生效(平板既有口径);
|
||
文档定义的 `isFclose`(筛选航班是否关闭)**pageQuery 与 pageQueryTotal 均静默忽略**(A/B 对照
|
||
全量 72 票与单日两组,加参前后结果完全一致,缺口 #21);`pageQueryTotal` 出参无已入库/已离港
|
||
分项计数(#21);`detail` 出参 Map 无 `lockStatus`(锁定状态)与计费重量字段(#22);
|
||
详情「入库件数/入库重量」= `warehouseList` 各批次 pc/weight 求和(mock 与实测均吻合);
|
||
卡片状态推断口径:`fclose` 非空=已离港、否则 `opDate` 非空=已入库;
|
||
特码字典沿用 `DictUtils.getSpecialCodeList(flag=1, ieFlag="")`(ieFlag 必须空串)
|
||
- `IntExpStorageUse`(出港仓库,2026-07-31 文档核对,内网 A/B 未测——当日开发机不在内网):
|
||
`pageQuery`/`pageQueryTotal` 同一套入参 schema:`fdate`/`fno`/`wbNo`/`likeNo`/`spCode`/`dest`/
|
||
`agentCode`/`location`/`clearNormal`/`checkIn`/`reviewStatus`/`dep`/`hno`/`prefix`;设计稿筛选的
|
||
**运单类型/业务类型/品名(中) 均无入参**(`awbType`/`businessType`/`goodsCn`,缺口 #23);
|
||
手机三格统计按 `clearNormal=0/1` 各调一次 `pageQueryTotal` 取 `wbNumber`,该过滤**是否真实生效
|
||
待内网 A/B**(缺口 #24,参照 isFclose 有定义但被忽略的先例);出参 `clearNormal` 0=未清仓/1=已清仓
|
||
(卡片标签与「清仓」按钮置灰按 ==\"1\" 判定);详情复用 `IntExpSearch/detail`(maWbId Query 参数)
|
||
- `IntImpStorage`(进港仓库,2026-07-31 文档核对;**注意前端 Api.kt 路径前缀是 `IntImpStorage`
|
||
不是 `IntImpStorageUse`**,api-doc 按后者搜不到):`pageQuery`/`pageQueryTotal` 同一套入参:
|
||
`fdate`/`fno`/`wbNo`/`hno`/`fdep`(始发站)/`location`/`clearNormal`/`fid`;比出港仓库还少
|
||
`agentCode`/`spCode`/`dest`——设计稿筛选的代理/特码/运单类型/业务类型/品名(中) **5 项均无入参**
|
||
(缺口 #25);分项统计与 clearNormal 过滤待实测同 #24(记为 #26);详情复用
|
||
`IntImpSearch/detail`(Body 传 `prefix`+`no`,出参 `data.maWb` 无 schema,字段 key 以平板绑定为准:
|
||
`awbPc`/`awbWeight`/`cashWeight`/`inPc`/`inWeight`/`lockState`(0/1 需转中文)/`mftStatus` 原始舱单/
|
||
`tallyStatus` 理货报告/`command` 海关放行/`by1` 承运人/`dlvTime` 出库时间;`dep`/`dest1`/`dest`/
|
||
`unNumber`/`opDate` 为按出港推断,待实测 #27)
|
||
- `IntImpPickUpDlv`(提取出库,2026-07-31 文档核对):`pageQuery` 入参含 **`outState`
|
||
(0 未出库/1 已出库)**——待出库/已出库 Tab 直接真实过滤(抓包证实携带,效果待内网 A/B #29);
|
||
另有 `fno`/`dest`/`origin`/`carrier`/`awbType`/`businessType`/`goods`/`likeNo`/`pkId`/
|
||
`beginDate`/`endDate`+`dateType`(0 入库/1 离港)/`chargeFlag`(0 未提取/1 已提取);
|
||
**无 `fdate`(航班日期)入参**(缺口 #28);出参无车牌/目的港/业务类型名称(#28);
|
||
运单号筛选参数名是 **`no`**(平板既有口径);`chargeName`/`chargeTime` = 柜台办理人/缴费(提取)时间,
|
||
`dlvTime` = 出库时间,`dlvName` 按提货人语义推断作「出库人」展示(待确认 #29);
|
||
确认出库接口 `pickUpOut` 文档注明**需将全字段传至后端**(平板 `selectedItems.toRequestBody()` 已满足)
|
||
- `IntImpSearch`(进港查询,2026-07-31 文档核对):`pageQuery`/`pageQueryTotal` 分页参数名是
|
||
**`page`/`limit`**(不是 pageNum/pageSize);手机筛选 9 项入参全部真实存在:`beginDate`/`endDate`/
|
||
`fno`/`agentCode`/`spCode`/`origin`(始发站)/`awbType`/`businessType`/`goods`(品名,**不是 goodsCn**);
|
||
`outState`(0 未出库/1 已出库)文档为 integer,前端传字符串靠 Jackson 隐式转换;`pageQueryTotal`
|
||
出参无已入库/已出库分项计数(缺口 #30,手机三格统计为客户端计数口径:dlvTime 非空=已出库、
|
||
inDate 非空=已入库);出参含 `agentName`(中文名)+ `agentCode`,agentName 可为 null
|
||
- 「进港移库」(设计稿 11)**归属更正(2026-08-03 用户确认):属国内进港移库 `DomImpMove`**,
|
||
不是国际进港移库——「后端模块缺失」的旧结论(原缺口 #31)作废,原按 IntExpMove 约定预接的
|
||
IntImpMove 前端实现已整体删除。溯源:api-doc 53 模块中移库仅 `DomImpMove`(国内进港,8 接口)与
|
||
`IntExpMove`(国际出港,4 接口),移库按转关业务前缀划分(DomImpMove 处理 `CI**`、IntExpMove
|
||
处理 `IO**`),设计稿详情卡的「国际进港」红标签实为 `awbType` 值(如「国际进港(国内中转)」),
|
||
不代表页面归属
|
||
- `DomImpMove`(国内进港移库,2026-08-03 内网实测):手机 Tab 双端点——待移库 `search` /
|
||
已移库 `searchMoved`(同一套入参 schema,分页参数名 **`page`/`limit`**);入参 A/B 实测:
|
||
**`wbNo`、`awbType`(值为 CICO/CIII/CIIO 转关码)过滤生效;`fdate`/`fno`/`spCode` 文档有定义但
|
||
被静默忽略**(缺口 #32);`searchMoved`/`detail` 出参 **`opId`/`opDate` 恒 null** 且无操作人姓名
|
||
字段(缺口 #33,移库人/移库时间显示"-"),`moveId` 形如 `CICO240617155016` 疑似内嵌移库时间戳但无
|
||
文档语义;`searchMoved` 出参**无 pic 三字段**(search 有)——拍照上传页统一先调 `GET detail`
|
||
回填已有照片(#34 前端已规避);照片提交走 `modify` 最小体
|
||
`{mawbId, remark, picNumber, pic, originalPic}`(平板移交编辑页同链路,removeEmptyOrNull);
|
||
批量移库 `move` 入参 `{fid:"0", ids:[mawbId]}`;国内进港特码字典
|
||
`DictUtils.getSpecialCodeList(flag=0, ieFlag="I")` 实测取数正常
|
||
- `IntExpCheckIn`(出港计重,2026-07-29 内网实测):`likeNo` **并非任意模糊**——按 8 位 `no`
|
||
匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList`
|
||
文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖);
|
||
查询入参无通道号/承运人/运单类型(#13)、`checked/*` 无收运时间范围(#14);`checked/pageQuery`
|
||
出参无通道号(#15)但有 `userName`(计重人中文名);`listRecordByWh` 出参计重人仅 `opId`(#16);
|
||
`preArrive` 成功后 `pageQuery` 的 `arriveFlag` 不回填(#17);`splitCheckIn`/`completeCheckIn`
|
||
无 `palletNumber`/`carWeight` 入参(#18,手机版托盘数量/自重按设计稿传预留参数);
|
||
运单类型字典:`DictUtils.getWaybillTypeList2(ieFlag="E", type="IO")`
|
||
|
||
## 内网直调验证技巧(比驱动 UI 快得多)
|
||
|
||
登录态 token 存在 SharedPreference 里,取出后可以直接 curl 后端做行为验证
|
||
(**注意:不要把 token 打印到输出里**,落临时文件引用):
|
||
|
||
```bash
|
||
adb -s <serial> shell "run-as com.lukouguoji.aerologic cat \
|
||
/data/data/com.lukouguoji.aerologic/shared_prefs/data.xml" \
|
||
| grep -o '<string name="token">[^<]*' | sed 's/.*>//' > /tmp/air_token
|
||
|
||
curl -s -X POST "http://192.168.1.250:8093/IntExpOutHandover/pageQuery" \
|
||
-H "Authorization: $(cat /tmp/air_token)" -H "Content-Type: application/json" \
|
||
-d '{"pageNum":1,"pageSize":50,"handoverState":0}'
|
||
```
|
||
|
||
用它做「参数是否生效」的 A/B 对比(同条件只改一个参数看 total 变化),
|
||
再回 App UI 走一遍确认端到端链路。UI 里的滚轮日期选择器可用
|
||
`input swipe`(慢速 500ms、每次 3 格 ≈ 396px)驱动,滑完截图校准。
|