一句话理解
DGJ2.0 是一个围绕服务站经营的进销存系统:上游承接商品、订单、活动、支付、OA、调拨、TMS 等中心系统,下游给 PC 后台、App、小程序、渠道方和报表平台提供业务能力。
技术形态
- PHP 版本要求:
composer.json中为php >= 7.1 - 框架风格:CodeIgniter 老项目,入口控制器在
application/controllers - 依赖:Guzzle、Monolog、MongoDB、Redis、Elasticsearch 6.7、PhpSpreadsheet、kz/kzmq、league/csv
- 自动加载:
App\\指向application/ - 数据访问:大量模型继承老模型基类,模型目录在
application/models - 服务层:新老并存,
application/Services偏新,application/service偏老
入口层
application/controllers/scm:传统进销存页面入口,包含采购、销售、库存、收付款、任务。application/controllers/po:新采购相关入口,如采购订单详情、购物车、采购退货。application/controllers/sale:销售和出库相关入口。application/controllers/basedata:基础资料管理,如商品、客户、供应商、库存、价格、区域。application/controllers/inner:内部 API,给 OPS、活动、渠道、移动商城等系统使用。application/controllers/app:App 侧控制器。application/controllers/tasks:定时任务和 MQ 消费入口。application/controllers/reports:报表页面和报表接口入口。application/controllers/allcarpart:全车件相关业务入口。
服务层
application/service/scm/InvPoService.php:老采购单列表、详情、关闭、退货展示等逻辑。application/Services/PoOrders/PoOrderSer.php:新采购领域服务,负责订单校验、创建、自制单同步、关闭、支付状态等。application/service/scm/InvSaService.php:老销售出库列表、详情、对账、核销等逻辑。application/Services/SaOrders/SaOrderSer.php:销售订单创建、出库、来源场景转换、库存扣减和下游同步。application/Services/Storage/InventorySer.php:库存流水和实时库存变更。application/Services/Activity/FlashActivitySer.php:OPS 秒杀活动管理。application/Services/Activity/FlashSaleSer.php:服务站侧秒杀商品、购物车、预览、下单、支付状态联动。application/Services/MoveMall:移动商城、智能询价、机器人、补货、马上送。application/Services/Channel:大客户渠道订单、商品、售后。application/Services/Mq/MqSer.php:MQ 生产者封装。application/Services/SyncOrder:销售单和出库单同步到下游。
模型层
常见模型目录和业务含义:
application/models/bs:基础资料,商品、库存、客户、供应商、服务站、品牌、分类、账户。application/models/orders:采购单、销售单、预订单、采购关闭、采购出库、付款信息。application/models/saOrders:销售订单、销售出库单、销售异常、品牌统计。application/models/payment:收付款单、明细、还款单。application/models/activity:活动、秒杀活动、秒杀购物车、秒杀订单、活动商品。application/models/moveMall:移动商城、微仓、机器人会话、关键词、补货退货。application/models/channel:渠道订单、渠道订单明细、渠道售后。application/models/report:报表模型。
配置层
application/config/routes.php:少量显式路由,多数沿 CI 默认 controller/method 路由。application/config/apis.php:老开放接口 code 到 service 文件映射,例如订单状态、出库、收款成功回调。application/config/appapis.php:App 接口 code 到 app service 文件映射,覆盖登录、商品、销售、采购、资金、系统、WMS、盘点。application/config/mq.php:MQ 配置。application/config/redis.php、memcached.php、mongodb.php、elasticsearch.php:缓存和检索配置。application/config/tables.php:大量表常量来源之一。
外部系统边界
- 订单中心:采购正向、售后、取消、自制订单下发、关闭。
- 支付中心:在线支付成功、支付异常、退款、授信还款、白条到期。
- OA:活动、授信或审批类业务的审批结果。
- OPS:活动配置、商品活动、品牌授权、白名单等运营能力。
- 商品中心:物料、结算价、套包、限购、敏感词、销售限价。
- 调拨中心 / TMS:发货、配送、物流状态、第三方运单号。
- SAAS / APP:E站商城、E站维修、订单和出库同步。
- 报表中心 / Hologres:采购、销售、库存、利润等聚合查询。
主链路脑图
基础资料
-> 商品/客户/供应商/仓库/货位/价格
-> 采购、销售、库存、活动共用
采购
-> 草稿 -> 提交 -> 审核/支付 -> 待出库 -> 配送/入库 -> 完成/关闭/退货
-> 影响采购明细、库存、付款、报表、订单中心
销售
-> 开单 -> 待出库 -> 出库 -> 派送 -> 送达 -> 对账 -> 核销
-> 影响库存、收款、客户余额、APP/SAAS 同步
活动/秒杀
-> OPS 建活动 -> OA 审批 -> 服务站选购 -> 生成秒杀单 -> 生成采购单 -> 支付/关闭回写
移动商城/渠道
-> 外部订单/询价/机器人 -> DGJ 销售或采购单 -> 库存/支付/配送 -> 外部系统回调
任务/MQ
-> 支付、OA、订单中心、商品中心、TMS、SAAS 消息
-> 补偿、同步、状态推进、缓存刷新
系统上下游全景
flowchart LR
subgraph Clients[调用端]
PC[PC 后台]
APP[App/PDA/小程序]
OPS[OPS/内部平台]
OPEN[渠道/OpenAPI]
end
subgraph DGJ[DGJ2 单体]
C[Controller/路由]
S[新旧 Service/Provider/Factory]
M[Model/SQL]
CACHE[Redis/ES/Mongo 缓存]
TASK[Tasks/MQ 消费者]
end
subgraph Data[数据结果]
MYSQL[(MySQL 业务表)]
REPORT[(Hologres/报表中心)]
end
subgraph Centers[外部中心]
ITEM[商品中心]
ORDER[订单中心]
PAY[支付中心]
OA[OA]
DISPATCH[调拨/配送/TMS]
SAAS[SAAS/E站/APP]
end
PC --> C
APP --> C
OPS --> C
OPEN --> C
C --> S
S --> M
S <--> CACHE
M --> MYSQL
S --> Centers
Centers --> TASK
TASK --> S
MYSQL --> REPORT
DGJ2 是业务编排和服务站账本,不是所有数据的最终权威:商品、订单审核、支付、OA、配送可能由外部中心主导;采购、销售、库存、收付款和服务站经营快照主要落在 DGJ2。排查前要先判断当前字段的主责系统。
五类请求入口
| 入口 | 路由/解析方式 | 身份上下文 | 典型返回 | 适用场景 |
|---|---|---|---|---|
| PC 页面/接口 | CI 默认 目录/Controller/method,少量 routes.php 显式映射 | 登录 session 中的 jxcsys | splash、splashJson、ajaxData | 采购、销售、库存、财务页面 |
| 内部 API | application/controllers/inner + BaseApiController | 内部 token、Header、Inspire-Api-User | R/Controller handle 包装 | OPS、活动、渠道、移动商城 |
| App API code | appapis.php code -> application/service/api/app | App 协议身份、服务站和用户 | App 统一协议包装 | App、PDA、WMS、盘点 |
| 老开放 API code | apis.php code -> application/service/api | 外部接口签名/上下文 | 老协议包装 | 历史订单、出库、收款回调 |
| CLI/MQ | 直接执行 controllers/tasks/* | MQ payload 或 CLI 参数 | ACK/NACK、日志、JSON 输出 | 回调、定时、同步、补偿 |
显式路由和默认路由
routes.php 只维护少量特殊映射,例如全车件 REST 风格路径。其他大量接口依赖 CodeIgniter 默认规则:URI 片段直接映射 Controller 和方法。因此:
- 看到 Controller 方法不能立即确定公网 URL,还要看 Nginx/OpenResty、入口文件和网关前缀。
- 全车件路由存在 kebab-case 到 Controller
index/$action的二次映射。 - App/OpenAPI 不能只按 Controller 路径寻找,要先从接口 code 找映射值。
- Tasks 是 CLI/MQ 入口,不应暴露给普通前端直接调用。
PC 请求生命周期
sequenceDiagram
participant B as 浏览器
participant N as Nginx/OpenResty
participant I as index.php/CI Router
participant C as BaseController/业务 Controller
participant S as Service/Factory
participant DB as Model/MySQL
participant X as MQ/外部系统
B->>N: GET/POST + session/cookie
N->>I: 转发到 PHP 入口
I->>C: 默认或显式路由到方法
C->>C: 权限检查 + getPageData
C->>C: 注入 JXCSID/JXCUID/JXCUNAME
C->>S: 业务参数 + 登录上下文
S->>DB: 查询/事务/状态更新
opt 跨系统动作
S->>X: HTTP/MQ/Provider
end
S-->>C: 结果或异常
C-->>B: splash/splashJson/ajaxData
getPageData() 实际行为
BaseController::getPageData():
- 合并 GET 和 POST。
- 调用历史参数过滤逻辑。
- 把
page最小归一为 1,rows缺省为 99999。 - 从登录态覆盖注入服务站、用户、区域和所属片区。
| 注入字段 | 来源 | 用途 |
|---|---|---|
JXCSID | $this->jxcsys['sid'] | 服务站数据隔离和分表/查询条件 |
JXCUID | 登录用户 ID | 操作人、审计、权限 |
JXCUNAME | 登录用户名 | 单据制单人和日志 |
areaCode | 服务站区域 | 供应、退货地址、商品规则 |
eparchy | 服务站所属区域 | 物料和采购规则 |
业务 Service 应优先使用服务端注入值,不信任 postData 中同名字段。排查跨站数据问题时,先确认 Controller 是否正确使用 JXCSID,再查 SQL 是否漏了 sid。
内部 API 生命周期
flowchart TD
A[JSON/Form 请求] --> B[BaseApiController 构造]
B --> C[解析 Content-Type 和 raw body]
C --> D[记录 URI/body/header 请求日志]
D --> E{是否内部服务并需要 token}
E -- 是 --> F[校验 inner_token]
F -- 失败 --> X[非法请求]
E -- 否 --> G[读取 Request-Id/Inspire-Api-User/Shipper]
F -- 通过 --> G
G --> H[业务 Controller handle]
H --> I[Request Entry 归一字段]
I --> J[领域 Service]
J --> K[表/缓存/Provider]
K --> L[统一 JSON 返回并记录日志]
BaseApiController 会读取:
Content-Type:JSON 走raw_input_stream,其他内容合并 GET/POST/raw body。Request-Id:请求追踪标识。Inspire-Api-User:URL 编码 JSON 的 OPS 用户信息。Shipper:部分承运/内部业务上下文。inner_token:满足条件时执行内部服务鉴权。
内部 API 日志默认记录请求 body 和 header。新增接口时禁止把密码、token、支付敏感信息原样写入日志,应在入口或 Logger 层脱敏。
新旧代码分层如何理解
| 代码形态 | 常见位置 | 典型作用 | 阅读建议 |
|---|---|---|---|
| 老 Service | application/service | 页面查询、历史业务编排、直接模型操作 | 先找 Controller 装载的 service 名称 |
| 新领域 Service | application/Services | 领域规则、实体校验、跨入口复用 | 优先找 ::service() 和 public 方法 |
| Provider | application/Providers | 远程采购、支付、OA、文件等适配 | 明确远程边界、超时和返回协议 |
| Factory | application/Factory 或领域目录 | 按普通销售/销退/订单类型选择实现 | 查字符串类型对应的具体实现 |
| Entry/DTO | application/Entries | 请求归一、默认值和领域参数 | 确认兼容别名和单位 |
| Model | application/models | 表查询和写入 | 用 tables.php 常量确认物理表 |
| Enum | application/KzData/Enums | 状态、类型、来源、交易类型 | 状态判断和报表过滤的共同证据 |
同一链路可能是“老 Controller -> 老 Service -> 新领域 Service -> Provider”。不要按目录新旧直接判断是否废弃,要用调用方搜索和环境开关确认。
数据访问和事务边界
flowchart LR
C[Controller] --> S[Service]
S --> BM[BaseModel/业务 Model]
BM --> T[(业务主表/明细表)]
S --> R[(Redis 锁/缓存)]
S --> ES[(ES/Mongo)]
S --> P[Provider/外部中心]
T --> MQ[MqSer/SyncOrder]
MQ --> D[下游消费者]
需要特别区分:
- MySQL 事务只能保证同一连接内的表写入,不能自动回滚已成功的 HTTP/MQ 外部动作。
- Redis 锁是短时互斥,不等于业务幂等。
- ES/Mongo/Redis 常是查询或缓存口径,不能用缓存结果替代库存/资金最终校验。
- 报表中心是异步聚合,允许短时延迟,但不允许长期与业务事实冲突。
三类常见响应
| 方法 | 典型结构 | 特点 |
|---|---|---|
resp | {"code":0,"msg":"...","data":{}} | 标准 code/msg/data |
splashJson/splash | 包含 status/message/data 等历史字段 | 可能直接 _display() 并 exit |
ajaxData/直接 die(json_encode()) | 直接输出 Service 返回 | 结构取决于调用 Service |
联调前先确认当前 Controller 使用哪一种返回方法。不要因为 HTTP 200 就判定业务成功,也不要强行要求所有历史接口立即改成同一种结构;统一响应属于跨前端契约改造,需要兼容期和完整调用方清单。
业务链路和主数据依赖
flowchart TD
BASE[服务站/客户/供应商/商品/仓库/货位/价格] --> PO[采购]
BASE --> SA[销售]
BASE --> ACT[活动]
PO --> INV[库存]
SA --> INV
ACT --> PO
PO --> FUND[付款/退款]
SA --> FUND2[收款/对账/核销]
INV --> REPORT[报表/利润/经营分析]
FUND --> REPORT
FUND2 --> REPORT
PO --> SYNC[订单中心/支付/OA/配送/SAAS]
SA --> SYNC
ACT --> SYNC
主数据错误会沿链路扩散:商品状态、仓库货位、客户账户或服务站区域不正确时,采购、销售、库存、支付和报表可能同时异常。排查业务单前先验证主数据快照和当前资料是否一致。
一次问题的定位顺序
- 确认调用端、接口路径/code、环境、服务站和请求时间。
- 根据入口找到 Controller 方法,确认请求解析和权限。
- 记录主业务键:
sid、业务单号、主单 ID、来源类型、来源单号、request ID。 - 沿调用链进入 Service/Provider/Factory,列出状态前置条件。
- 查主表、明细、数量/金额/日志表,不只看页面状态。
- 涉及库存、资金时核对流水事实和实时结果。
- 涉及异步时查生产日志、destination/routing key、消费 ACK/NACK。
- 查自动补偿是否执行;人工处理前确认幂等和影响范围。
- 对比下游和报表,区分实时失败、同步延迟和口径差异。
rg -n "class .*Controller|function 目标方法" application/controllers
rg -n "目标方法|业务单号字段|状态常量" application/service application/Services application/models
rg -n "目标表常量|物理表名" application/config/tables.php application
rg -n "routing key|destination|事件名" application/controllers/tasks application/Services/Mq application/KzData/Enums
系统级改动风险和回归
| 改动位置 | 主要影响 | 最小回归 |
|---|---|---|
BaseController 请求解析/过滤 | 所有 PC 和内部 API | GET/POST/JSON、分页、中文、特殊字符、登录上下文 |
BaseApiController 鉴权/Header | OPS、内部系统、渠道 | 合法/非法 token、Request-Id、用户头、日志脱敏 |
tables.php 常量 | 所有模型读写 | 目标环境表存在、读写权限、历史分表 |
| 公共 Enum | 页面、Service、MQ、报表 | 新旧状态展示、合法迁移、历史数据 |
MqSer/SyncOrderSer | 所有下游系统 | 消息字段、路由、重复消息、失败重试 |
InventorySer | 采购/销售/退货/盘点/调拨 | 每个交易类型正负方向和实时库存 |
| 支付回调 | 采购、活动、账户、退款 | 成功、失败、重复、超时、来源业务 |
证据与待确认
已确认代码证据:index.php、application/config/routes.php、apis.php、appapis.php、tables.php、application/core/BaseController.php、Controller/Service/Model/Enum/Tasks 目录。
仍需按环境确认:
- Nginx/OpenResty 到不同入口目录的真实路由和域名。
- PC session、App 签名、OpenAPI 签名及内部 token 的环境配置。
- MQ、Redis、ES、Mongo、报表中心在开发/预发/生产的连接和降级策略。
- 老/新采购、销售服务的数据源和流量灰度开关。
getThoroughData()当前代码中参数过滤变量使用不一致的历史行为是否在运行路径被覆盖。
这些环境项应记录配置名和验证方式,不能在知识库中写入真实密码、密钥或 token。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| PC 业务请求 | 浏览器经 index.php 进入 CI 路由 | session、sid、表单或 JSON、分页参数 | application/core/BaseController.php 的业务子类 | 对应 application/service 或 application/Services | Model 与 tables.php 表常量 | 订单、库存或基础资料写入业务库 |
| App/内部接口 | appapis.php 中的 API code 或 /inner/* | API code、用户/门店头、签名上下文、业务参数 | BaseApiController、Carsteward、OpenAppService 或 controllers/inner/* | 映射目标 Service | 与 PC 链路共用业务 Service/Model | 同一业务表产生等价事实,入口鉴权不同 |
| 老 OpenAPI | 外部系统按 apis.php API code 调用 | appId、时间戳、签名、外部单号 | Openapi、OpenService | 老接口适配层和领域 Service | 来源单号、sid、内部 billNo | 外部请求转换为 DGJ 内部订单/状态 |
| MQ/回调/任务 | 支付、订单、配送、OA、商品中心或定时调度 | destination、routing key、消息体、外部消息号 | application/controllers/tasks/*Notify.php、*Task.php | MqSer、领域 Service、Provider | 业务单号与消息业务键 | 异步更新状态、数量或下游同步结果 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 |
|---|---|---|---|---|---|
| 网关到 PHP | Web 访问日志、CI 请求日志 | URI、HTTP method、request_id、API code | 2xx 且进入目标 Controller | 4xx/5xx、鉴权失败、路由未命中 | 用 request_id 与应用日志关联 |
| Controller | 目标类和方法、统一异常处理日志 | Controller 方法、sid、billNo/外部单号 | 参数校验通过并调用 Service | ValidateException、业务异常 | 用业务单号进入 Service 和数据库回查 |
| Service/事务 | Service 类方法及异常堆栈 | 完整类名、方法名、sid、事务异常 | commit 后返回业务主键 | rollback、duplicate、库存/状态校验失败 | 用主键和 transType 查主表、明细、流水 |
| 外部调用/MQ | Provider 请求日志、tasks/*Notify 消费日志 | destination、routing key、外部单号、支付/OA 单号 | 外部成功码或消费 ACK | 超时、非成功码、NACK、重复消息 | 用来源单号回到本地关系表和状态字段 |
静态代码只能确认“应在哪个类和方法找”。具体日志文件名、日志平台索引及是否打印成功日志,必须在目标环境确认;没有日志时以数据库事实和下游回执补证,不能伪造日志文本。
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 入口归一 | BaseController、BaseApiController | 事务外 | 请求参数、session/header、站点上下文 | 请求上下文,不直接写业务表 | 原始参数 -> 标准参数;补入 sid/用户上下文 | 请求日志与 Controller 入参 |
| 业务校验 | Validate/Entry/Enum | 通常事务外 | 当前状态、类型、权限、数量 | 无或校验缓存 | 非法请求不应产生业务写入 | 错误码、异常堆栈、主表无新增 |
| 核心落库 | 领域 Service + Model | 本地数据库事务内 | 主表、明细、库存/资金当前值 | 业务主表、明细、关系表 | status: old -> new;数量/金额按动作增减 | 主键、sid、billNo、更新时间、流水 |
| 异步副作用 | MqSer、Provider、Task Consumer | 与本地事务通常不是同一原子边界 | 已提交的业务事实 | MQ、缓存、索引、下游系统 | 本地已成功 -> 下游待处理/已处理 | 消息键、消费日志、下游回执、补偿任务 |
子模块级独立追踪
子模块追踪:pc-request PC 请求生命周期
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 接入解析 | 浏览器经 OpenResty/PHP index.php | request ID、URI/method、session、form/JSON、sid | index.php -> application/config/routes.php -> application/core/BaseController.php -> 业务 Controller | session 用户、站点、权限、请求参数 | 入口阶段不写业务表;原始请求 -> 标准 Controller 参数 | access log + request ID + Controller/method + user/sid | 401/403/404/校验失败零业务写入;先确认环境和路由,不直接改 Service |
| 业务提交 | Controller 调用领域 Service | request ID、sid、业务单号/来源单号 | application/controllers/scm/*.php -> application/service 或 application/Services -> Model | 当前状态、主明细、权限和业务前置 | 本地事务 insert/update/delete,状态/数量/金额 old -> new;commit 后响应 | request ID + business billNo + SQL/影响行数 | rollback 返回业务错误;响应未知按业务键查 DB,禁止盲目重发 |
子模块追踪:inner-api Inner 内部 API 生命周期
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 认证解析 | 内部服务调用 /inner/* | request ID、内部 token/header、sid/user、JSON、source order | application/core/BaseApiController.php -> application/controllers/inner/* | 调用方身份、站点/用户头、接口权限 | 认证阶段不写业务表;header/body -> 标准上下文 | URI + request ID + caller/sid + Controller method | 认证/参数失败零写入;日志脱敏 token,不用生产凭证写文档 |
| 领域处理 | Inner Controller 调 Service/Provider | request ID、source/internal bill、业务 payload | application/controllers/inner/* -> application/Services/* | 来源唯一性、当前状态和业务数据 | 事务内业务事实 old -> new;外部 HTTP/MQ 在 commit 后异步边界 | request ID + source/internal bill + external/message ID | 外部超时先按外部 ID 查询;本地已成只补外部段 |
子模块追踪:app-gateway App tradeCode 网关
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 映射鉴权 | App/车管家 tradeCode 请求 | request ID、tradeCode、签名、用户/设备/sid、params | application/controllers/Carsteward.php -> application/service/OpenAppService.php -> application/config/appapis.php | code 映射、签名/登录上下文和设备规则 | 网关阶段不写业务表;envelope -> 目标 Service 参数 | request ID + tradeCode + user/device + mapped class/method | 无映射/签名失败零写入;先审计缺失映射和兼容响应 |
| 执行业务 | app/pda/service/api 目标方法 | request ID、tradeCode、业务单号 | application/service/api/app/* 或 application/controllers/app|pda/* -> 领域 Service | 当前业务事实和 App 字段口径 | 本地事务 insert/update,业务字段 old -> new;响应保留旧合同 | tradeCode + request ID + billNo | HTTP 200 仍核业务 code;响应未知按业务键回查而非重复调用 |
子模块追踪:openapi 老 OpenAPI 动态分发
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 鉴权分发 | 外部系统 Openapi 请求 | request ID、transCode、appId、timestamp/sign、source order | application/controllers/Openapi.php -> application/service/OpenService.php -> application/config/apis.php | app 授权、签名、交易码映射、Redis/Mongo 幂等状态 | 鉴权阶段业务表不写;requestId 状态无 -> processing | transCode + request ID + appId(hash) + source order | E100xx 零业务写入;processing 超时先查 Mongo/业务事实 |
| 动态执行 | 映射的 service/api/* | request ID、transCode、标准参数、来源单 | application/service/api/* -> 领域 Service/Model | 来源唯一性、状态、数量金额 | 事务内业务 old -> new;Mongo 幂等 processing -> success/fail | request ID + transCode + source/internal bill | 部分成功以业务表为准;补断点并保持旧错误码/响应字段兼容 |
子模块追踪:mq-consumer MQ 回调与消费者
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 分发 | Broker destination/event | destination、routing key、message ID、source bill | application/controllers/tasks/*Notify.php -> event handler | 消息版本、事件类型、当前业务态、幂等键 | 分发阶段通常不写;有效消息进入单消息事务 | destination + event + message ID + stable business key | 未知事件/载荷异常按 Consumer 合同 NACK/告警,不主动 ACK 丢消息 |
| 落库 ACK | handler 调领域 Service | source/internal bill、payment/OA/external ID | Notify Controller -> application/Services/* / Model | 当前状态及是否已达到目标 | 消费事务 old -> new,commit 后 ACK;重复业务键 DB 不变 | 业务键串联 Consumer、表记录和 ACK/NACK | commit 与 ACK 之间不原子;重复投递必须幂等,补偿按业务键不用随机 message ID |
子模块追踪:cli-task CLI 与定时任务
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 调度扫描 | crontab/调度平台执行 CLI | task/method、env、batch、time window、cursor/limit | application/controllers/tasks/*Task.php -> 任务 Service | 候选状态、更新时间、checkpoint 和环境开关 | 扫描只读;候选集合由查询 -> 固定批次 | task + batch + start/end + scanned count | 禁止 Web 运行、无边界全表扫描;异常保留 cursor 续跑 |
| 单条执行 | 任务对每个 business ID | batch、business ID、稳定幂等键 | Task -> application/Services/* / MqSer | before 状态和外部最终事实 | 每条事务 old -> expected 或重发消息;已完成 DB 不变 | batch + ID + before/after + success/skip/fail | 小批重跑;部分失败只重试失败 ID,禁止把成功项再次扣加 |
子模块追踪:transaction-data 统一事务与数据访问边界
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 本地事务 | 领域 Service 开始写业务事实 | request ID、sid、billNo、当前版本/status | application/Services/* / application/service/* -> Model/BaseModel | 主表、明细、流水、行锁和分片键 | 同连接事务内 insert/update/delete,主明细/库存/资金 old -> new;异常 rollback | request ID + billNo + transaction exception + affected rows | 确认所有 Model 是否同连接;跨连接写不能假装原子,需独立补偿 |
| 事务外副作用 | commit 后 Provider/MQ/cache/index/report | billNo、external request/message ID、cache key | application/Providers/BaseProvider.php、application/Services/Mq/MqSer.php、Cache Service | 已提交本地事实 | 本地 DB 不变;外部/缓存/索引 old -> new,最终一致 | billNo + external/message ID + cache/index version | 外部成功本地失败/反向情况分段处理;只补失败副作用,不重做主事务 |