本文回答一个很容易判断错误的问题:“DGJ2 的开放接口入口在哪里?”

答案不是一个 URL 或一个 Controller。当前仓库同时保留交易码映射型 OpenAPI、APP 交易码入口、SAAS 交易码入口、积分商城签名入口、令牌式开放页面、全车件代理入口、BaseApiController 直连 JSON API,以及名为 v2 但仍属于 PC 登录态的接口。它们的请求格式、鉴权、上下文、响应、日志和幂等完全不同。

本文只公开接口字段名、代码路径和验证方法,不记录任何签名密钥、令牌值、Cookie、生产主机或内部网络凭据。静态代码无法证明生产网关、WAF、IP 白名单和调用方实际流量,相关结论统一标为待环境确认。

1. 先读结论:八类入口不能混用

家族入口路由方式主要鉴权典型调用方
老 OpenAPI/Openapi/indextransCode -> apis.php -> service/api代码内未见签名;可能依赖外部网关NC、OMS、商城等历史系统
APP 映射 API/Carsteward/index、index2tradeCode -> appapis.php数据签名+登录 Session;存在历史旁路站管家 APP、部分全车件来源
老 SAAS 映射 API/OpenSaasApi/indextransCode -> SaasApis.php代码内未见签名;可能依赖网关快维/SAAS 历史调用
积分商城映射/PointMall/indextradeCode -> appapis.php独立签名算法积分商城
令牌式 OpenSaaS/opensaas/Order/*显式 Controller 方法pr 令牌查 Redis 客户上下文VIP 下单/支付页面
全车件代理/allcarpart/**显式路由+三段通配PC Session+服务站功能权限DGJ PC 全车件模块
新直连 API/inner/** 等Controller/方法显式路由Inner Token/网关头/网络边界,按入口不同内部微服务、OPS、任务
PC v2/v2/**Controller/方法普通 PC Session 和菜单权限新版 PC 页面
flowchart TB
    C[调用方] --> G{入口家族}
    G --> O[Openapi/transCode]
    G --> A[Carsteward/tradeCode]
    G --> S[OpenSaasApi/transCode]
    G --> P[PointMall/tradeCode]
    G --> T[opensaas/pr token]
    G --> F[allcarpart/session proxy]
    G --> I[inner/direct JSON]
    G --> V[v2 PC session]
    O --> Legacy[application/service/api]
    A --> App[application/service/api/app]
    S --> Saas[application/service/api/saas]
    P --> App
    T --> Vip[VipOrderSer]
    F --> Micro[全车件微服务]
    I --> Domain[application/Services]
    V --> PcService[PC Service]

2. 文档目标和边界

本文重点说明:

  1. 请求如何从 URL 和交易码定位到真实 PHP 类。
  2. 每类入口要求哪些 envelope、Header、Session 或 Token。
  3. 登录用户、服务站、来源系统和请求 ID 如何注入业务数据。
  4. 成功、失败、重复请求、弃用接口分别返回什么。
  5. 请求日志、Mongo 幂等记录、Redis 锁和业务表怎样关联。
  6. 老字段、状态、错误码、大小写和 JSON 字符串为何不能随意修改。
  7. 如何在不破坏历史调用方的前提下迁移到显式版本接口。

本文不逐项重写 248 个映射项的业务逻辑。采购、销售、库存、活动、财务等真实业务规则仍以对应专题为准;本篇负责解释它们从开放入口怎样进入。

3. 数量和存量规模

通过当前配置和文件静态审计得到:

对象数量说明
apis.php 交易码39老 OpenAPI
SaasApis.php 交易码20老 SAAS
appapis.php 交易码189APP、WMS、盘点、配送等
service/api PHP 文件208包含上述多个家族
service/api/app PHP 文件151APP 映射实现
service/api/saas PHP 文件18SAAS 映射实现
controllers/allcarpart PHP 文件23全车件代理/导入/打印
controllers/v2 PHP 文件6PC v2 Controller

配置映射到物理文件的静态检查发现:老 OpenAPI 2 个、SAAS 2 个、APP 39 个映射找不到对应路径。缺失可能代表已废弃、迁移后遗留、配置拼写或发布包不完整,不能直接删除,也不能把“配置中存在”当成“接口可用”。

4. 各入口的信任边界

flowchart LR
    Internet[外部/客户端] --> Edge[DNS/WAF/API网关/负载均衡]
    Edge --> DGJ[DGJ2 Controller]
    DGJ --> Auth{应用内验证}
    Auth -->|签名| AppAPI[APP/PointMall]
    Auth -->|Session+权限| ACP[AllCarPart/v2]
    Auth -->|Redis令牌| OpenSaaS[令牌式OpenSaaS]
    Auth -->|InnerToken/Header| Inner[直连内部API]
    Auth -->|代码内未见验证| Legacy[Openapi/OpenSaasApi]

“代码内未见验证”不等于生产公网裸露。可能还有 Caddy、网关、VPN、IP 白名单或安全组保护;但也不能因为“应该有网关”就假定安全。上线审计必须同时验证网络层和应用层。

5. 老 OpenAPI 请求 envelope

application/controllers/Openapi.php 接收 POST 表单并交给 OpenService::parse。

代表性请求:

POST /Openapi/index
Content-Type: application/x-www-form-urlencoded

transCode=shmstockOut&reqCode=OMS&targetCode=SMH&requestId=req-unique-id&data=[{"nid":"PO...","billNo":"SH...","sku_id":"SKU...","qty":1,"price":10,"amount":10}]
字段是否必填含义注意事项
transCode是配置交易码老 OpenAPI 使用 transCode,APP 使用 tradeCode
reqCode是请求方系统代码同时影响响应编码和日志来源
targetCode是目标系统代码当前分发主要不依赖其值,但合同仍要求
data是业务数据很多 Service 把它当 JSON 字符串再次 decode
requestId条件必填请求唯一 ID存在时启用 Redis 锁和 Mongo 幂等状态

6. 老 OpenAPI 完整分发链

sequenceDiagram
    participant X as NC/OMS/商城
    participant C as Openapi Controller
    participant O as OpenService
    participant R as Redis request lock
    participant M as Mongo dgj_api_log
    participant S as 动态Service
    participant D as MySQL业务表
    X->>C: POST envelope
    C->>O: parse(str_enhtml(post))
    O->>O: checkParams
    opt requestId存在
        O->>R: SET NX EX 600s
    end
    O->>M: INSERT/查询请求状态
    alt 相同requestId已成功
        O-->>X: 通用success,不再执行业务
    else 首次或上次未成功
        O->>O: transCode查apis.php
        O->>S: 加载映射类并调用 service 方法
        S->>D: 校验和业务读写
        S-->>O: _success/_error直接输出
        O->>R: 释放锁
        O->>M: 成功回调status=1
    end

7. transCode 动态定位规则

以配置为例:

shmstockOut => invoice.StockOut

解析步骤:

  1. 从 apis.php 取映射字符串。
  2. 把 . 换成 /,得到 invoice/StockOut。
  3. 拼接 application/service/api/invoice/StockOut.php。
  4. include_once 文件。
  5. 对末段执行 ucfirst,实例化 StockOut。
  6. 调用统一方法 service($params['data'])。
flowchart LR
    A["transCode=shmstockOut"] --> B["apis.php"]
    B --> C["invoice.StockOut"]
    C --> D["invoice/StockOut"]
    D --> E["service/api/invoice/StockOut.php"]
    E --> F["new StockOut"]
    F --> G["service data"]

Linux 文件名大小写敏感。修改配置或重命名类时,要同时验证映射值、文件名、类名和部署系统大小写,不能只在默认不区分大小写的开发磁盘上测试。

8. 老 OpenAPI 交易码分组

目录映射数代表用途
invoice15审核、采购出库、关闭、修改、预订单收款/核销
services6服务商新增、修改、重置密码、登录、Session 校验
goods4商品编辑、上下架、价格、测试接口
advanceOrder4预订单列表、数量、详情、审核
topservice3商城收款、取消订单、修理厂新增
goodsInfo3销售组织、结算价、商城筛选
users2手机号、全车件客户信息
其他2分类、服务商类型

shmInfoEdit、shmGatheringBillApprove 等配置仍存在,但实现直接返回“接口已弃用”。这是一种保持旧调用方不报 404 的兼容策略。

9. 老 OpenAPI 参数校验和错误码

OpenService::checkParams 使用以下通用错误码:

错误码触发条件含义
E10001整体参数为空请求参数格式不正确
E10002data 为空请求体格式不正确
E10003transCode 为空交易码为空
E10004reqCode 为空请求系统代码为空
E10005targetCode 为空目标系统代码为空
E10006交易码不存在无效交易码
E10008Redis NX 加锁失败相同重复请求
E50001映射文件不存在接口类不存在

错误码和中文文案可能被历史调用方直接判断。改文案也可能是破坏性变更,迁移时应先通过调用日志确认对方判断的是 status、error_code 还是文本。

10. requestId Redis 锁

stateDiagram-v2
    [*] --> 无锁
    无锁 --> 持锁: SET key NX EX 600
    持锁 --> 重复拒绝: 并发请求SET失败
    持锁 --> 无锁: _success/_error释放
    持锁 --> 自动过期: 进程崩溃/未走统一响应
    自动过期 --> 无锁

锁的目的只是防止 10 分钟内并发执行,不是永久幂等记录。_success 和 _error 从原始 POST 中读取 requestId 后释放锁;若 Service 直接 die/echo/throw 而未走统一响应,锁只能等待过期。

11. Mongo 请求幂等状态

OpenService 使用 Mongo collection dgj_api_log:

字段含义
requestId外部请求唯一 ID
原始 envelope当前代码把参数整体插入
status=0已接收但未通过成功回调完成
status=1Service 调用 _success 后更新成功
createTime/modifyTime创建和状态更新时间
stateDiagram-v2
    [*] --> 未记录
    未记录 --> 处理中: INSERT status=0
    处理中 --> 已成功: _success callback status=1
    处理中 --> 再执行: 锁释放后相同requestId重试
    已成功 --> 快速成功: 相同requestId不再执行业务

高风险点:重复成功请求只返回新的“通用成功”,不会返回第一次的原始业务结果。若调用方需要本地单号、状态或明细,不能把这个响应当作首次结果重放。

12. requestId 正确使用规则

场景requestId 策略
网络超时重试同一次业务动作必须复用原 requestId
用户发起新的独立动作必须生成新 requestId
批量内多张业务单最好每业务单独 ID,或明确批次原子语义
已成功但客户端没收到响应先查询业务事实,再用同 ID 重试
上次业务失败并已回滚可同 ID 重试,Mongo status=0 会允许再次执行
上次部分成功不能盲重试;先按业务键核对每一行

13. 老 OpenAPI 统一响应

BaseService::_success 典型字段:

{
  "transCode": "shmstockOut",
  "reqCode": "OMS",
  "targetCode": "SMH",
  "requestId": "req-unique-id",
  "params": "原data的兼容格式",
  "status": "success",
  "message": "调用成功!",
  "result": {},
  "totalNumber": 0
}

失败响应增加 error_code,但不同 Service 可能把错误信息或部分业务结果放进 result。reqCode=KW 时使用 JSON_UNESCAPED_UNICODE,其他来源使用默认 JSON 编码,这也是历史兼容差异。

14. 统一响应并不等于统一 HTTP 状态

_success/_error 都直接 die(json_encode(...)),代码中没有设置对应 4xx/5xx HTTP 状态。调用方很可能始终收到 HTTP 200,再读取 JSON status/error_code 判断业务结果。

迁移到标准 HTTP 状态时不能直接替换:老客户端可能只处理 200;网关重试策略也可能因为状态变化而改变。推荐新版本同时返回标准 HTTP 状态和稳定业务码,旧路径维持合同直到流量归零。

15. 接口日志演进

日志位置当前用途风险
Mongo dgj_api_log老 OpenAPI requestId 幂等状态可能包含完整业务请求,需脱敏和保留期
ApiService/ApiService 日志_success/_error 记录 params/result/source大报文和敏感字段风险
t_sys_apis_log*历史接口日志表常量多数 _saveLog DB insert 已注释
t_sys_apis_log4OpenSaasService 入口仍主动插入请求是否持续增长、索引和清理待确认
APP logger app/app记录完整 APP 请求可能包含 sign、手机号、业务数据
BaseApi logger inner/api记录 body 和 headers头部可能含内部身份信息

不要在公共文档或工单粘贴完整日志。排查只保留 requestId、交易码、业务单号、脱敏 sid/uid、状态和错误码。

16. 代表链路:shmstockOut 采购出库

shmstockOut -> invoice.StockOut 是理解老 OpenAPI 如何落业务事实的代表案例。

核心业务字段:

字段含义校验/用途
nid来源采购订单编码用 saCode 或本地 billNo 找采购单
billNo外部出库/发运单号SH 按单号去重,TS 按单号+箱号去重
boxNo箱号与 billNo 组成并发锁键
sku_idSKU转换为本地 invId
qty出库数量0 直接跳过,非空校验
price/amount单价/金额写采购出库明细
wmsNo物流单号履约追踪
isGift赠品标志区分采购明细

17. shmstockOut 数据流

sequenceDiagram
    participant OMS as OMS/NC
    participant O as OpenService
    participant S as StockOut
    participant G as t_bs_goods
    participant PO as t_scm_po_order + 分片明细
    participant OUT as t_scm_po_out_info
    participant R as Redis lock
    OMS->>O: transCode=shmstockOut,data=JSON数组
    O->>S: service(data字符串)
    S->>S: JSON decode+字段校验
    S->>G: sku_id转invId
    S->>OUT: 查billNo/boxNo重复
    loop 每行
        S->>PO: 按nid找采购主单和明细
        S->>R: 锁billNo+boxNo 300秒
        S->>S: 构造170410采购出库明细
    end
    S->>PO: 事务更新billStatus=4
    S->>OUT: 批量新增出库明细
    S-->>OMS: success/error envelope

18. shmstockOut 幂等和部分成功风险

  1. SH 前缀发现同 billNo 已存在时整次报重复。
  2. TS 前缀按 billNo-boxNo 过滤重复。
  3. 同一请求内再次出现相同单号箱号会跳过重复加锁。
  4. Redis 锁失败或数据库已有重复时,代码把 $flag=false 后继续其他行。
  5. 只要 infoList 非空,仍可能提交其他行并返回“调用成功”。
  6. 因此这是允许部分处理的批量接口,不能只看外层 status=success。
flowchart TD
    A[一批出库行] --> B{逐行校验/去重/加锁}
    B -->|有效| C[加入infoList]
    B -->|重复/锁失败| D[flag=false并跳过该行]
    C --> E{infoList是否非空}
    D --> E
    E -->|是| F[提交有效行并success]
    E -->|否| G[返回数量0不处理]

重试前必须按 billNo+boxNo+skuId 查已落明细,不能因为外层成功或失败直接整批重放。

19. “弃用但返回成功”的兼容模式

goods/InfoEdit 和 invoice/receiptCallback 当前直接:

_success([], "该接口已弃用")

它的含义是“请求被兼容接收,但不再产生业务副作用”,不是业务处理成功。调用方若只看 status=success,可能误认为商品或收款已经同步。

推荐弃用过程:先记录调用量和调用方;返回结构增加机器可识别 deprecated=true/replacement 但不删除旧字段;推动调用方迁移;流量归零后再下线。

20. APP API envelope

Carsteward 入口使用 tradeCode,data 是 JSON 字符串。

POST /Carsteward/index
Content-Type: application/x-www-form-urlencoded

tradeCode=appsaleOrderList&reqCode=app_android&targetCode=SMH&sign=<signature>&data={"page":1,"rows":20}

常见 envelope 字段:

字段作用
tradeCodeAPP 映射交易码
reqCode客户端/来源标识
targetCode目标系统标识
sign对 data 标量字段计算的历史签名
dataJSON 字符串业务参数
device、uid历史设备校验入参;当前校验代码提前返回
verify历史旁路字段,当前存在安全风险

21. APP 签名算法的兼容规则

不记录密钥值,只描述算法:

  1. JSON decode data。
  2. 字段名转小写用于不区分大小写排序。
  3. 按键名升序恢复原字段名。
  4. 只拼接非数组的 key=value,嵌套数组被忽略。
  5. 先对拼接字符串做一次 MD5,再与固定共享密钥标识组合后再次 MD5。
flowchart LR
    A[data JSON] --> B[decode数组]
    B --> C[键名大小写归一后排序]
    C --> D[忽略数组值]
    D --> E[key=value&...]
    E --> F[第一次MD5]
    F --> G[拼共享密钥标识]
    G --> H[第二次MD5=sign]

兼容重点:字段大小写、数字转字符串、浮点精度、空值、布尔值、字段顺序和嵌套数组都可能改变签名。不能“顺手改成标准 HMAC”而不提供新版本。

22. APP 签名当前高风险事实

静态代码确认:

  • verify=yes 时允许签名不一致继续执行。
  • 签名失败响应会把服务端计算出的签名写进错误信息。
  • Carsteward::build 接收 JSON 后返回可用 envelope 和签名。
  • 签名共享密钥硬编码在代码中,无法独立轮换调用方。
  • 签名只覆盖 data 中的标量,不覆盖 envelope 和嵌套数组。
  • 请求中没有强制时间戳、nonce 或签名级防重放。
flowchart TD
    A[收到APP请求] --> B[计算服务端sign]
    B --> C{sign相同}
    C -->|是| D[继续]
    C -->|否| E{verify是否yes}
    E -->|是| D
    E -->|否| F[返回含计算值的错误]

这些入口是否被网关限制不能由代码证明,但应用层应立即收敛:删除旁路、关闭签名生成入口、错误不回显计算值、密钥配置化并支持版本化轮换。

23. APP 登录白名单和来源旁路

无需登录检查的交易码:

  • applogin
  • appMobileLogin
  • appgetVersion
  • appMobileLoginSmsCode
  • appMobileLoginSelectStation

除此之外,代码在 reqCode != 'qcj' 时才执行登录、设备、黑名单和强制下线检查。也就是说 reqCode=qcj 会跳过整段登录态验证。签名仍会执行,但结合签名旁路时风险更高。

生产必须确认:qcj 是否由可信网关固定注入、客户端能否自行伪造、允许访问哪些 tradeCode。

24. APP 登录上下文注入

sequenceDiagram
    participant A as APP
    participant O as OpenAppService
    participant S as Session/Redis
    participant D as 动态App Service
    A->>O: envelope+data JSON
    O->>O: 校验签名
    alt 登录接口或reqCode=qcj
        O->>O: 跳过登录检查
    else 普通接口
        O->>S: login_session
        O->>O: 黑名单/强制下线
        O->>O: data JSON decode
        O->>O: 合并sid,user_id,user_name,areaCode等
    end
    O->>D: service(合并后的数组)

注入字段来自服务端登录态:sid、user_id、user_name、areaCode/province、eparchy。业务 Service 不应信任客户端同名字段;当前 array_merge($clientData,$serverData) 由后者覆盖前者,这一点必须保持。

25. APP 设备校验实际上被禁用

checkDevice 虽然查询 t_app_device,但查询后立即 return,后面的“设备不存在则强制下线”不可达。

flowchart LR
    A[device+uid查询APP_DEVICE] --> B[立即return]
    B -.不可达.-> C[不存在则Compulsory offline]

这意味着“单设备登录”不能靠当前方法成立。修复早退会改变大量存量用户登录行为,必须先统计设备数据完整性、老版本 device 字段、同账号多设备和客服操作流程,再灰度启用。

26. APP 映射分组

目录映射数代表业务
public28登录、首页、商品、车型、版本
system28客户、仓库、账户、系统设置
wms20WMS、签收、仓内操作
purchase16采购、关闭、退货、MOQ
invCheck16盘点
transport14骑手、轨迹、签收、取消
fund14收付款
sale13销售单和出库
person9个人消息等
moveStore8微仓/移动商城
workOrder7工单
sale26第二代销售接口
其他10调拨、全车件、积分商城、扩展

配置中 apphomePage 重复定义,PHP 最终保留最后一个值。重复键即使值相同也说明配置缺少自动校验。

27. APP 映射到缺失文件的审计结果

当前静态审计有 39/189 个映射找不到目标文件,集中在:

  • 旧首页、Banner、发运展示。
  • 旧商品图片/参数。
  • 旧销售新增、详情修改、删除。
  • 一批旧采购新增、关闭、退货、MOQ。
  • 旧资金新增/修改。
  • 旧系统客户和个人消息。

部分功能可能已经迁移到 sale2、直连 Controller 或新微服务,但配置没有清理。处理顺序必须是:线上访问日志 -> 客户端版本 -> 替代入口 -> 灰度告警 -> 下线,不可直接删映射。

28. APP 高精度坐标兼容

OpenAppService::setIniPrecision 试图对配送坐标相关交易码把 PHP precision 调为 16,以兼容高德 14 位小数。

但当前调用是 appSignKey($params_data),传入的是解码后的 data,方法却从参数中读取 tradeCode。如果业务 data 本身没有 tradeCode,这段兼容不会触发。

需要用真实配送请求验证:签名前 JSON decode 是否已经损失精度、客户端和服务端拼接字符串是否一致、经纬度是否应该用字符串传输。

29. APP 异常响应风险

OpenAppService::parse 捕获 Exception 后直接 print_r($e),而多数校验通过 splash 直接结束。结果是异常路径可能返回调试对象、HTML 或非统一 JSON。

这会导致:

  • 客户端无法稳定解析。
  • 堆栈或文件路径可能泄漏。
  • HTTP 状态与业务状态不一致。
  • 接口日志难以聚合错误码。

迁移时应保持旧客户端可识别字段,同时统一新增 code/message/request_id/data 响应层。

30. 老 SAAS 映射入口

OpenSaasApi -> OpenSaasService -> SaasApis.php -> service/api/saas/* 使用与老 OpenAPI 类似的 envelope:

POST /OpenSaasApi/index
Content-Type: application/x-www-form-urlencoded

transCode=shmOrderCreate&reqCode=KW&targetCode=SMH&data={"customerCode":"...","srcOrderNo":"...","details":[]}

它校验 data/transCode/reqCode/targetCode 和交易码,但代码内未见签名、requestId 锁或 Mongo 幂等。BaseController('api') 也会跳过普通 PC Session 校验。

31. 老 SAAS 交易码

20 个映射覆盖:

  • 客户注册和状态。
  • 供应商与库存查询。
  • 订单创建、关闭。
  • 活动列表和详情。
  • 品牌、车型、分类、商品查询。
  • VIP 下单和会员信息。
  • 按手机号查客户、小程序下单。

静态检查中 shmGetCarModel、shmGetCars 没有目标文件,配置注释中又有“备用”语义,需确认是否从未上线或已由其他车型接口替代。

32. SAAS 创建销售单请求

代表性 shmOrderCreate 数据:

{
  "customerCode": "external-customer-code",
  "srcOrderNo": "external-order-no",
  "srcOrderId": "external-order-id",
  "description": "order remark",
  "details": [
    {
      "srcOrderEntryId": "external-line-id",
      "skuId": "SKU001",
      "qty": 2,
      "price": 15.5,
      "amount": 31,
      "deduction": 0,
      "isGift": 0,
      "activityId": 0
    }
  ]
}

33. SAAS 创建销售单数据流

sequenceDiagram
    participant K as 快维/SAAS
    participant S as OrderCreate
    participant B as t_bs_user_join/contact/admin
    participant G as t_bs_goods
    participant O as t_scm_sa_order分片
    participant I as t_scm_sa_order_info分片
    participant M as 通知MQ
    K->>S: customerCode+srcOrderNo+details
    S->>B: 校验注册、绑定服务站、主账号
    S->>O: 按sid+srcOrderNo查重
    S->>G: skuId转invId/指导价
    S->>I: 事务逐行新增,qty写负数
    S->>O: 新增170502销售主单
    S->>I: 按srcOrderId回填iid
    S->>S: commit
    S->>M: 发送新订单提醒
    S-->>K: 订单生成成功

34. SAAS 创建销售单关键口径

外部字段本地字段/算法注意事项
customerCode查客户绑定 sid/contact/uid一个客户必须绑定服务站
srcOrderNo主单来源单号当前代码用它查重,是核心幂等键
srcOrderId主单/明细来源 ID明细回填 iid 时使用
srcOrderEntryId明细来源 ID行级追踪
qty明细写 -abs(qty)销售数量采用负数方向
qty*price-deduction行金额代码另用传入 amount 累加总金额,需核对一致性
skuIdt_bs_goods.id=invId缺失商品的错误处理需回归
本地生成billNo=XD...、transType=170502不由外部传入

风险:行明细金额按公式计算,但主单 sum2 累加传入的 arr['amount'],二者不一致时可能形成主明细金额差异。

35. SAAS 订单幂等和事务后副作用

stateDiagram-v2
    [*] --> 未创建
    未创建 --> 已创建: sid+srcOrderNo不存在,事务提交
    已创建 --> 重复拒绝: 相同srcOrderNo再次调用
    已创建 --> 通知失败: commit后sendMailToMq异常
    通知失败 --> 已创建: 业务单仍存在

srcOrderNo 是比外层 requestId 更重要的业务幂等键。事务提交后发送提醒 MQ,通知失败不应重建销售单。数据库是否有 (sid,srcOrderNo) 唯一索引需生产 DDL 确认。

36. 令牌式 OpenSaaS 不是 OpenSaasApi

application/controllers/opensaas/Order.php 继承 OpenSaasController,使用 pr 查询 Redis:

KEY_VIP_ORDER_TOKEN + pr -> kw_customer_code/customer_id/customer_name/...

令牌可以来自 Query 或 JSON Body。校验成功后生成 $customerData,后续页面和接口不再要求 PC Session。

sequenceDiagram
    participant U as 用户浏览器/SAAS
    participant C as OpenSaasController
    participant R as Redis token hash
    participant O as opensaas/Order
    participant V as VipOrderSer
    U->>C: pr token
    C->>R: HGETALL token key
    alt 无token/失效/无客户编码
        C-->>U: 非法请求/令牌失效
    else 有效
        C->>O: 注入customerData
        O->>V: 方案/下单/支付/状态
        V-->>U: R::success/error
    end

日志当前记录 Query 和 Body,可能包含 pr。日志必须脱敏,Token 需要短有效期、单用途、可撤销和防重放策略。

37. VIP 下单子流程

方法核心请求数据/副作用
getPlanInfopr 上下文返回客户、套餐、有效期、一次性 nostr
submitplanId,nostr,startDate,endDate校验防重,创建 VIP 订单,缓存支付信息 30 分钟
toPaypay_no获取支付二维码/URL
statuspay_no查询订单支付状态
flowchart LR
    A[有效pr] --> B[getPlanInfo]
    B --> C[planList+nostr]
    C --> D[submit planId+nostr]
    D --> E[创建VIP订单/pay_no]
    E --> F[Redis支付缓存30分钟]
    F --> G[toPay]
    G --> H[支付]
    H --> I[status轮询]

38. 积分商城映射入口

PointMall -> OpenPmService 复用 appapis.php 动态加载 Service,但签名算法作用于整个请求 envelope,并排除 sign 字段;它与 APP 对 data 签名的算法不是同一合同。

高风险点:

  • 同样使用历史共享密钥和双 MD5。
  • 签名失败返回服务端计算值。
  • 请求先写 t_sys_apis_log2,再加载 Service。
  • 配置中只有 2 个 pointMall 映射,但它理论上能按 appapis.php 查任意 tradeCode;是否由网关限制需确认。

39. 全车件代理的业务边界

全车件 PC Controller 继承 App\controllers\allcarpart\Controller,最终转发到 MICRO_URL + 当前URI。

sequenceDiagram
    participant U as DGJ PC用户
    participant B as BaseController
    participant A as allcarpart Controller
    participant P as StationSer权限
    participant M as 全车件微服务
    U->>B: Session请求
    B->>B: 登录/账号/菜单校验
    B->>A: 路由到具体Controller/action
    A->>P: enableAllCarPart(sid)
    alt 未开通或无权限
        A-->>U: JSON status=500/message
    else 已开通
        A->>A: 解析JSON或使用内部data
        A->>M: POST JSON + sid/uid/user_name headers
        M-->>A: code/message/data
        A-->>U: JSON响应
    end

40. 全车件路由分类

路由组代表路径本地特殊处理
仓库/货位storage/storage-area*导入、区域转发
商品goods/good-search/*查询代理
采购purchase/goods/*、退货、发运导入、打印、导出
销售sale/sa-order/*、出库、销退导入、打印、导出
库存storage/inventory/*导入、库存/发运导出
资金fund/payment-order/*、account导出和代理
盘点storage/pd/import本地解析后转发
询价purchase/rfq/inquiry显式方法
通配allcarpart/:module/:controller/:action转到 index/$action

显式 import/print/export 路由必须放在三段通配之前,否则可能被错误转发到通用 index。

41. 全车件请求上下文

代理 Header 由服务端 Session 生成:

{
  "sid": "当前服务站",
  "uid": "当前账号",
  "user_name": "当前姓名"
}

这些字段不是前端可信入参。微服务必须只信任经过网关认证的 DGJ 请求,并对 Header 做来源验证。当前本地 Controller 把下游异常转换为 JSON code/message,但通常仍是 HTTP 200;前端要按业务码判断。

42. 全车件本地导入、打印和导出不是纯代理

部分 Controller 会:

  1. 读取上传 Excel。
  2. 在本地解析列、转换 SKU/单号。
  3. 调全车件微服务查询/导入。
  4. 用 DGJ View 生成打印页面或 PDF。
  5. 把微服务数据导出 Excel。

因此全车件改字段时要同时回归微服务 JSON、DGJ 导入模板、PHP 解析、打印 View 和导出列,不能只测通用 forward。

43. 新直连 BaseApiController 请求解析

BaseApiController 统一支持:

  • Content-Type: application/json:解析 raw body。
  • 其他 Content-Type:合并 GET、POST 和 urlencoded raw body。
  • Request-Id Header:保存到 Controller 属性。
  • Shipper Header:保存货主标识。
  • Inspire-Api-User Header:URL decode 后解析 JSON 身份。
  • 结构化日志:URI、Body、Header。
flowchart TD
    A[HTTP请求] --> B{Content-Type是JSON}
    B -->|是| C[json_decode raw stream]
    B -->|否| D[GET+POST+parse_str body]
    C --> E[requestData]
    D --> E
    E --> F[读取Request-Id/Shipper/Headers]
    F --> G[InnerToken条件校验]
    G --> H[解析Inspire-Api-User]
    H --> I[具体Controller方法]

44. BaseApiController 内部认证边界

代码逻辑是:当模块不在少数例外列表、且 InnerTokenAuth::isInnerServer() 判定为内部服务时,才读取 body inner_token;而 URI 第一段为 inner 时又跳过该 token 校验。

这说明认证边界依赖:

  • isInnerServer() 的网络/请求判断。
  • URI 分段。
  • 上游网关对 inner 路径的保护。
  • Inspire-Api-User Header 的可信注入。

不能只看到 BaseApiController 就认为所有内部接口都校验 token。每个新入口必须画清“谁能到达 URL、谁写 Header、应用校验什么”。

45. v2 并不等于 OpenAPI v2

当前 controllers/v2 只有 Application、Desktop、Demo、Resource、Report、Right 等 PC 功能,继承普通 BaseController,默认执行 Session、账号可用性、维护状态和菜单权限检查。

flowchart LR
    A["/v2/Right 等"] --> B["BaseController 默认构造"]
    B --> C["login_session"]
    C --> D["账号、维护、菜单校验"]
    D --> E["v2 Service"]

它是 PC 代码版本/资源权限演进,不应被外部调用方当成稳定的开放平台版本协议。

46. 鉴权能力对照矩阵

入口Session签名requestId 防重Redis Token功能权限主要缺口
老 OpenAPI否未见有,条件启用否未见依赖外部网络边界
APP条件有有但存在旁路签名级无 nonce否业务内旁路、签名生成、设备校验禁用
老 SAAS否未见仅业务字段否未见入口鉴权和统一幂等
PointMall否有未见否未见可映射范围和签名回显
令牌 OpenSaaS否否nostr 用于提交有客户上下文Token 日志与重放
全车件有服务间另行确认微服务负责否开通标志+权限下游 Header 信任
BaseApi/inner否入口自定Request-Id仅保存Inner Token条件校验入口自定路径和网关边界不统一
PC v2有否业务自定否菜单/资源不是开放版本合同

47. 响应合同对照

家族成功判断失败判断HTTP 状态兼容风险
老 OpenAPI/SAASstatus=successstatus=error,error_code多为 200字段多、原 data/params 兼容
APPsplash/各 Service 自定义文本、JSON 或异常输出不统一老客户端强依赖文案
OpenSaaS/innerR::successR::error取决于 R 实现与老 envelope 不同
全车件下游 code/message/data本地异常转 code/message多为 200下游可能使用 0 或 200 表示成功
PC v2页面/splash/JSON登录重定向或业务错误页面语义不适合外部系统

48. 字段兼容规则

  1. 不删除旧字段;先新增可选字段并保持默认值。
  2. 不把 transCode 和 tradeCode 统一改名到原路径。
  3. 不改变 data 是 JSON 字符串还是对象的历史形态。
  4. 不改变 qty 正负方向、金额单位和字符串/数字类型。
  5. 不改变来源单号的大小写、前缀和前导零。
  6. 不复用旧字段表达新语义;新增明确字段和版本。
  7. 返回新增字段不能破坏老客户端严格反序列化。
flowchart LR
    A[旧请求v1] --> C[兼容适配层]
    B[新请求v2] --> C
    C --> D[规范领域Command]
    D --> E[业务Service]
    E --> F[规范领域Result]
    F --> G[旧响应适配]
    F --> H[新响应适配]

49. 状态和错误码兼容规则

  • 外部状态必须先映射到本地状态机,不能直接写任意 billStatus。
  • 对已完成/已关闭终态的重复更新,要返回幂等成功或明确不可变错误。
  • 同一个错误码不能在不同版本改变语义。
  • 新错误先使用新增 code,旧路径保留原 message。
  • “接口已弃用但成功”必须增加可观测告警,避免静默丢业务。
  • 批量部分成功必须返回逐行结果,不能只给总成功。

50. 推荐迁移架构

flowchart TB
    L[Legacy Controllers] --> AD[Anti-Corruption Adapters]
    N[Versioned /api/v2] --> DTO[Typed Request DTO]
    AD --> DTO
    DTO --> AUTH[统一认证/租户/幂等]
    AUTH --> CMD[Domain Command]
    CMD --> DS[Domain Service]
    DS --> OB[Outbox/Audit]
    DS --> RES[Domain Result]
    RES --> LR[Legacy Response Adapter]
    RES --> NR[Versioned Response]

老入口不直接重写业务,而是只做字段、状态、错误码和身份适配;新旧入口最终调用同一个领域 Service。这样才能既修复安全和一致性,又不复制两套订单逻辑。

51. 接口下线流程

stateDiagram-v2
    [*] --> 存量
    存量 --> 已标记弃用: 文档+响应字段+监控
    已标记弃用 --> 双轨迁移: 新旧入口并行
    双轨迁移 --> 只读兼容: 禁止新写,保留查询
    只读兼容 --> 无流量: 调用量连续为0
    无流量 --> 下线: 路由/映射/类删除
    双轨迁移 --> 回退: 新版异常

每一步都要有调用方、流量、替代接口、截止时间和回滚负责人。仅在代码注释写“弃用”不算完成迁移。

52. 映射配置自动审计

建议把以下检查加入发布前任务:

配置键是否重复
映射文件是否存在
文件名和类名大小写是否匹配
类是否有 public service 方法
同一文件是否被多个交易码引用
标记弃用的交易码是否还有流量
缺失映射是否有明确替代入口
flowchart LR
    A[apis/appapis/SaasApis] --> B[解析映射]
    B --> C[检查目标文件]
    C --> D[反射类和service方法]
    D --> E[重复键/孤立文件/缺失文件报告]
    E --> F[与线上访问日志合并]
    F --> G[阻断高风险发布]

53. SOP:交易码返回无效或类不存在

  1. 确认请求使用 transCode 还是 tradeCode。
  2. 确认进入 /Openapi、/OpenSaasApi、/Carsteward 还是 /PointMall。
  3. 在对应配置文件查交易码,不能只全局搜同名方法。
  4. 把映射的 . 替换为 /,检查目标文件大小写。
  5. 检查末段类名和 service() 方法。
  6. 查线上部署包是否与当前仓库一致。
  7. 查调用方是否仍在使用已经注释“备用/弃用”的 code。
rg -n '"shmstockOut"|"appsale2StockOut"|目标交易码' \
  application/config/apis.php application/config/SaasApis.php application/config/appapis.php

rg -n 'class StockOut|public function service' application/service/api

54. SOP:APP 签名或登录失败

flowchart TD
    A[APP调用失败] --> B{错误阶段}
    B -->|缺sign/data/code| C[核对envelope]
    B -->|签名不一致| D[核对data原始字符串/类型/精度]
    B -->|Login failure| E[查Session和Cookie]
    B -->|Compulsory offline| F[查黑名单/强制重登]
    B -->|接口类不存在| G[查appapis和文件]
    D --> H[确认客户端版本与签名算法版本]
    E --> I[确认是否NO_LOGIN或reqCode旁路]

排查时禁止把服务端共享密钥或完整签名请求发到聊天、文档或工单。只比较脱敏后的排序字段、类型、是否为空和客户端/服务端摘要。

55. SOP:requestId 重复、超时或结果不确定

  1. 查 Redis 锁是否仍在 10 分钟窗口。
  2. 查 Mongo dgj_api_log 的 requestId/status/createTime/modifyTime。
  3. 用业务来源单号查 MySQL 最终事实。
  4. status=1 但客户端无结果时,不要换新 requestId 重建业务。
  5. status=0 且业务表无事实时,可复用同 requestId 重试。
  6. status=0 但业务表已有部分事实时,进入专项人工核对。
flowchart TD
    A[调用超时] --> B[查Mongo status]
    B -->|1| C[查业务事实并视为已处理]
    B -->|0/无记录| D[查业务来源单号]
    D -->|无事实| E[同requestId安全重试]
    D -->|完整事实| C
    D -->|部分事实| F[停止自动重试,逐行核对]

56. SOP:SAAS 订单重复或金额不一致

SELECT id, sid, billNo, srcOrderNo, srcOrderId, billStatus,
       totalQty, totalAmount, amount, arrears, createTime
FROM t_scm_sa_order_0_:sid_mod_16
WHERE sid = :sid AND srcOrderNo = :src_order_no;

明细表实际分片名按第 10、15 篇确认。排查:

  1. 客户编码是否绑定唯一服务站。
  2. 同 sid+srcOrderNo 是否多行。
  3. 主单总额是否等于明细计算金额。
  4. 明细 qty 是否为负数。
  5. srcOrderId/srcOrderEntryId 是否完整。
  6. 事务提交后提醒 MQ 失败不能作为重建理由。

57. SOP:全车件代理失败

  1. 确认 PC Session、账号状态和菜单权限。
  2. 查服务站 isAllCarPart 和 ALL_CAR_PART 数据权限。
  3. 确认命中显式路由还是三段通配。
  4. 查本地是否先做 Excel/打印转换。
  5. 查转发 Header 中的 sid/uid 是否来自正确 Session。
  6. 查 MICRO_URL 下游响应 code/message 和超时。
  7. 对导入/导出同时检查文件、对象存储和模板列。

58. SOP:inner/直连接口身份错误

  1. 先确认 Controller 的父类,不要假定所有 /inner 都一样。
  2. 查 BaseApiController 是否实际执行 InnerTokenAuth。
  3. 查 URI 第一段、模块例外列表和 isInnerServer() 结果。
  4. 确认 Inspire-Api-User 由哪个可信网关注入。
  5. 查 Request-Id 是否贯穿日志,但不要认为它自动幂等。
  6. 查目标 Service 是否自行校验 sid、uid、货主和权限。

59. 常用只读查询和日志定位

SELECT id, source_type, status, time
FROM t_sys_apis_log4
WHERE source_type = :req_code
ORDER BY id DESC
LIMIT 100;

实际字段以生产 DDL 为准,优先查日志平台而不是扫描全表中的大 JSON。

rg -n "class OpenService|class OpenAppService|class OpenSaasService|class OpenPmService" application/service

rg -n "transCode|tradeCode|requestId|setRequestLock|dgj_api_log" \
  application/controllers application/service application/core

rg -n "verify.*yes|checkDevice|NO_LOGIN_CHECK_APIS|appSignKey" application/service/OpenAppService.php

rg -n "allcarpart" application/config/routes.php application/controllers/allcarpart

rg -n "extends BaseApiController|extends OpenSaasController|extends BaseController" \
  application/controllers/inner application/controllers/opensaas application/controllers/v2

rg -n "srcOrderNo|srcOrderEntryId|shmOrderCreate|shmstockOut" \
  application/service/api application/models

60. 安全和兼容风险登记册

编号风险影响建议
R48-01老 OpenAPI 代码内未见鉴权外部可伪造业务请求确认网关后增加应用级调用方认证
R48-02老 SAAS 同样无签名/requestId重放和伪造mTLS/HMAC+业务幂等
R48-03APP verify=yes 旁路绕过签名立即下线并审计访问量
R48-04APP 错误回显计算签名降低攻击成本只返回稳定错误码
R48-05Carsteward/build 提供签名生成形成签名 oracle从生产路由移除/仅本地 CLI
R48-06共享密钥硬编码无法轮换和隔离调用方密钥中心+key id+版本轮换
R48-07签名不含 envelope/数组字段可被替换或重放新版 HMAC 覆盖 canonical body
R48-08reqCode=qcj 跳过登录来源伪造导致越权网关固定来源+交易码白名单
R48-09checkDevice 提前 return单设备策略失效先补数据再灰度启用
R48-10APP 捕获异常 print_r非 JSON/信息泄漏统一异常响应和日志
R48-11配置缺失目标 43 项运行时类不存在映射 lint+流量治理
R48-12重复映射键静默覆盖配置漂移解析源文件检测重复键
R48-13文件/类大小写依赖Linux 发布失败CI 在 Linux 检查
R48-14requestId 锁仅 10 分钟过期后可重入业务唯一键+持久幂等结果
R48-15重复成功不返回原始结果调用方状态不确定保存并重放首次响应摘要
R48-16Mongo 记录完整请求敏感数据和容量字段脱敏、TTL/归档
R48-17DB 接口日志部分注释、部分仍写排查口径不一致统一结构化日志平台
R48-18多数错误仍 HTTP 200网关/监控误判新版本标准化,旧路径兼容
R48-19弃用接口返回 success调用方静默丢业务机器可识别弃用状态和告警
R48-20批量接口可部分成功整批重放重复返回逐行结果+行级幂等
R48-21SAAS 主明细金额来源不同金额不一致服务端统一重算并校验
R48-22SAAS 来源单唯一索引未知并发重复销售单生产 DDL 确认并补唯一键
R48-23OpenSaaS Token 写日志Token 泄漏日志脱敏、短期单用途
R48-24全车件信任转发 Header伪造租户上下文服务间认证并签名 Header
R48-25BaseApi 记录完整 Header内部凭据泄漏日志Header allowlist/redaction
R48-26/inner 路径跳过 token依赖隐含网络边界明确网关策略和双层认证
R48-27v2 名称易被误当开放版本错误集成文档和路由命名分层
R48-28坐标签名精度兼容可能未触发配送接口签名失败用字符串坐标和契约测试

61. 回归测试清单

61.1 老 OpenAPI

  • 6 个通用缺参错误码和无效交易码。
  • 目标文件不存在、类名大小写、缺 service()。
  • requestId 首次、并发重复、成功后重复、失败后重试、锁超时。
  • Mongo status 0/1 和业务表事实一致。
  • reqCode=KW 与其他来源的中文编码。
  • 弃用接口保持旧结构但产生弃用告警。
  • shmstockOut SH/TS、同批重复、部分成功、0 数量、事务失败。

61.2 APP

  • NO_LOGIN 交易码与普通交易码。
  • 签名字段顺序、大小写、空值、数字、浮点、嵌套数组。
  • Session 失效、黑名单、强制重登、服务站状态异常。
  • 所有旁路字段在生产无效。
  • 服务端注入 sid/uid 覆盖客户端同名字段。
  • 老客户端错误 JSON 和新客户端兼容。
  • 39 个缺失映射逐项有流量或替代结论。

61.3 SAAS 和 OpenSaaS

  • 客户未注册、未绑定、重复来源单、商品缺失、金额不一致。
  • 同 srcOrderNo 并发只产生一张销售单。
  • 事务成功 MQ 失败不重复建单。
  • pr 缺失、失效、错误客户、过期和重复使用。
  • nostr 重复提交、支付缓存 30 分钟边界、支付状态轮询。

61.4 全车件、inner 和 v2

  • 未登录、未开通、无数据权限、正常代理。
  • 显式 route 优先级和三段通配。
  • 导入、打印、导出以及下游 0/200 两种成功码。
  • JSON/form 两种 BaseApi 请求解析。
  • InnerToken、URI、网关注入用户和 Request-Id。
  • v2 PC Session、菜单、总分店切换,不作为外部 API 暴露。

62. 证据来源

主题当前代码证据
老 OpenAPI Controller/分发/Mongoapplication/controllers/Openapi.php、application/service/OpenService.php
APP 入口、签名、登录和分发application/controllers/Carsteward.php、application/service/OpenAppService.php
老 SAASapplication/controllers/OpenSaasApi.php、application/service/OpenSaasService.php
积分商城application/controllers/PointMall.php、application/service/OpenPmService.php
交易码配置application/config/apis.php、appapis.php、SaasApis.php
统一响应、锁和用户注入application/core/BaseService.php
PC/Direct API 基类application/core/BaseController.php、BaseApiController
老采购出库代表链application/service/api/invoice/StockOut.php
SAAS 销售创建代表链application/service/api/saas/OrderCreate.php
令牌 OpenSaaSapplication/controllers/opensaas/Order.php、OpenSaasController
全车件路由和代理application/config/routes.php、application/controllers/allcarpart/Controller.php
全车件本地转换application/controllers/allcarpart/*
PC v2application/controllers/v2/*、application/service/v2/*
日志表常量application/config/tables.php

63. 待环境确认

  1. /Openapi、/OpenSaasApi、/Carsteward/build、/PointMall 的生产公网可达性、网关认证、IP 白名单和调用方清单。
  2. APP verify=yes、reqCode=qcj 和签名生成入口是否仍有真实流量。
  3. 共享签名密钥的调用方数量、轮换机制和客户端最低版本。
  4. 39 个 APP、2 个老 OpenAPI、2 个 SAAS 缺失映射的线上访问量与替代入口。
  5. Redis requestId 锁前缀、Mongo collection 索引、唯一约束、TTL 和容量。
  6. t_sys_apis_log1-4 的生产 DDL、写入量、保留期和查询索引。
  7. 老调用方对 HTTP 状态、status/error_code/message/result/params 的实际判断方式。
  8. SAAS 销售表 (sid,srcOrderNo) 和采购出库 (billNo,boxNo,skuId) 的生产唯一索引。
  9. OpenSaaS pr Token 的创建入口、TTL、单用途/撤销策略和日志脱敏。
  10. 全车件微服务的服务间认证、Header 信任、成功码和幂等合同。
  11. InnerTokenAuth::isInnerServer() 的真实网络判定以及 /inner 路由的网关保护。
  12. 所有旧接口的责任人、替代版本、迁移截止日和回滚方案。

64. 修改后的最小发布验证

  1. 用不存在的交易码验证错误码和日志,不执行写业务。
  2. 选一个只读老 OpenAPI,验证 envelope、requestId 首次/重复和 Mongo 状态。
  3. 选一个 APP 只读接口,验证签名、Session 和服务端用户注入。
  4. 验证所有签名旁路和签名生成入口在目标环境不可用。
  5. 选一个 SAAS 只读接口和一笔测试来源单,验证来源单幂等。
  6. 验证全车件未开通、已开通和下游失败三条分支。
  7. 验证一个 BaseApi JSON 和 form 请求,确认 Header 脱敏日志和身份校验。
  8. 对照配置自动审计报告,确保本次改动没有新增缺失文件、重复键和类名大小写问题。
  9. 最后回归旧客户端/旧调用方原响应字段、错误码、状态和编码,确认新安全层没有改变其合法请求语义。

请求-日志-数据变更追踪卡

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
老 OpenAPIOpenapi.php + apis.phpappId、timestamp、sign、API code、外部单号/参数Openapi ControllerOpenService -> service/api/*API code + source order老合同转换为内部业务动作
App/车管家Carsteward.php + appapis.phpApp 签名、用户/站点、API codeCarsteward ControllerOpenAppServiceapp code + business IDApp 调用领域 Service
OpenSaaS/积分商城OpenSaasApi/PointMalltenant/app、签名、来源单对应 ControllerOpenSaas/OpenPm Servicetenant+source orderSaaS/积分业务转换为内部单据
全车件转发/allcarpart/*/opensaas/Orderroute、来源单、业务 payloadAllcarpart/OpenSaas ControllerMICRO_URL Providerrequest+source order请求代理到微服务并返回兼容响应

日志证据矩阵

| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 路由/API code | Web/Openapi 日志 | request_id、URI、API code、appId/tenant | 命中预期 service/api 类方法 | E10001/路由未配置、方法不兼容 | API code 映射服务方法 | | 鉴权/解析 | OpenService | appId、timestamp、nonce/sign 结果、source order | 鉴权成功,参数按旧合同归一 | E10002-E10008、JSON/form 编码差异 | 标准参数进入领域 Service | | 业务处理 | service/api/*/领域 Service | 类方法、sid、source/internal bill | commit 并返回旧格式成功码 | E50001、部分写入、重复来源 | 来源单映射内部单号 | | 代理下游 | MICRO Provider/OpenSaas | external request ID、source order、HTTP/业务码 | 下游成功且响应适配 | timeout、HTTP 200 业务失败 | 两端 request/source ID |

环节数据变更台账

步骤代码位置事务读取事实写入表/缓存/MQ字段或数量变化回查证据
入口映射apis.php/appapis.php/routes事务外path/API code/method无old code -> service class/method配置行、request ID
鉴权上下文OpenService/OpenAppService事务外app/tenant、签名、时间窗、站点API 请求日志(脱敏)合法请求补入 sid/user/source;失败 0 业务写入错误码、上下文快照
参数兼容service/api/*事务外旧字段名/类型/编码无old payload -> canonical DTO;默认值保留旧语义入参映射表、回归样例
领域落库Purchase/Sale/StockOut 等 Service本地事务来源唯一性、状态、数量业务主明细/库存/资金insert/update old -> new;source order 建关系source/internal bill、流水
响应/异步Openapi Controller/MqSer/Providercommit 后业务结果旧 JSON 响应、MQ/下游保持原字段/错误码/编码;异步结果另行跟踪响应快照、message/external request ID

子模块追踪:legacy-openapi 老 OpenAPI 动态分发与幂等

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
动态分发合作方提交 transCodeappId、timestamp、requestId、sourceOrderNoapplication/controllers/Openapi.php -> application/config/apis.php -> application/service/OpenService.php签名/时间窗、code 映射、来源唯一性和请求记录鉴权/适配阶段不写;目标领域本地事务 old -> newrequest ID + transCode + source/internal billNocode/签名失败零业务写入;超时按来源单回查
幂等重放同来源/请求再次调用requestId、sourceOrderNo、transCodeapplication/service/OpenService.php已有映射、主业务和库存/资金副作用已完成 new -> new、新增单据/流水 0old/new request IDs + source/internalrequestId 语义按代码合同;只补响应/异步段

子模块追踪:app-api App API 签名、登录与映射

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
code 映射App 提交 API/trade codecode、signature/token、user/sid、business keyapplication/config/appapis.php -> application/service/OpenAppService.phpcode 类映射、登录上下文、签名和参数路由鉴权不写;目标 API 领域本地事务 old -> newrequest ID + code + user/sid + bill/source未登录/签名/code 不存在零业务写入
兼容回查App 成功/失败与 PC 不一致code、service class、responseapplication/config/appapis.php实际类文件、上下文注入和业务状态查询只读;响应阶段 DB 不变request ID + code/class + response/business row映射改动扫描所有客户端版本;超时先查业务事实

子模块追踪:legacy-saas 老 SAAS 映射与销售建单

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
SAAS 分发老 SAAS code 创建销售等业务saas code、tenant/sid、sourceOrderNoapplication/config/SaasApis.php -> application/service/api/saas/OrderCreate.phptenant 映射、商品客户、来源唯一性和旧字段销售本地事务 insert 主明细/来源关系,none -> createdrequest ID + saas code + source/sale billNo重复来源返回既有单;字段缺失零写入
下游同步销售/出库后回传 SAASsource/internal billNo、eventapplication/Services/Mq/MqSer.php已提交本地事实、旧 payload 合同和发送记录commit 后事务外发 MQ,本地核心 DB 不变both billNos + routing/message result发送失败只补消息;不重复建销售/扣库存

子模块追踪:open-saas 令牌式 OpenSaaS 与 VIP 下单

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
令牌鉴权OpenSaaS API 请求token hash、tenant/sid、path、sourceOrderNoapplication/controllers/OpenSaasApi.php -> application/service/OpenSaasService.phptoken 状态/范围、租户站点、route 和时间鉴权查询只读;失败 0 业务写入request ID + token hash + tenant/sid + pathtoken 不入日志;过期/越权明确错误码
VIP 下单OpenSaaS VIP 创建订单sourceOrderNo、VIP/customer、itemsapplication/controllers/opensaas/Order.phpVIP 资格、商品价库存、来源唯一性和地址订单本地事务 insert 主明细/关系 none -> createdrequest ID + source/internal order + customertimeout 按 sourceOrderNo 回查;重复 0 新单

子模块追踪:point-mall 积分商城映射入口

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
积分请求积分商城 code/path 调用app/source order、user/sid、points/itemsapplication/controllers/PointMall.php -> application/service/OpenPmService.php鉴权、积分/商品合同、来源唯一性和映射适配不写;目标领域本地事务创建订单/关系 none -> createdrequest ID + source/internal + user/sid外部积分扣减与本地订单非原子,timeout 查两端终态
取消补偿建单失败/取消返积分source/internal order、points transactionapplication/service/OpenPmService.php本地订单终态、外部积分扣/返和已有补偿本地补偿事务状态/关系 old -> expected;外部返还事务外both orders + points transaction + result只补失败端;同原交易键返还一次

子模块追踪:allcarpart-proxy 全车件代理与本地能力

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
路由代理allcarpart 显式 URLpath/method、auth、external request/orderapplication/config/routes.php -> application/controllers/allcarpart/Controller.php路由目标、鉴权、代理/本地分支和参数纯代理本地 DB 不写;本地能力走领域事务 old -> newrequest ID + route + external/local ordertimeout 按外部业务键回查;不因代理失败切本地重复执行
本地车管家路由到本地询价/订单能力sourceOrderNo、VIN/SKU、sidapplication/controllers/Carsteward.php来源映射、商品/报价和当前状态本地领域事务 insert/update,保存 external/internal 关系request ID + source/internal + method外部合同需联调;敏感 VIN 脱敏,来源幂等

子模块追踪:base-api BaseApiController 直连接口

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
直接请求URL 直接命中 API Controllermethod/path、headers、sid、business keyapplication/core/BaseService.php -> application/core/BaseController.php内部身份、method、JSON/form、用户站点和权限解析鉴权不写;领域 Service 本地事务 old -> newrequest ID + path/method + sid/billNo与 code 网关协议分开;401/403/校验失败零写入
结果回查直连接口返回异常/超时request ID、business keyapplication/core/BaseService.php应用日志、主业务、异步消息和响应合同查询只读;响应/异步阶段 DB 不变request ID + business row + response/message先查是否 commit;只清理/补偿本次业务段

子模块追踪:compat-response 旧响应、错误码与字段兼容

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
响应适配领域结果返回老客户端API/code/version、result/errorapplication/controllers/Openapi.php -> application/service/OpenService.php旧 success/code/message/data、字段类型/编码和调用方版本响应转换查询/内存操作 不写 DB;保留旧字段默认值request ID + API/version + response snapshotHTTP 200 与业务失败分开;错误码变更需兼容映射
合同回归新旧请求/响应对照fixture ID、old/new payloadapplication/service/api/invoice/StockOut.php参数别名、默认值、数值/字符串和副作用对照测试仅测试数据事务;同输入最终业务事实一致fixture + old/new response + DB diff字段可新增不可破坏旧必填/语义;日志不含敏感 payload

子模块追踪:api-migration 接口迁移、审计与下线

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
调用审计计划从旧 API 迁新入口old code/path、caller/version、time windowapplication/config/apis.php、application/config/appapis.php、application/config/routes.php全部静态映射、访问日志、动态调用方和业务合同审计查询只读 不写;形成迁移矩阵和最后调用时间old/new route + caller/version + request count未证明零调用不下线;环境访问日志必须实际验证
双跑下线灰度切新 API、禁用旧映射migration ID、feature/caller scopeapplication/service/OpenAppService.php新旧响应/副作用、幂等键、失败率和回滚开关目标调用方路由 old -> new;同业务不得双写,旧入口最终明确废弃码migration + caller + old/new metrics/DB diff灰度异常回切;下线后保留可观测错误,不静默路由到错误业务