本文解决的不是“系统里有哪些接口”,而是“一个请求究竟从哪里进入、如何获得用户和服务站上下文、怎样找到真实业务代码、成功或失败如何表达、发生异常时从哪一层开始查”。
DGJ2.0 不是统一 REST 架构。当前代码同时保留 PC 控制器、inner JSON 接口、App tradeCode 网关、直接 App/PDA Controller、老 OpenAPI、全车件代理路由、报表导出、CLI/MQ 消费者等多代入口。相同业务可能由多种入口复用同一个 Service,因此只修一个 Controller 或只回归一个页面是不够的。
1. 业务目标
- 看见 URL、
tradeCode、transCode、routing key 或 CLI 命令后,能在 3 分钟内找到入口文件。 - 明确每类入口的请求载体、登录态、服务站字段、请求 ID、响应结构和幂等边界。
- 能沿“入口 -> 参数解析 -> 鉴权 -> Service -> Model -> 表/MQ”解释完整数据流。
- 能区分前端接口、内部接口、外部回调、消息消费者和人工补偿任务,避免错误调用。
- 联调时不依赖真实密钥示例;所有签名、令牌和账号信息都由环境安全注入。
- 修复公共 Service 时,能根据入口影响矩阵决定需要回归哪些调用方。
2. 适用场景
| 场景 | 先看哪一节 | 最小定位信息 |
|---|---|---|
| PC 页面报错或返回登录失效 | PC 控制器协议 | URL、用户、sid、请求时间 |
| OPS 调用 DGJ 内网接口失败 | inner 协议 | URL、Request-Id、请求体、调用方身份头 |
| 手机 App 提示接口类不存在 | App tradeCode 网关 | tradeCode、reqCode、App 版本 |
| PDA/盘点接口失败 | 直接 App/PDA Controller | URL、reqCode、设备与登录态 |
| OMS/NC 老接口重复执行 | 老 OpenAPI | transCode、requestId、来源系统 |
| 全车件页面返回上游错误 | allcarpart 代理 | 原 URL、登录服务站、代理目标响应 |
| 导出接口浏览器拿到 JSON/HTML | 报表与文件下载 | URL、权限、响应头、文件路径 |
| 支付/OA/订单消息未生效 | MQ 消费入口 | destination、routing key、业务单号 |
| 秒杀超时订单没有释放 | CLI/定时任务 | 命令、limit、任务日志、订单状态 |
3. 八类入口总览
3.1 入口协议矩阵
| 类型 | 典型入口 | 路由键 | 请求载体 | 身份来源 | 主要响应 |
|---|---|---|---|---|---|
| PC 控制器 | /scm/invPo/getInvPoList | URL controller/method | Query、Form、部分 JSON | CI Session、菜单权限 | splash、splashJson、resp、HTML |
inner API | /inner/activity/flashActivity/listFlashActivity | URL controller/method | JSON 或 Form | 内部边界、Inspire-Api-User、部分 token | R::successJson、R::bizErrorJson |
| App 网关 | /carsteward/index、index2 | tradeCode | Form 或 JSON 外壳,data 为 JSON 字符串 | App Session、uid、设备、签名 | BaseService::splash |
| 直接 App/PDA | /app/...、/pda/... | URL controller/method | JSON 外壳,data 为对象 | App Session、签名、设备 | R::*Json |
| 老 OpenAPI | /openapi/index | transCode | Form,data 为数组 | 来源系统字段、可选 requestId | _success、_error |
| allcarpart | /allcarpart/... | 显式 routes + 通配路由 | JSON/Form/文件 | PC Session、全车件开关 | 上游 JSON 或文件流 |
| 报表/导出 | /reports/...、业务 controller export | URL method | Query/Form | PC Session、查询/导出权限 | JSON、Excel、PDF、HTML |
| MQ/CLI | tasks/*/consume、tasks/*/* | routing key 或 CLI method | MQ payload 或 CLI 参数 | MQ 配置、部署进程、主机权限 | ACK/NACK、标准输出、日志 |
3.2 请求进入系统的总图
flowchart LR
Browser["PC 浏览器"] --> PC["BaseController"]
OPS["OPS / 内部服务"] --> Inner["BaseApiController"]
Mobile["手机 App"] --> Gateway["Carsteward + OpenAppService"]
PDA["PDA / 新 App"] --> Direct["BaseAppController"]
Legacy["OMS / NC / 老商城"] --> Open["Openapi + OpenService"]
AllCar["全车件前端"] --> Proxy["allcarpart Controller"]
MQ["RabbitMQ"] --> Consumer["tasks/*Notify"]
Cron["crontab / 人工命令"] --> Task["tasks/*"]
PC --> Service["业务 Service"]
Inner --> Service
Gateway --> Service
Direct --> Service
Open --> Service
Proxy --> Remote["全车件微服务"]
Consumer --> Service
Task --> Service
Service --> Model["Model / Provider"]
Model --> DB["MySQL / Redis / MongoDB"]
Service --> OutMQ["MQ / 外部系统"]
3.3 不同入口最容易混淆的地方
| 混淆点 | 正确认知 |
|---|---|
URL 里出现 inner 就一定校验 inner_token | 不能这样推断,必须结合 BaseApiController 当前分支和网关边界核对 |
success: true 就代表业务成功 | R::bizError/bizErrorJson 也写 success: true,必须同时判断 code |
appapis.php 有映射就一定可调用 | 还要检查对应 application/service/api/...php 是否存在、类名是否可加载 |
| HTTP 200 就代表业务成功 | 多数旧接口把业务错误放在 JSON 字段中,MQ 则看 ACK/NACK |
Request-Id 自动保证所有入口幂等 | PC/inner 多用于日志串联;老 OpenAPI 才实现独立的请求锁与完成记录 |
| tasks 方法能通过浏览器调用 | 多数消费者/新任务有 is_cli() 保护,应该由 CLI 或进程管理器运行 |
| allcarpart 是 DGJ 本地完整实现 | 大部分是登录校验后转发到微服务,本地只保留导入、导出、打印等适配 |
4. 从 URL 或 code 定位代码的统一方法
4.1 URL 路由入口
CodeIgniter 常规 URL 可按以下方式初步映射:
/目录/控制器/方法
-> application/controllers/目录/控制器.php
-> class 控制器
-> public function 方法()
例如:
/scm/invPo/getInvPoList
-> application/controllers/scm/InvPo.php
-> InvPo::getInvPoList()
但以下情况不能直接套用:
application/config/routes.php做了显式改写。allcarpart使用显式路由和三段通配路由。- App 网关 URL 固定,真实目标由
tradeCode决定。 - 老 OpenAPI URL 固定,真实目标由
transCode决定。 - 版本路由 Hook 可能在控制器前改写 API 版本。
4.2 code 到文件的解析公式
App 网关:
tradeCode
-> application/config/appapis.php
-> app.purchase.purOrderList
-> application/service/api/app/purchase/purOrderList.php
-> PurOrderList::service($data)
老 OpenAPI:
transCode
-> application/config/apis.php
-> invoice.statusUpdate
-> application/service/api/invoice/statusUpdate.php
-> StatusUpdate::service($data)
4.3 定位决策图
flowchart TD
A["拿到调用信息"] --> B{"是 URL 还是业务 code?"}
B -->|URL| C{"URL 前缀"}
C -->|scm / basedata / reports| D["controllers 对应目录"]
C -->|inner| E["controllers/inner"]
C -->|app / pda| F["controllers/app 或 pda"]
C -->|allcarpart| G["先查 routes.php"]
B -->|tradeCode| H["查 appapis.php"]
B -->|transCode| I["查 apis.php"]
B -->|routing key| J["查 MqEventEnums 和 registryCallback"]
B -->|CLI method| K["controllers/tasks"]
D --> L["找 Controller 方法"]
E --> L
F --> L
G --> L
H --> M["service/api/app 文件"]
I --> N["service/api 文件"]
J --> O["消费者回调方法"]
K --> P["任务方法"]
L --> Q["继续追 Service / Model / 表"]
M --> Q
N --> Q
O --> Q
P --> Q
5. PC 控制器协议
5.1 核心入口文件
| 文件 | 职责 |
|---|---|
application/core/BaseController.php | 登录、账号、菜单、维护状态、请求数据和响应封装 |
application/controllers/scm/*.php | 采购、销售、库存、财务等 PC 业务入口 |
application/controllers/basedata/*.php | 商品、客户、供应商、仓库等基础资料入口 |
application/controllers/reports/*.php | 报表查询和导出入口 |
application/service/scm/*.php | 大量历史业务 Service |
application/Services/* | 新领域 Service |
5.2 构造阶段发生什么
普通 Controller 继承 BaseController,默认构造参数是 base。除 API 类型和特殊静默登录外,构造阶段会依次处理:
- 从 Session 读取
$this->jxcsys。 - 判断系统维护状态。
- 判断是否登录。
- 判断账号和服务站是否可用。
- 判断 PC 登录状态缓存是否允许继续访问。
- 判断用户是否能获得菜单。
- 记录用户活跃时间和
Request-Id。 - 解析 JSON 请求体到
$requestData。
sequenceDiagram
participant B as PC 浏览器
participant C as BaseController
participant S as Session/Redis
participant M as 菜单与账号
participant A as 业务 Controller
participant D as Service/DB
B->>C: GET/POST/JSON + Cookie + Request-Id
C->>S: login_session()
S-->>C: sid/uid/name/area
C->>M: 账号状态、登录状态、菜单校验
M-->>C: 可访问/重定向
C->>A: 注入 jxcsys 与 requestData
A->>D: 业务查询或写入
D-->>A: 业务结果
A-->>B: splash / resp / HTML / 文件
5.3 请求字段如何合并
getPageData() 先合并 GET 与 POST,再补分页和登录上下文:
| 字段 | 来源 | 说明 |
|---|---|---|
page | 请求 | 最小为 1 |
rows | 请求 | 未传时默认 99999,列表接口应警惕大查询 |
JXCSID | Session | 当前服务站 ID |
JXCUID | Session | 当前用户 ID |
JXCUNAME | Session | 当前用户名 |
areaCode | Session | 区域编码 |
eparchy | Session | 地市/区域上下文 |
getRequestData() 再把以上页面参数与 JSON 请求体合并。出现“请求明明传了 sid,代码却查了另一个站”的问题时,要确认最终使用的是 JXCSID、sid、storeId 还是总部账号切换后的默认门店。
5.4 PC 请求骨架
以下示例只表达协议,不包含真实 Cookie:
curl '/scm/invPo/getInvPoList?page=1&rows=20&status=3' \
-H 'Accept: application/json' \
-H 'Request-Id: <调用链请求ID>' \
-H 'Cookie: <由已登录浏览器安全提供>'
JSON 写接口常见骨架:
curl -X POST '/scm/invSa/addNew' \
-H 'Content-Type: application/json' \
-H 'Request-Id: <调用链请求ID>' \
-H 'Cookie: <由已登录浏览器安全提供>' \
--data '{"contactId":123,"goodsList":[{"skuId":456,"qty":2}]}'
不能只用裸 curl 判断接口是否可用,因为缺少 Session 时可能返回登录页、重定向或 nologin JSON。
5.5 PC 响应并不统一
| 方式 | 形状 | 客户端判断 |
|---|---|---|
splashJson('success',...) | {"success":true,"status":"success","msg":"..."} | status 和同名布尔字段 |
splashJson('error',...) | {"error":true,"status":"error","msg":"..."} | status=error |
resp() | {"code":0,"msg":"...","data":{...}} | code |
| 页面渲染 | HTML | HTTP 状态、页面内容、重定向 |
| 导出 | 文件流或临时下载地址 | Content-Type、文件名、返回体 |
5.6 PC 代表性入口字典
| 业务 | Controller 方法 | 继续追踪 |
|---|---|---|
| 采购列表 | scm/InvPo::getInvPoList | InvPoService、采购 Model、分表 |
| 采购保存 | scm/InvPo::saveInvPo | 参数校验、订单主明细写入 |
| 采购提交 | scm/InvPo::submitInvPo | 状态、OA/订单中心、支付方式 |
| 采购取消待支付 | scm/InvPo::cancelWaitPayOrder | 库存/额度释放、支付异常 |
| 采购入库 | scm/InvPo::invPoInStroage | 入库单、库存流水、实时库存 |
| 销售列表 | scm/InvSa::SalesList | 销售主表/明细 |
| 销售新增 | scm/InvSa::addNew、add | 销售 Service、价格和库存校验 |
| 销售出库 | scm/InvSa::addOutBound | 出库单、库存扣减、配送与同步 |
| 销售退货 | scm/InvSa::saveAftersale、returnupdate | 售后、退货入库、退款 |
| 付款单 | scm/Payment::addNew、updatePayment | 付款主明细、账户流水 |
| 无页面支付 | scm/Payment::noPagePay | 支付中心下单与回调 |
| 采购报表 | reports/PurchaseReport::*Report | Validate、Report Service、报表 Model |
5.7 PC 失败分支
flowchart TD
A["PC 请求失败"] --> B{"返回内容"}
B -->|登录页/302| C["Session 失效、账号不可用或菜单为空"]
B -->|status=nologin| C
B -->|status=error| D["业务校验或 Service 异常"]
B -->|HTTP 200 但页面空| E["权限、筛选条件、sid 上下文"]
B -->|导出失败| F["导出权限、时间范围、数据量、文件流"]
C --> G["核对 Cookie、sid、uid、登录状态缓存"]
D --> H["按 Request-Id 和方法名查日志"]
E --> I["核对最终 JXCSID/storeId"]
F --> J["核对查询权限与导出权限是否分离"]
6. inner 内部 API 协议
6.1 核心入口
| 目录 | 业务 |
|---|---|
controllers/inner/activity | OPS 活动、秒杀活动管理 |
controllers/inner/moveMall | E站、移动商城、机器人、秒杀购买 |
controllers/inner/channel | 大客户渠道订单和售后 |
controllers/inner/Inventory.php | 库存内部查询/同步 |
controllers/inner/Goods*.php | 商品、物料和搜索 |
controllers/inner/Payment.php | 支付和财务内部能力 |
controllers/inner/sys | OPS 白名单、系统配置 |
这些 Controller 通常继承 BaseApiController,构造时调用 parent::__construct('api'),因此不走 PC Session 登录校验。
6.2 请求解析
| 条件 | 解析方式 |
|---|---|
Content-Type 以 application/json 开头 | raw_input_stream JSON 解码 |
| 非 JSON | 合并 GET、POST 和 php://input 解析结果 |
Request-Id 请求头存在 | 保存到 $requestID,用于日志串联 |
Shipper 请求头存在 | 保存到 $scmShipper |
Inspire-Api-User 存在 | URL 解码后 JSON 解码为 $apiUser |
6.3 内部身份和安全边界
静态代码显示:
cacheManage、manage、province被列入 token 校验例外模块。- 只有
InnerTokenAuth::isInnerServer()为真时才进入 token 分支。 - token 从请求体
inner_token读取。 - 当前条件还判断 URI 第一段是否不等于
inner;正常/inner/...路径第一段通常就是inner。 Inspire-Api-User被直接按数组键读取,调用方未传时存在告警或契约不稳定风险。
因此不能仅凭应用代码声称所有 inner 请求都经过 token 校验。正确做法是同时确认:
- 生产入口是否只允许内网或可信网关访问。
- 网关是否完成身份校验并写入调用方用户头。
InnerTokenAuth::isInnerServer()在各环境如何判断。- URI 条件是否符合原设计。
- 敏感写接口是否有业务级权限和幂等校验。
安全文档只记录校验位置和风险,不记录任何真实 token、密钥或内部账号值。
6.4 通用请求骨架
curl -X POST '/inner/activity/flashActivity/listFlashActivity' \
-H 'Content-Type: application/json' \
-H 'Request-Id: <全链路请求ID>' \
-H 'Inspire-Api-User: <由可信网关注入的URL编码JSON>' \
--data '{
"page": 1,
"page_size": 20,
"activity_name": "",
"status": ""
}'
调用方需要 token 时应由安全配置注入:
{
"inner_token": "<由调用方运行环境安全注入>",
"activity_id": 10001
}
6.5 OPS 秒杀活动入口
| 方法 | 关键请求 | Service | 结果 |
|---|---|---|---|
listFlashActivity | 页码、名称、状态、时间 | FlashActivitySer::listFlashActivity | 活动分页 |
detailFlashActivity | activity_id | detailFlashActivity | 主表、商品、范围 |
saveFlashActivity | 活动字段、商品列表 | saveFlashActivity | 新增/编辑结果 |
submitFlashActivity | activity_id、提交参数 | submitFlashActivity | 直接生效或 OA 结果 |
saveFlashActivityStationScope | 活动、类型、站点 | saveStationScope | 黑白名单分页结果 |
importFlashActivityGoods | 文件、活动 ID | importGoods | 解析后的商品明细 |
exportFlashActivityGoods | activity_id | buildGoodsExport | 下载地址/文件 |
endFlashActivity | activity_id、原因 | endFlashActivity | 提前结束结果 |
sequenceDiagram
participant O as OPS
participant I as FlashActivity Controller
participant E as RequestEntry
participant S as FlashActivitySer
participant DB as 活动表
participant OA as OA/MQ
O->>I: JSON + Request-Id + 用户头
I->>E: fromArray(requestData)
E-->>I: 规范化 activityId/参数
I->>S: save/submit/detail
S->>DB: 主表、商品、站点范围
opt 需要审批
S->>OA: 发起 OA
end
S-->>I: 业务结果或异常
I-->>O: R successJson / bizErrorJson
6.6 服务站秒杀入口
inner/moveMall/FlashSale.php 会把请求体中的 sid 转成 JXCSID,然后删除原 sid,复用站管家侧 Service 约定。
{
"sid": 100001,
"activity_goods_id": 20001,
"qty": 2
}
flowchart LR
A["请求 sid"] --> B["stationRequestData"]
B --> C["JXCSID = sid"]
C --> D["删除 sid"]
D --> E["FlashSaleSer"]
E --> F["活动可见性"]
E --> G["库存与限购"]
E --> H["购物车/订单"]
主要方法:goodsList、goodsDetail、packageGoodsList、goodsFilters、getCartCount、addCart、cartList、cartFilters、updateCart、orderPreview、submitOrder、orderDetail、cancelOrder。
6.7 渠道入口与幂等
inner/channel/Order.php 的 create、confirm_receipt、order_approve 使用请求体 JSON 的 MD5 组成 Redis 锁键,降低短时间重复提交风险。
| 方法 | 业务 | 幂等/校验 | 下游 |
|---|---|---|---|
Order::create | 大客户渠道下单 | Redis 锁 + OrderCreateValidate | 场景化订单 Service |
Order::confirm_receipt | 渠道确认收货 | Redis 锁 + Validate | 渠道订单 Service |
Order::order_approve | 渠道审核订单 | Redis 锁 + Validate | FC 订单 Service |
Aftersale::apply | 渠道售后申请 | Redis 锁 + Validate | 售后 Service |
Aftersale::get_return_stock_in | 查询退货入库 | 当前静态实现直接返回空成功 | 尚未形成真实查询链路 |
Redis 锁只覆盖锁有效期内的相同请求,不等同于数据库唯一约束。渠道单号、来源订单号和落库唯一性仍需单独核对。
6.8 R 响应语义
{
"success": true,
"code": 0,
"data": {},
"msg": ""
}
业务错误示意:
{
"success": true,
"code": "<业务错误码>",
"data": {},
"msg": "业务校验失败原因"
}
R::bizError 与 R::bizErrorJson 的 success 仍为 true。前端和调用方必须以 code 为主要判断条件,不能只写:
if (response.success) { 当作成功 }
7. App tradeCode 网关协议
7.1 调用链
sequenceDiagram
participant A as App
participant C as Carsteward
participant O as OpenAppService
participant CFG as appapis.php
participant S as service/api/app
participant DB as DB/Redis
A->>C: tradeCode + data + sign + reqCode
C->>O: parse(params)
O->>O: checkParams/sign
O->>O: checkLogin/device/blacklist
O->>CFG: tradeCode 查映射
CFG-->>O: app.purchase.purOrderList
O->>S: service(注入登录上下文后的 data)
S->>DB: 查询或写入
S-->>A: splash JSON
7.2 统一入口
| URL | 请求解析 |
|---|---|
/carsteward/index | 读取 Form POST |
/carsteward/index2 | 合并 Form POST 与原始 JSON |
/carsteward/build | 根据传入数据生成签名外壳;不应作为生产公共签名能力暴露 |
7.3 外层字段合同
| 字段 | 必填 | 说明 |
|---|---|---|
tradeCode | 是 | appapis.php 的映射键 |
data | 是 | JSON 字符串,不是直接对象 |
sign | 是 | 客户端签名模块生成;文档不保存密钥 |
reqCode | 是 | 常见 app_android、app_ios、qcj |
targetCode | 是 | 目标系统标识 |
uid | 登录接口外通常需要 | 设备与登录用户关联 |
device | 登录接口外通常需要 | App 设备标识 |
verify | 不应由普通调用方使用 | 当前代码值为 yes 时跳过签名比较,属于高风险旁路 |
7.4 请求骨架
{
"tradeCode": "apppurOrderList",
"reqCode": "app_android",
"targetCode": "SMH",
"uid": "<当前登录用户ID>",
"device": "<当前设备ID>",
"sign": "<由客户端签名模块生成>",
"data": "{\"status\":\"pending\",\"page\":1,\"limit\":20,\"offset\":0,\"skey\":\"\"}"
}
注意两层 JSON:外层 data 是字符串。若直接传对象,签名结果、JSON 解码和 Service 参数可能不一致。
7.5 登录上下文注入
除以下接口外,网关会做登录检查:
apploginappMobileLoginappgetVersionappMobileLoginSmsCodeappMobileLoginSelectStation
登录通过后,网关把 Session 用户数据合入业务 data:
| 注入字段 | 含义 |
|---|---|
sid | 服务站 ID |
user_id | 用户 ID |
user_name | 用户名 |
areaCode / province | 区域兼容字段 |
eparchy | 地市/片区 |
user_ip | 调用 IP |
AppSessionHook 在 pre_system 阶段识别 app_android、app_ios,把 Session 过期和更新时间调到 30 天。排查“PC 正常、App 经常掉线”时,应同时检查 Hook 是否启用、请求体是否能被 Hook 解析、reqCode 是否准确。
7.6 静态映射审计结果
对 appapis.php 跳过整行注释后进行静态扫描,得到:
| 指标 | 结果 | 含义 |
|---|---|---|
| 有效映射行 | 190 | 配置中的活动映射行数 |
| 唯一 code | 189 | 存在重复 code |
| 重复 code | apphomePage 2 行 | 当前值相同,但会掩盖后续维护差异 |
| 映射目标文件不存在 | 40 行 | 配置存在不代表当前代码树可调用 |
映射目标缺失集中在部分首页、旧销售、旧采购、资金、系统联系人、个人消息等模块。遇到“接口类不存在”时,必须确认:
- 映射是否仍是线上有效协议。
- 文件是否被迁移到新 Controller 或新 Service。
- 大小写是否与 Linux 文件系统一致。
- 部署包是否漏文件。
- App 是否仍在调用已废弃 code。
7.7 主要 App code 分类
| 分类 | 代表 code | 目标目录 |
|---|---|---|
| 公共/商品 | appgetInventory、appgetBarCode | app/public |
| 配送 | transportTaking、transportReceipt | app/transport |
| 销售 | appsaleOrderList、appsale2StockOut | app/sale、app/sale2 |
| 采购 | apppurOrderList、apppurOrderReturnList | app/purchase |
| 资金 | appfundReceiptList、appfundPaymentList | app/fund |
| 系统 | 系统设置、客户、提醒相关 code | app/system |
| WMS | 扫码、入库、出库类 code | app/wms |
| 盘点 | inventoryCheck* | app/invCheck |
| 微仓 | moveOpen、inventoryCheckCreate | app/moveStore |
7.8 App 网关高风险点
| 风险 | 当前静态证据 | 影响 |
|---|---|---|
| 签名旁路 | verify=yes 跳过比较 | 必须由网关禁止普通调用方传入 |
| 设备校验失效 | checkDevice() 查询后立即 return | 后续设备不存在校验不可达 |
| 异常输出不稳定 | parse() 捕获后 print_r($e) | 可能破坏 JSON 合同并泄露内部信息 |
| 签名辅助入口 | Carsteward::build 可生成签名外壳 | 需确认生产路由是否隔离 |
| 映射悬空 | 40 行目标文件不存在 | 老版本 App 可能运行时失败 |
| 重复映射 | apphomePage 重复 | 后写覆盖前写,审计困难 |
| 缺少统一请求幂等 | 网关层没有 requestId 完成记录 | 写接口必须在业务层实现幂等 |
8. 直接 App 与 PDA Controller
8.1 为什么还有第二种 App 协议
新一些的接口不再通过 tradeCode 找类,而是直接使用 URL Controller,继承 BaseAppController。代表目录:
application/controllers/appapplication/controllers/pda
代表入口包括:
| Controller | 方法示例 | 业务 |
|---|---|---|
app/MoveSupply | getMsAppList、moveSupplyIn | 微仓申请与入库 |
pda/User | login、home、banner | PDA 登录与首页 |
pda/Purchase | getInStroComfirm、inStoIn | PDA 采购入库 |
app/SaOrders | 销售单相关方法 | 新 App 销售能力 |
8.2 请求数据流
flowchart TD
A["JSON 外壳"] --> B["BaseAppController"]
B --> C["检查 sign/reqCode/uid"]
C --> D["检查 Session 与设备"]
D --> E["data 解析为 businessData"]
E --> F["注入 sid/user_id/user_name"]
F --> G["具体 Controller 方法"]
G --> H["R::successJson / bizErrorJson"]
8.3 风险边界
checkSign对空data或reqCode=debug存在跳过路径,必须确认外部网关不允许生产调用方使用调试来源。R::bizErrorJson仍返回success:true。- 直接 App 和
tradeCodeApp 可能使用相同 Session,但参数合同不同,不能互相套用请求体。 - PDA 登录接口可能是例外入口,排查时先看具体 Controller 构造函数是否跳过父类校验。
9. 老 OpenAPI 协议
9.1 核心文件
| 文件 | 职责 |
|---|---|
application/controllers/Openapi.php | 统一 HTTP 入口 |
application/service/OpenService.php | 校验、幂等、映射和调用 |
application/config/apis.php | transCode 到 Service 映射 |
application/service/api/* | 真实老接口实现 |
Mongo dgj_api_log | 可选 requestId 请求执行记录 |
9.2 请求骨架
老 OpenAPI 的 data 通常是 Form 数组,不是 App 网关那种 JSON 字符串:
curl -X POST '/openapi/index' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'transCode=shmStatusUpdate' \
--data-urlencode 'reqCode=<来源系统>' \
--data-urlencode 'targetCode=<目标系统>' \
--data-urlencode 'requestId=<来源系统唯一请求ID>' \
--data-urlencode 'data[orderNo]=<业务单号>' \
--data-urlencode 'data[status]=<目标状态>'
9.3 参数和幂等流程
sequenceDiagram
participant X as 外部系统
participant O as OpenService
participant R as Redis Lock
participant M as Mongo dgj_api_log
participant S as API Service
X->>O: transCode + data + requestId
O->>O: 校验必填和映射
opt 有 requestId
O->>R: 设置 10 分钟请求锁
O->>M: 查询历史记录
alt 已完成 status=1
O-->>X: 直接 success,不重复执行
else 未完成或首次
O->>M: 写 status=0
end
end
O->>S: service(data)
S-->>O: success callback(requestId)
O->>M: 更新 status=1
O-->>X: legacy success response
9.4 requestId 的真实语义
- 有
requestId时先设置 Redis 锁,降低并发重复。 - 请求内容写入 Mongo
dgj_api_log,初始status=0。 - 如果找到相同
requestId且status=1,直接返回成功,不再执行 Service。 - Service 成功时通过 callback 把记录更新成完成。
- 若首次执行已经产生部分副作用但没有更新完成标记,重试仍可能再次进入业务层。
因此 requestId 是重要保护,但写接口仍需数据库唯一键、状态前置检查或业务幂等共同兜底。
9.5 代表性 transCode
| code | 映射 | 业务 |
|---|---|---|
shmStatusUpdate | invoice.statusUpdate | 订单审核状态同步 |
shmstockOut | invoice.StockOut | 订单出库 |
shmCloseOrder | invoice.closeOrder | 订单关闭 |
shmUpdateOrder | invoice.updateOrder | 订单修改 |
shmGatheringBillApprove | invoice.receiptCallback | 预订单收款成功回调 |
shmInfoEdit | goods.InfoEdit | 商品新增/修改/删除 |
shmDisabled | goods.Disabled | 商品上下架同步 |
shmAdvanceOrder | advanceOrder.listInfo | 预订单列表 |
9.6 OpenAPI 失败分支
flowchart TD
A["OpenAPI 失败"] --> B{"在哪一步失败"}
B -->|必填/映射| C["E1000x 参数错误"]
B -->|文件不存在| D["E50001 接口类不存在"]
B -->|requestId 冲突| E["检查 Redis 锁和 Mongo 状态"]
B -->|Service 异常| F["legacy _error"]
B -->|返回成功但业务没变化| G["可能命中已完成 requestId"]
E --> H["区分 status=0 部分执行与 status=1 已完成"]
F --> I["按 transCode、requestId、业务单号查日志"]
G --> J["核对来源是否错误复用了 requestId"]
10. allcarpart 路由与代理协议
10.1 路由特点
application/config/routes.php 为全车件配置了两类路由:
- 导入、导出、打印等显式路由。
allcarpart/(:any)/(:any)/(:any)三段通配路由。
代表性映射:
| 外部 URL | 本地方法 |
|---|---|
/allcarpart/purchase/po-return-order/batch-return-import | PoReturnOrder::batchReturnImport |
/allcarpart/sale/sa-order/import | SaOrder::import |
/allcarpart/sale/sa-invoice-order/export | SaInvoiceOrder::export |
/allcarpart/storage/inventory/export | Inventory::export |
/allcarpart/fund/payment-order/export | PaymentOrder::export |
/allcarpart/purchase/rfq/inquiry | Rfq::inquiry |
10.2 代理数据流
sequenceDiagram
participant U as 登录用户
participant D as DGJ allcarpart Controller
participant S as Session/Station
participant M as 全车件微服务
U->>D: /allcarpart/... + JSON/文件
D->>S: 校验 PC 登录和 enableAllCarPart
S-->>D: sid/uid/user_name
D->>M: 转发原 URI 和请求体,注入用户头
M-->>D: JSON/文件/错误
D-->>U: 透传或本地适配
排查全车件问题时应把责任边界分成:
- DGJ 登录和服务站功能开关。
- DGJ 本地导入/导出/打印适配。
- 代理 URL、请求头和请求体。
- 全车件微服务实际业务逻辑。
- 微服务返回后 DGJ 的透传或文件处理。
11. 报表、导出和文件入口
11.1 查询和导出权限可能分离
以 reports/PurchaseReport.php 为例,查询和导出分别检查权限。用户“能看到列表但不能导出”不一定是 bug,应核对:
- 查询权限,例如采购报表查询资源。
- 独立导出权限,例如订单查询导出资源。
- 导出日期范围限制。
- 导出数据量和超时。
11.2 报表调用链
flowchart LR
A["报表 Controller"] --> B["权限检查"]
B --> C["Validate"]
C --> D["Report Service"]
D --> E["报表 Model / 分表 SQL"]
E --> F["口径计算"]
F --> G{"查询还是导出"}
G -->|查询| H["splashJson 分页"]
G -->|导出| I["Excel/PDF/下载地址"]
11.3 文件接口判断顺序
- 看 HTTP 状态和
Content-Type。 - 看返回体是 JSON 下载地址、二进制文件还是 HTML 错误页。
- 核对前端是否把下载接口当普通 JSON 请求。
- 核对临时文件是否写入成功、路径和文件名是否一致。
- 核对反向代理的响应体大小、超时和缓冲配置。
- 核对导出数据是否跨越过多分表或日期。
12. MQ 消费者与 CLI 任务入口
12.1 这不是 HTTP API
tasks/*Notify.php 的 consume() 通常创建 topic consumer,按 routing key 注册回调并长期运行。业务函数接收的是 MQ 解码后的数组,返回 ACK 或 NACK。
sequenceDiagram
participant P as 外部生产者
participant Q as RabbitMQ
participant C as tasks Consumer
participant H as 回调方法
participant DB as 业务表
P->>Q: destination + routing key + payload
Q->>C: 投递消息
C->>H: registryCallback 对应方法
H->>DB: 校验状态并写业务数据
alt 成功或可忽略重复
H-->>Q: ACK
else 可重试失败
H-->>Q: NACK
end
12.2 代表性消费者
| 消费者 | destination | 主要 routing key/业务 |
|---|---|---|
PayCenterNotify | DEST_PAYCENTER_NOTIFY | 支付结果、支付异常、退款、白条、分期 |
OrderCenterNotify | DEST_ODC_NOTIFY | 订单审核、更新、关闭、取消、自制订单 |
OaNotify | DEST_OA_NOTIFY | OA 审批通知 |
OaResultNotify | OA 结果 destination | OA 状态同步 |
ItemCenterNotify | 商品中心 destination | 限购、套包、价格、敏感词 |
DispatchCenterNotify | 配送中心 destination | 出库、物流、自提、退货关闭 |
SaasOrderNotify | DEST_DGJ | SAAS 订单、库存事件、采购关闭确认 |
ChannelOrderNotify | DEST_DGJ_CHANNEL | 渠道下单、发货、退款、审核 |
12.3 正确启动方式
php index.php tasks/PayCenterNotify/consume
php index.php tasks/OaNotify/consume
消费者应该由进程管理器守护,不应把 consume 暴露成浏览器接口。排查“完全没有消费”时先看进程、队列绑定和 destination,而不是先查业务表。
12.4 秒杀补偿任务
php index.php tasks/FlashSaleTask/expireWaitPayOrders 100
php index.php tasks/FlashSaleTask/refreshGoodsStatus 500
php index.php tasks/FlashSaleTask/refreshInvalidCarts 500
php index.php tasks/FlashSaleTask/compensatePaidOrders 100
php index.php tasks/FlashSaleTask/compensateClosedOrders 100
php index.php tasks/FlashSaleTask/runCompensate 500
| 方法 | 作用 | 主要风险 |
|---|---|---|
expireWaitPayOrders | 释放超过支付截止时间的订单 | 与迟到支付回调并发 |
refreshGoodsStatus | 刷新活动商品有效状态 | 批量范围过大 |
refreshInvalidCarts | 失效不可购买购物车 | 前端缓存与数据库状态不同步 |
compensatePaidOrders | 补支付成功但活动单未同步 | 重复推进采购单 |
compensateClosedOrders | 补关闭后未释放库存 | 重复释放锁量 |
runCompensate | 顺序执行五类补偿 | 单项失败只输出 JSON,进程退出码未必能表达失败 |
FlashSaleTask 捕获异常后输出失败 JSON,但没有显式非零退出。因此调度平台不能只看进程退出码,还应解析输出或任务日志。
13. 统一数据流转链路
13.1 查询类请求
flowchart LR
A["请求参数"] --> B["入口解析"]
B --> C["身份/服务站上下文"]
C --> D["Validate / Entry"]
D --> E["Service 查询条件"]
E --> F["Model / 分表"]
F --> G["MySQL/Redis/ES"]
G --> H["领域格式化"]
H --> I["入口响应封装"]
查询结果不正确时按反方向排:响应格式化 -> Service 条件 -> 分表/表 -> 上下文字段 -> 原始请求。
13.2 写入类请求
flowchart TD
A["写请求"] --> B["鉴权和参数校验"]
B --> C["重复请求/状态前置检查"]
C --> D["事务开始"]
D --> E["主表"]
E --> F["明细/扩展/关系表"]
F --> G["库存/资金/活动副作用"]
G --> H{"事务结果"}
H -->|成功| I["提交事务"]
H -->|失败| J["回滚数据库"]
I --> K["MQ/外部系统/缓存"]
K --> L["响应成功"]
J --> M["响应失败或 NACK"]
外部调用如果发生在事务提交后,数据库成功而 MQ 失败需要补偿;外部调用如果发生在事务中,超时会拉长锁和事务。具体入口必须继续追 Service,不能从 Controller 层假设原子性。
13.3 回调类请求
flowchart TD
A["支付/OA/订单回调"] --> B["按来源单号找业务单"]
B --> C{"业务单存在?"}
C -->|否| D["NACK/记录异常"]
C -->|是| E{"当前状态允许推进?"}
E -->|否且已完成| F["按幂等策略 ACK 或忽略"]
E -->|否且冲突| G["NACK/人工确认"]
E -->|是| H["写回调事实和业务状态"]
H --> I["写明细/资金/库存副作用"]
I --> J["提交后 ACK"]
14. 响应合同总表
| 入口 | 成功判断 | 业务失败判断 | 传输失败 |
|---|---|---|---|
PC splashJson | status=success | status=error/nologin | HTTP/网络 |
PC resp | code=0 | 非零 code | HTTP/网络 |
R::successJson | code=SUCCESS | bizError 的业务 code | HTTP/网络 |
App splash | status=success | status=error | HTTP/非 JSON |
| OpenAPI | legacy status/message/result | _error code/message | HTTP/非 JSON |
| allcarpart | 依上游合同 | 依上游合同 | 代理超时/上游不可达 |
| MQ | ACK | NACK | 消费进程/连接失败 |
| CLI | 输出内容与任务日志 | 输出 success=0 等 | 进程未启动;退出码不一定可靠 |
14.1 调用方统一判断建议
第一层:HTTP/MQ/进程是否成功传输
第二层:响应是否符合该入口 JSON 合同
第三层:业务 code/status 是否成功
第四层:关键业务表和副作用是否真的完成
任何一层都不能代替下一层。尤其是回调和补偿任务,最终必须核对业务状态、明细和副作用表。
15. 鉴权与上下文矩阵
| 入口 | Session | 签名/token | 菜单/资源 | 服务站来源 | 幂等 |
|---|---|---|---|---|---|
| PC | 必需 | 通常无独立签名 | 构造层菜单 + 方法内资源 | Session JXCSID | 业务层 |
inner | 不走 PC Session | 内部 token 分支需按环境核验 | 依网关/业务校验 | 请求 sid 或显式字段 | 部分 Redis 锁/业务层 |
| App 网关 | 登录接口外必需 | App 签名 | 黑名单/强制下线 | Session 注入 sid | 业务层 |
| 直接 App/PDA | 多数必需 | BaseApp 签名 | Controller 特定逻辑 | Session 注入 | 业务层 |
| OpenAPI | 不依赖 PC Session | 来源系统合同 | transCode 白名单 | data 明确传入 | requestId 锁 + Mongo |
| allcarpart | PC Session | 代理边界 | enableAllCarPart | Session 注入 | 上游/业务层 |
| MQ | 不需要 Session | MQ 连接配置 | routing key 注册 | payload 中 sid/业务单 | 回调方法 |
| CLI | 主机执行权限 | 无 HTTP 鉴权 | is_cli() | 参数/数据本身 | 任务实现 |
16. 按现象排查
16.1 “接口类不存在”
- 确认是
tradeCode还是transCode。 - 在对应配置中找映射,注意大小写和重复键。
- 把点替换成斜杠,拼出
application/service/api/...php。 - 核对文件是否存在、类名是否
ucfirst后可实例化。 - 核对当前部署版本,而不是只看本地分支。
- 确认 App 是否仍调用已经迁移或废弃的 code。
16.2 “HTTP 200 但前端提示失败”
- 识别响应体系。
- 检查
status、code、msg,不要只看 HTTP 和success。 R::bizErrorJson需要按非成功code处理。- 检查前端是否把文件流当 JSON。
- 检查异常是否被
print_r破坏了 JSON。
16.3 “请求成功但数据查不到”
- 记录完整业务键:
sid、单号、主键、来源单号。 - 确认上下文最终使用
JXCSID还是请求sid。 - 确认分表定位。
- 查主表、明细、关系表是否都写入。
- 查事务是否提交。
- 查缓存/ES/报表是否异步刷新。
16.4 “同一个请求执行了两次”
flowchart TD
A["重复执行"] --> B{"入口类型"}
B -->|OpenAPI| C["核对 requestId 是否唯一且稳定"]
B -->|渠道 inner| D["核对 Redis 锁时长和请求体是否完全相同"]
B -->|App/PC| E["核对前端连点、超时重试、业务唯一键"]
B -->|MQ| F["核对 NACK、消费重启、ACK 时机"]
B -->|补偿任务| G["核对状态前置和处理标记"]
C --> H["Mongo dgj_api_log 状态"]
D --> I["数据库唯一性兜底"]
E --> I
F --> I
G --> I
16.5 “回调完全没有进入业务代码”
- 确认入口是 HTTP 回调还是 MQ routing key。
- MQ 场景检查消费者进程是否运行。
- 检查 destination、exchange、queue、routing key 是否匹配。
- 查消费者启动日志和注册回调。
- 查消息是否进入死信、重试或其他环境。
- HTTP 场景检查网关路由和来源鉴权。
- 最后才查业务单状态。
17. 常用定位命令
17.1 URL 和 Controller
rg -n "public function getInvPoList|public function addOutBound" application/controllers
rg -n "allcarpart/.+po-return-order|allcarpart/.+sa-order" application/config/routes.php
rg -n "class .* extends BaseApiController" application/controllers/inner
rg -n "class .* extends BaseAppController" application/controllers/app application/controllers/pda
17.2 App 和 OpenAPI 映射
rg -n '"apppurOrderList"|"appsale2StockOut"|"transportTaking"' application/config/appapis.php
rg -n '"shmStatusUpdate"|"shmstockOut"|"shmGatheringBillApprove"' application/config/apis.php
find application/service/api -type f -name '*.php' | sort
17.3 MQ 和任务
rg -n "createTopicConsumer|registryCallback|startConsume" application/controllers/tasks
rg -n "DEST_PAYCENTER_NOTIFY|TYPE_PAYCENTER_PAY_RESULT" application/KzData/Enums application/controllers/tasks
rg -n "return MqResultEnums::ACK|return MqResultEnums::NACK" application/controllers/tasks
rg -n "is_cli\(\)" application/controllers/tasks
17.4 从业务方法追到表
rg -n "function submitOrder|function paycenterPayResult" application/controllers application/Services
rg -n "SCM_PO_ORDER|SCM_SA_ORDER|SCM_INVENTORY|SCM_PAYMENT" application/Services application/service application/models
rg -n "send.*Mq|MqSer::|sendOA" application/Services application/service
17.5 日志检索键
优先组合检索,而不是只搜错误文本:
Request-Id + Controller 方法
业务单号 + routing key
sid + tradeCode
requestId + transCode
来源订单号 + 回调方法
18. 接口变更影响面
18.1 修改 Controller 时
- 请求字段是否由前端直接传入还是由父类注入。
- GET、Form、JSON 三种请求是否都需要兼容。
- 是否改变
splash、resp、R的响应形状。 - 导出接口是否仍返回正确文件类型。
- 方法是否被 routes.php 改写或由旧 App code 间接调用。
18.2 修改公共 Service 时
flowchart TD
A["公共 Service 改动"] --> B["PC 调用方"]
A --> C["inner 调用方"]
A --> D["App tradeCode"]
A --> E["直接 App/PDA"]
A --> F["OpenAPI"]
A --> G["MQ 回调"]
A --> H["补偿任务"]
B --> I["统一回归矩阵"]
C --> I
D --> I
E --> I
F --> I
G --> I
H --> I
重点核对:
- 参数名称和默认值是否兼容旧入口。
sid/JXCSID/storeId是否统一。- 返回值是否被不同 Controller 二次包装。
- 异常类型是否会被旧入口捕获。
- 事务外 MQ 和缓存是否会重复发送。
- 回调和补偿是否能够重复执行。
19. 回归清单
19.1 通用回归
- [ ] 正常请求返回符合对应入口合同。
- [ ] 缺少必填字段返回明确业务错误。
- [ ] Session/token/签名失效时不能进入业务写入。
- [ ] 服务站上下文不能由普通调用方越权覆盖。
- [ ] 同一请求快速重复提交不会重复创建主单。
- [ ] 请求超时后重试不会产生重复库存、资金或 MQ 副作用。
- [ ]
Request-Id能在入口和业务日志中串联。 - [ ] 业务错误不能仅依靠 HTTP 500 表达。
- [ ] 敏感信息不出现在响应、日志和文档中。
19.2 PC
- [ ] 登录、账号停用、服务站停用、菜单为空分别验证。
- [ ] GET、Form 和 JSON 参数合并符合预期。
- [ ] 总部账号切换门店后
JXCSID正确。 - [ ] 查询权限和导出权限分别验证。
- [ ] 登录失效时 AJAX 和页面请求表现符合前端预期。
19.3 inner
- [ ] JSON 与 Form 请求都能按约定解析。
- [ ]
Inspire-Api-User缺失/格式错误有明确处理。 - [ ] 网关、网络和 token 安全边界经过环境验证。
- [ ]
R::bizErrorJson前端按code识别失败。 - [ ] 渠道重复请求和数据库唯一性共同验证。
19.4 App
- [ ]
tradeCode映射文件真实存在。 - [ ] 外层
data字符串和签名数据完全一致。 - [ ] Android、iOS、全车件来源分别回归。
- [ ] Session Hook 的 30 天策略生效。
- [ ] 设备校验、签名旁路和调试来源在生产边界不可滥用。
- [ ] 老版本 App 调用的 code 仍兼容或有明确升级策略。
19.5 OpenAPI/MQ/任务
- [ ] 相同
requestId重复调用不会重复执行已完成请求。 - [ ]
status=0的部分执行请求有人工判定和恢复路径。 - [ ] MQ 重复投递、乱序、NACK 重试都验证。
- [ ] 消费进程重启后业务幂等成立。
- [ ] CLI 任务失败时调度平台能从输出或日志识别。
- [ ] 补偿任务执行前后都有数量和状态核对。
20. 已确认风险与待环境验证项
20.1 静态代码已确认
| 结论 | 证据 |
|---|---|
| 系统存在八类入口协议 | controllers、config、tasks 和基础 Controller |
| App 活动映射 190 行、唯一 code 189 | appapis.php 静态扫描 |
apphomePage 重复映射 | appapis.php 相邻两行 |
| 40 行 App 映射目标文件不存在 | 映射到 service/api 的文件存在性审计 |
App verify=yes 可跳过签名比较 | OpenAppService::checkParams |
| App 设备检查后续代码不可达 | OpenAppService::checkDevice 的提前返回 |
R::bizError* 返回 success:true | application/Components/R.php |
| OpenAPI 使用 Redis 请求锁和 Mongo 完成记录 | OpenService |
| 秒杀任务失败仅输出 JSON | FlashSaleTask::runTask |
| 渠道退货入库查询当前返回空成功 | inner/channel/Aftersale::get_return_stock_in |
20.2 必须在目标环境确认
inner路由的真实网络可达范围和网关鉴权规则。InnerTokenAuth::isInnerServer()在开发、预发、生产的判断结果。- 40 个缺失 App Service 是否仍有线上调用流量。
Carsteward::build、verify=yes、reqCode=debug是否被网关阻断。- MQ destination、queue、routing key 的实际绑定和死信策略。
- 消费者由何种进程管理器守护、失败如何告警。
- OpenAPI Redis 锁 TTL、Mongo 索引和历史数据保留策略。
- 文件导出在反向代理上的超时、大小和缓冲限制。
21. 证据来源
| 主题 | 代码证据 |
|---|---|
| PC 登录、参数和响应 | application/core/BaseController.php |
inner 参数、请求头和 token 分支 | application/core/BaseController.php 中 BaseApiController |
R 响应语义 | application/Components/R.php |
| App 网关 | application/controllers/Carsteward.php、application/service/OpenAppService.php |
| App code 映射 | application/config/appapis.php |
| 直接 App/PDA | application/core/BaseAppController.php、application/controllers/app、pda |
| App Session Hook | application/hooks/AppSessionHook.php、application/config/hooks.php |
| 老 OpenAPI | application/controllers/Openapi.php、application/service/OpenService.php |
| 老接口映射 | application/config/apis.php |
| allcarpart 路由和代理 | application/config/routes.php、application/controllers/allcarpart |
| OPS 秒杀接口 | application/controllers/inner/activity/FlashActivity.php |
| 服务站秒杀接口 | application/controllers/inner/moveMall/FlashSale.php |
| 渠道入口 | application/controllers/inner/channel/Order.php、Aftersale.php |
| 支付 MQ | application/controllers/tasks/PayCenterNotify.php |
| 秒杀补偿任务 | application/controllers/tasks/FlashSaleTask.php |
| MQ 枚举 | application/KzData/Enums/MqEventEnums.php |
22. 一页式定位结论
看到普通 URL:先找 controllers,再看父类和 routes.php。
看到 tradeCode:查 appapis.php,再拼 service/api/app 文件。
看到 transCode:查 apis.php,再拼 service/api 文件。
看到 routing key:查 MqEventEnums 和 tasks 中 registryCallback。
看到定时任务:必须用 CLI,先确认 is_cli 和进程管理。
看到 success=true:仍要判断 code/status。
看到 HTTP 200:仍要核对主表、明细、库存/资金和 MQ 副作用。
改公共 Service:必须回归 PC、inner、App、OpenAPI、MQ 和补偿任务的实际调用方。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| PC 路由 | /scm/*、/basedata/*、/reports/* | session、query/form/JSON、sid | 对应 Controller + BaseController | application/service/scm/* 或 Services/* | 领域 Service | 页面动作转换为业务表事实 |
| Inner API | /inner/* | 内部鉴权头、sid、用户头、JSON | controllers/inner/* + BaseApiController | 领域 Service/Provider | 与 PC 共用 Service/Model | 内部系统调用形成同类业务事实 |
| App API code | appapis.php 映射 | API code、App 签名/用户上下文、参数 | controllers/app/pda/服务映射 | application/service/api/* | 领域 Service | App/PDA 请求落到采购、销售、库存等领域 |
| 老 OpenAPI | Openapi.php + apis.php | appId、签名、外部单号、参数 | Openapi/OpenService | 老适配 Service | 来源单映射 | 外部合同转换为内部表和状态 |
| CLI/MQ | CLI 调度或 Broker | task 参数、destination、routing key、消息体 | controllers/tasks/* | Consumer/补偿 Service | 业务键 | 异步状态、数量或同步结果 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 路由层 | Web 访问日志、routes.php/API code 配置 | URI、method、API code、request_id | 命中目标 Controller/method | 404、method 不符、code 未映射 | 配置映射到类方法 | | 鉴权层 | Base Controller/API/OpenAPI 日志 | session/appId/header、sid、request ID | 用户和站点上下文建立 | 401/403、签名/时间戳失败 | 上下文传入目标 Service | | 参数层 | Validate/Entry 异常 | 参数名、错误码、业务单号 | 标准参数对象生成 | JSON/form 形态不符、必填缺失 | 标准参数与 Service 入参对照 | | 业务层 | Controller/Service 日志 | 类方法、业务单号、来源单号 | commit 并返回业务结果 | 业务异常、rollback | 单号回查主明细和流水 | | 响应/异步层 | Provider/MQ/响应日志 | HTTP code、业务 code、message ID | 业务成功且 ACK/下游回执 | HTTP 200 但业务失败、NACK/超时 | request ID/业务键贯穿两端 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 解析入口 | routes.php、apis.php、appapis.php | 事务外 | path/API code/method | 无 | 外部入口 -> Controller/method | 配置行、访问日志 |
| 建立上下文 | BaseController/BaseApiController/OpenAPI | 事务外 | session/header/signature | 请求上下文 | 补入用户、sid、来源;失败零业务写入 | 鉴权日志、上下文快照 |
| 参数归一 | Validate/Entry/Controller | 事务外 | query/form/JSON | 无 | 多种载荷 -> Service DTO/数组 | 校验结果、Controller 入参 |
| 领域写入 | 目标 Service | 本地事务 | 当前状态和业务数据 | 主表、明细、流水 | insert/update;状态/数量/金额 old -> new | 单号、事务结果、表记录 |
| 返回/通知 | Controller、MqSer、Provider | commit 后 | 已提交结果 | HTTP 响应、MQ/下游 | 返回成功不等于异步完成 | HTTP/业务码、消息 ACK、下游状态 |
子模块追踪:pc-entry PC Controller 入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 请求处理 | 浏览器 /scm/*、/basedata/*、/reports/* | session、sid、query/form/JSON、业务单号 | application/core/BaseController.php -> application/controllers/scm/InvPo.php | 登录用户、菜单权限、站点上下文、当前业务态 | 参数校验外零写入;领域 Service 事务内 insert/update old -> new | access request ID + URI + sid + billNo | 401/403/校验失败零业务写入;超时按业务单回查是否已提交 |
| 响应回查 | Controller JSON/下载响应 | HTTP code、业务 code、返回单号 | application/core/BaseController.php | commit 结果和异步任务 ID | DB 已提交后返回;MQ/文件生成不属于同一事务 | request ID + response code + business ID | HTTP 200 不等于业务成功;用返回单号查主表和异步状态 |
子模块追踪:inner-entry Inner 内部 API 入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 内部请求 | /inner/* 服务间调用 | 内部鉴权头、sid、用户头、JSON | application/controllers/inner/activity/FlashActivity.php | 身份上下文、字段规则、目标业务当前态 | 鉴权/校验阶段不写;Service 本地事务写主明细与状态 old -> new | request ID + URI + sid + activity/order ID | 鉴权失败零写入;调用超时按稳定业务键回查,不盲重试 |
| 统一响应 | R 组件封装返回 | success/code/data、业务 ID | application/Components/R.php | Service 返回和异常类型 | 响应阶段 DB 不变;异步通知另有边界 | request ID + R code + business ID | 调用方必须判断业务 code;失败后仅补未完成环节 |
子模块追踪:app-entry App tradeCode 映射入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| code 路由 | App 网关提交 tradeCode | tradeCode、签名/会话、sid、参数 | application/config/appapis.php -> application/core/BaseAppController.php | code 映射、登录上下文、目标服务类 | 路由和鉴权不写;命中 application/service/api/app/purchase/purOrderList.php 等目标 | request ID + tradeCode + user/sid | code 未映射或类不存在零写入;先修配置/文件一致性 |
| 业务执行 | App service API 方法 | 来源单号、分页或业务动作参数 | application/service/api/app/purchase/purOrderList.php | 当前业务事实和权限范围 | 查询类不写;动作类在领域事务内 old -> new | request ID + tradeCode + bill/source ID | 响应成功后仍查业务主表;重复动作依赖来源键幂等 |
子模块追踪:pda-entry App、PDA 直接 Controller 入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 直接路由 | App/PDA URL 直接访问 Controller | token/session、sid、扫码/单据参数 | application/controllers/Carsteward.php -> application/core/BaseAppController.php | 登录上下文、设备输入、业务状态 | 校验阶段不写;领域事务写盘点/出入库等事实 | request ID + URI + sid + 单号/条码 | 与 tradeCode 协议不同;404/参数形态错时不应切换接口猜测 |
| 结果核对 | PDA 扫码提交返回 | 业务单、SKU、仓位、返回 code | application/core/BaseAppController.php | 受影响行和库存/单据结果 | commit 后返回;缓存/MQ 为异步边界 | request ID + billNo + SKU/location | 网络重试前按单号和 SKU 查流水,避免重复出入库 |
子模块追踪:openapi-entry 老 OpenAPI 入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 外部请求 | 合作方提交 transCode | appId、签名、timestamp、requestId、来源单 | application/controllers/Openapi.php -> application/config/apis.php | 签名、时间窗、transCode 映射、幂等记录 | 鉴权失败零写入;通过后调用 application/service/OpenService.php | request ID + transCode + external orderNo | 同一 requestId 的语义按现有实现核实;超时先查来源单映射 |
| 合同适配 | OpenService 调具体 API | 外部字段、内部标准参数 | application/service/OpenAppService.php -> application/service/api/invoice/statusUpdate.php | 原外部单、内部关系、当前状态 | 领域事务 insert/update old -> new;响应适配不再写 | requestId + 外部/内部单号 + result code | HTTP 200 仍判断业务码;重复回调保持单向状态和零重复副作用 |
子模块追踪:allcarpart-entry 全车件路由与代理入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 路由代理 | allcarpart 显式 route 请求 | URI、method、token、外部询价/订单号 | application/config/routes.php -> application/controllers/Carsteward.php | 路由目标、身份和外部请求上下文 | 代理前不写;本地落单时由领域事务创建来源关系 | request ID + route + external orderNo | 路由错/上游超时先查代理日志和外部单,不重复创建本地订单 |
| 下游回查 | 代理响应或异步状态查询 | 外部/本地单号、状态 | application/controllers/Carsteward.php | 本地映射与外部最终态 | 查询阶段 DB 不变;确认结果后按服务规则 old -> new | request ID + 两端单号 + external code | 外部合同和生产日志需环境验证;不凭静态代码宣称已送达 |
子模块追踪:report-file-entry 报表、导出与文件入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 查询导出 | 报表页面查询或导出按钮 | sid、时间窗、筛选、导出权限 | application/core/BaseController.php -> application/controllers/scm/InvPo.php | 权限、分表口径、状态/软删过滤 | 查询只读 不写;异步导出仅写任务/文件元数据 | request ID + report/export type + filters | 查询和导出权限可分离;失败保留筛选与任务 ID 后重试 |
| 文件读取 | 模板下载、结果文件访问 | file/task ID、文件名 | application/config/routes.php | 文件归属、状态、路径白名单 | 文件读取 DB 不变;生成完成 pending -> success/failed | request ID + task/file ID | 文件不存在先查生成任务;路径和对象存储事实需目标环境确认 |
子模块追踪:mq-cli-entry MQ Consumer 与 CLI Task 入口
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 消费/任务 | Broker 投递或调度平台 CLI | destination、routing key、message/task 参数 | application/controllers/tasks/PayCenterNotify.php、application/controllers/tasks/FlashSaleTask.php | CLI 环境、事件注册、消息版本、当前业务态 | 单消息/单记录事务更新 old -> new;ACK 不属于 DB 事务 | message/task ID + routing key + business key | 非 CLI/未知事件零写入;异常按 ACK/NACK 和任务重试策略处理 |
| 幂等回查 | Consumer 重投或补偿重跑 | 原 message ID、稳定业务键、批次 | application/KzData/Enums/MqEventEnums.php | 当前状态、已有流水和副作用 | 已完成则 DB new -> new 且新增流水 0;仅补缺失异步环节 | business key + retry/batch + row count | 不以随机 messageId 当幂等键;不确定先查事实再小批重放 |