进港查询(10,06出港查询的进港镜像): - IntImpQueryActivity 手机布局:搜索行 + 三格统计(总票数/已入库/已出库客户端计数) + 状态标签卡片(dlvTime/inDate 前端推断)+ 筛选弹层9项(入参全部经api-doc核对存在) - 详情页与运单追踪复用08/06已适配页零新增;平板侧滑筛选面板判空守卫,Pad端零变化 - 修复代理人空值兜底:转换器将JSON null归一为空串致DataBinding `??` 失效, 改用Bean计算属性 agentDisplay(06出港查询同步修正) 进港移库(11,首个后端整模块缺失的设计稿页): - 全新手机专属3页:列表(双Tab+角标/筛选4项/多选批量移库/卡片长按拍照浮层) + 运单详情(三卡片,Intent传bean零请求)+ 拍照上传(复用ImageSelectNewViewHolder, pic/originalPic/picNumber三字段规范) - 后端无IntImpMove模块(api-doc核对),按IntExpMove同名约定预接四接口 + 预定义权限串AppIntImpMove,后端交付后前端零改动生效(缺口#31) 其他:手机首页国际Tab新增进港查询/进港移库入口;新增资源bg_phone_btn_green/ bg_phone_card_header_orange/bg_phone_photo_add/phone_amber;问题清单登记#30~#31; 双端模拟器验证零崩溃(手机Map Local mock全交互链路,平板回归零变化) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
12 KiB
api-doc MCP:接口定义核对流程(阶段 2 必读)
适配页面前,所有用到的接口都要在 api-doc(AgentFox)里核对一遍入参/出参,
不要靠平板代码或猜测推断参数名——出库交接就因为猜了 hoState(实际是 handoverState)
白白折腾了一轮实机排查。
连接方式
项目 .mcp.json 已配置 api-doc 远程 HTTP MCP。若会话内 MCP 工具可直接调用
(mcp__api-doc__*),直接用;若工具没有注入会话(ToolSearch 搜不到),用 curl 走
MCP 协议手工调用,三步:
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 调用:
- 从
module_base/.../http/net/Api.kt拿到@POST路径 →search_endpoints找到接口 →get_endpoint_detail - 入参:前端传的每个 key 必须能在文档字段表里找到同名字段;找不到 = 传了也白传(后端静默忽略,不报错)
- 类型:状态类字段(
moveState/handoverState/outState)文档均为 integer,前端传"0"字符串靠 Jackson 隐式转换(实测可用);新代码尽量传数字 - 出参:设计稿卡片/详情页的每个展示字段,都要能对上出参字段;对不上就是后端缺口
- 结论落两处:
- 后端缺口(缺入参/缺出参/数据不回填)→ 记入
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 有定义但被忽略的先例);出参clearNormal0=未清仓/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 可为 nullIntImpMove(进港移库,2026-07-31 文档核对):后端整个模块不存在(api-doc 仅有国内进港DomImpMove与国际出港IntExpMove),权限串也没有对应 key(缺口 #31)。前端按 IntExpMove 同名约定预接pageQuery/pageQueryTotal/move/uploadPic四接口 + 预定义权限串AppIntImpMove,后端交付后前端零改动生效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 打印到输出里,落临时文件引用):
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)驱动,滑完截图校准。