Artifact 08d5912512f5d271242b6e2e9b3fdedca9629fdd0327d8ef915f3b63784b0dde:
- File
docs/api/ql361_reverse_spec.md
— part of check-in
[5bb18ed4d4]
at
2026-10-23 03:34:10
on branch trunk
— 把来肯对接和本地库落到可切换的后端。
账号从环境读取,商品由人工增量同步,订单按五分钟轮询并缓存登录会话。开发期用 SQLite,同一套结构可切到 PostgreSQL。 (user: pluto size: 7794)
# QL361 (来肯企汇) 逆向接口协议分析与对接技术规范 | 文档版本 | 编写日期 | 目标站点 | 分析工具 / 方式 | | :--- | :--- | :--- | :--- | | **v1.0.0** | 2026-10-23 | `https://22beta.ql361.com/` | Web 静态分析、JS 运行时代码逆向 | --- ## 1. 核心网络与网关拓扑 ### 1.1 站点入口 - **业务工作台页面**:`https://22beta.ql361.com/desktop.html?v=196647593#/` - **静态资源基础路径**:`https://22beta.ql361.com/v_22beta/` - **统一 API 网关地址**:`https://22beta.ql361.com/v_22beta/api.cc`(或分布式直连网关 `https://20-apiserver-prod.ql361.com/api.cc`) - **统一认证登录页面**:`https://www.ql361.com/account/logon` - **认证接口**:`POST https://www.ql361.com/account/Auth` ### 1.2 安全与防御层 (WAF) - **CDN 提供商**:腾讯云 EdgeOne (`Server: TencentEdgeOne`) - **防御机制**:EdgeOne 具备 Bot 防护与防抓取策略。所有 HTTP 请求必须附带合法的现代浏览器 `User-Agent` 与合规的 `Accept`、`Referer` 头,否则将返回 `HTTP 567 Unknown Status` 阻断。 - **登录验证码**:集成了网易易盾(NetEase Dun)滑动验证码,同时系统也支持手机 App 扫码登录(`GET /account/QRCode` 轮询)。 --- ## 2. API 通信协议规范 (api.cc) QL361 采用基于 RPC 风格的统一网关协议,所有业务调用均路由至 `api.cc`。 ### 2.1 请求格式 - **HTTP Method**:`POST` - **URL 查询参数**: $$\text{URL} = \text{base\_url} + \text{?___method=} + \text{MethodName} + \text{&pagetype=erp&from=allinoneclient&platform=pc} + \text{TokenQuery}$$ | 参数名 | 必填 | 说明 | 示例 | | :--- | :--- | :--- | :--- | | `___method` | 是 | 后端业务逻辑层 (BLL) 方法名,大小写敏感 | `CC.ERP.BLL.API.MessageServer.GetLoginStatusForDesktop` | | `pagetype` | 是 | 页面类型 | 默认 `erp`,报表或消息传 `crmoa` | | `from` | 是 | 客户端来源代号 | 固定 `allinoneclient` | | `platform` | 是 | 客户端平台 | 固定 `pc` | | `s` | 是 | 会话标识 (Session Id) | 从登录态 Cookie 中获取的 `s` 值 | | `tok` | 是 | 鉴权令牌 (Token) | 从登录态 Cookie 中获取的 `tok` 值 | - **HTTP 请求头 (Headers)**: ```http Host: 22beta.ql361.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Content-Type: application/json Referer: https://22beta.ql361.com/desktop.html?v=196647593 Cookie: s=<SESSION_ID>; tok=<TOKEN>; acw_tc=<WAF_COOKIE> ``` - **请求体 (Body)**: 标准的 JSON 字符串。 ```json { "billid": 100234, "begindate": "2026-10-01", "enddate": "2026-10-23" } ``` --- ## 3. 响应结构与异常状态码 QL361 网关返回的 JSON 响应具有统一的包裹结构: ### 3.1 正常响应 (Normal) ```json { "type": "normal", "data": { ... }, "msg": "", "productVersion": "v_22beta" } ``` ### 3.2 会话过期 / 未登录 (Logoff) ```json { "type": "logoff", "msg": "当前账号已在其他地方登录或已退出登录", "data": null } ``` > **处理策略**:SwiftFlow 逆向引擎捕获到 `type == "logoff"` 或 `msg` 包含“已退出登录”时,立即触发告警或重新发起认证流程。 ### 3.3 业务异常 (Error) ```json { "type": "error", "msg": "未找到指定单据信息", "data": null } ``` --- ## 4. 核心 BLL 业务方法清单 (只读抽取) | 业务领域 | 核心方法名 (`___method`) | 业务用途 | 实测状态 | | :--- | :--- | :--- | :--- | | **会话状态** | `CC.ERP.BLL.API.MessageServer.GetLoginStatusForDesktop` | 检查当前 Session 是否有效 | 已验证通过 | | **基础仓库** | `cc.erp.bll.bas.baseinfomanager.getforautocomplete` | 获取全部仓库列表 (`bastype: "store"`) | 已验证通过 | | **销售订单列表** | `cc.erp.bll.bill.billbllmanager.getdatalist` | 分页拉取销售订单 (`billtype: 604`) | 已验证通过 | | **订单商品行明细** | `cc.erp.bll.bill.billbllmanager.getfulldata` | 查询指定订单全量明细与商品条码/规格/库位 | 已验证通过 | | **待拣货销售清单** | `cc.erp.bll.report.reportmanager.getdatalist` | 待拣货专用报表视图 (`billtype: 2352`) | 已验证通过 | | **待发货出库清单** | `CC.ERP.BLL.Bill.Core.Delivery.DeliveryHandOverService.QueryCanHandOverBillList` | 交接发货出库单据 | 已验证通过 | --- ## 5. 销售订单与商品明细数据协议 (实测样本) ### 5.1 订单列表拉取 (`cc.erp.bll.bill.billbllmanager.getdatalist`) - **请求负载**: ```json { "reportparam": { "orders": "", "parid": 0, "pagetype": "erp", "displaytype": "tree", "pagerow": 100, "pagenumber": 0 }, "conditionparam": { "begindate": "2024-01-01", "enddate": "2026-12-31", "billtype": 604, "status": 20 } } ``` - **核心字段映射**: - `billid`: 订单数字 ID (如 `1898938789`) - `billcode`: 业务单据号 (如 `XSDD-20260922-001`) - `dfullname`: 客户名称 (如 `山西省忻州市巷往烘焙`) - `contactor`: 联系人 - `phone`: 电话 - `soutfullname`: 发货仓库名称 (如 `门店`) - `soutid`: 仓库 ID (如 `793208`) - `qty`: 订单总件数 (如 `22.0`) - `totalmoney`: 订单总金额 - `status`: 订单状态 (`20` = 待发货/待拣货, `22` = 发货完成, `30` = 交易完成, `19` = 已取消) ### 5.2 订单全量商品明细 (`cc.erp.bll.bill.billbllmanager.getfulldata`) - **请求负载**: ```json { "billid": 1898938789, "billtype": 604 } ``` - **响应结构**:`data.header` (单头汇总) + `data.body0.datasource` (商品行明细列表) - **商品行字段提取**: - `gcode`: 商品编码/SKU (如 `c001057`) - `gfullname`: 商品名称 (如 `乙夫-蔬香紫薯味吐司400g`) - `barcode`: 69码/EAN13商品条码 (如 `6971979070155`) - `spec`: 规格 (如 `400g*20袋`) - `mqty` / `mufullname`: 大单位件数 (如 `2.0 件`) - `qty` / `bufullname`: 基本单位散件数 (如 `40.0 盒`) - `gpcode`: 原系统货位 (如 `默认货位(门店)`) - `sacode`: 原系统库区 (如 `默认库区`) --- ## 6. 拣货单生成转换逻辑 (SwiftFlow WMS 核心) 从 QL361 销售订单向 SwiftFlow 拣货任务的转换流程如下: ``` [QL361 销售订单: billtype 604] │ ▼ (只读抽取: getfulldata) { 订单商品行明细: SKU, 条码, 件数, 散数 } │ ├───────────────────────────────────────────┐ ▼ ▼ 【按单拣货 Single Order】 【波次汇总拣货 Wave Picking】 - 单任务 1:1 对应源单 - 多单合并、相同 SKU 累加数量 - SwiftFlow 推荐最优 S 型库位动线 - 匹配四级库位 (区-排-架-位) 推荐最短动线 - PDA 扫条码逐行核销 - 拣货完成后推入播种墙 (Seed Wall) 进行二次分播 │ │ ▼ ▼ 【拣货完成待复核】 【播种复核分箱完成】 ``` --- ## 5. SwiftFlow 在 Gleam 中的实现架构 - **模块位置**:[`shared/ql361api`](file:///Users/pluto/MyProject/NMProject/fosi.dev/SwiftFlow/shared/ql361api) - **底层驱动**:`gleam_httpc` + `gleam_json` - **设计模式**:只读数据适配器模式(Read-Only Adapter Pattern)。所有抓取到的数据经过清洗与类型转换后,投递给 `backend/` 业务核心。