本手册覆盖 DGJ2.0 秒杀/折扣商品专区从 OPS 创建、商品导入、冲突校验、服务站范围、OA 审批,到站管家列表、购物车、确认页、下单锁量、采购单、支付回调、关闭释放和定时补偿的完整链路。
适用于需求交接、前后端联调、预发数据准备、线上问题定位和补偿评审。本文以当前代码和已确认产品规则为依据;环境中的 OA、库存中心、支付中心、MQ 和定时调度配置仍需上线前实测确认。
1. 业务目标
- 让运营能配置单品/套包活动、价格、效期、仓库和服务站范围。
- 用 OA 审批保证活动上线前经过审核,审批结果可幂等回写。
- 服务站只看到当前时间、范围、价格、库存都有效的商品。
- 单品通过购物车结算,套包直接购买。
- 下单时原子锁定活动数量,支付后把锁定量转为已用量。
- 秒杀订单通过采购订单
30-Cxx-77进入原采购履约链。 - 取消、超时、采购关闭和行关闭时只释放应释放的差量。
- 定时任务只补偿可证明的缺口,不重复生成采购单、不重复释放数量。
2. 业务边界和参与系统
flowchart LR
OPS["OPS运营"] --> ACT["DGJ活动域"]
ACT --> OA["OA审批"]
ACT --> ST["站管家/移动商城"]
ST --> STOCK["库存中心/实时库存"]
ST --> ORDER["秒杀订单"]
ORDER --> PO["DGJ采购单"]
PO --> ODC["订单中心"]
PO --> PAY["支付中心"]
PO --> DIS["配送/入库"]
PAY --> ORDER
ODC --> ORDER
ORDER --> TASK["超时/补偿任务"]
| 系统/模块 | 负责 | 不负责 |
|---|---|---|
| OPS | 活动配置、商品、范围、提交审批、结束 | 服务站实时库存最终值 |
| OA | 审批流和结果 | 商品库存、订单支付 |
| 活动域 | 可见性、价格规则、数量账 | 采购履约明细状态 |
| 库存中心 | 指定仓/服务站仓可用量 | 活动 locked_qty/used_qty |
| 秒杀订单 | 用户购买快照和数量锁定 | 最终采购出入库 |
| 采购域 | 待支付、审核、配送、入库、关闭 | 活动配置冲突 |
| 支付中心 | 支付结果和退款回调 | 活动商品可见性 |
| 补偿任务 | 修复已知状态缺口 | 无条件覆盖人工状态 |
3. 适用现象和首查方向
| 现象 | 首查 | 常见原因 |
|---|---|---|
| OPS 保存失败 | 活动/商品参数校验 | 第三方商品、效期缺失、套包数量/仓库错误 |
| 导入成功但活动没商品 | 导入与保存边界 | 导入只解析,前端未带完整 goodsList 保存 |
| 提交审批冲突 | 跨活动冲突维度 | 时间重叠、单品效期重叠、套包同仓重叠 |
| OA 通过后仍审批中 | OA 号和模板回调 | summaryId/oa_no 不匹配、回调未消费 |
| OA 报字段不存在 | OA payload | 错把审批人工号放进表单字段 |
| 单品所有站都看不到 | 活动/商品/价格/库存 | 未启用、无价、库存为 0、商品失效 |
| 套包指定站看不到 | 白名单 | 未配置或保存时被全量替换 |
| 列表有库存,加购失败 | 库存明细去重和实时校验 | 列表重复累加、仓库/效期口径不同 |
| 加购成功,确认页失效 | 二次校验 | 活动跨时点结束、价格/库存变化 |
| 提交订单提示限购 | 单品/套包规则 | 套包已购 1 次、锁量未释放 |
下单后 locked_qty 不对 | 数量账 | 重复提交、事务部分成功、明细关联错误 |
| 支付成功仍待支付 | 支付回调和采购关系 | 回调未到、PO 来源关联缺失 |
已支付但 used_qty 未增 | markPaidByPoOrder | 重复/漏回调、秒杀订单未找到 |
| 超时后仍占量 | expireWaitPayOrders | 任务未调度、采购关闭失败、状态不满足 |
| 采购关闭后不能再买 | 关闭释放 | used_qty/locked_qty 未差量释放 |
4. 入口与代码地图
4.1 OPS 入口
| 能力 | Controller 方法 | Service |
|---|---|---|
| 活动列表 | listFlashActivity | FlashActivitySer |
| 活动详情 | detailFlashActivity | 同上 |
| 商品列表 | listFlashActivityGoods | 同上 |
| 保存活动 | saveFlashActivity | 同上 |
| 单行失效 | invalidateFlashActivityGoods | 同上 |
| 导入商品 | importFlashActivityGoods | 同上 |
| 下载模板 | downloadFlashActivityGoodsTemplate | 同上 |
| 导出活动/商品 | exportFlashActivity* | 同上 |
| 复制活动 | copyFlashActivity | 同上 |
| 保存黑白名单 | saveFlashActivityStationScope | 同上 |
| 查询/删除名单 | get/deleteFlashActivityStationScope | 同上 |
| 提前结束 | endFlashActivity | 同上 |
| 转草稿 | convertFlashActivityToDraft | 同上 |
| 提交审批 | submitFlashActivity | 同上 |
文件:application/controllers/inner/activity/FlashActivity.php。
4.2 站管家/移动商城入口
PC:application/controllers/scm/FlashSale.php。
Inner 移动端:application/controllers/inner/moveMall/FlashSale.php。
两套入口调用同一 FlashSaleSer:
| 方法 | 用途 |
|---|---|
checkStationAccess | 是否显示折扣商品专区入口 |
goodsList/goodsDetail | 商品列表/详情 |
packageGoodsList | 套包明细 |
goodsFilters/cartFilters | 筛选项 |
getCartCount | 购物车数量 |
addCart/updateCart/cartList | 单品购物车 |
orderPreview | 下单前快照校验 |
submitOrder | 创建订单、锁量和采购单 |
orderDetail | 秒杀订单详情 |
cancelOrder | 主动取消和释放 |
4.3 回调和任务
| 入口 | 文件 | 作用 |
|---|---|---|
| OA 消息 | tasks/OaNotify.php | 模板 callback_20260526 分流 |
| 支付中心 | tasks/PayCenterNotify.php | 支付成功后标记秒杀已支付 |
| 秒杀任务 | tasks/FlashSaleTask.php | 状态刷新、超时和补偿 |
| 采购关闭 | FlashSaleSer::releaseByPoOrder/releaseByPoCloseItems | 释放活动数量 |
5. 核心表和关联键
| 表 | 作用 | 主要键 |
|---|---|---|
t_flash_activity | 活动主表 | id、oa_no、时间、类型、状态 |
t_flash_activity_goods | 活动商品规则 | id、activity_id、inv_id/sku_id |
t_flash_activity_station_scope | 服务站黑白名单 | activity_id + sid + type |
t_flash_activity_province_scope | 省区自动加入规则 | activity_id + depart_code + operate_province |
t_flash_cart | 单品购物车 | sid + activity_goods_id |
t_flash_order | 秒杀订单主表 | id、bill_no、sid、状态 |
t_flash_order_detail | 秒杀订单快照 | order_id、activity_goods_id |
t_flash_activity_log | 活动操作日志 | activity_id、动作、操作者 |
t_scm_po_order | 采购主单 | srcOrderType=13 + srcOrderId/srcOrderNo |
t_scm_po_order_info_{sid%32} | 采购明细 | 秒杀订单明细来源关系 |
t_scm_po_order_payinfo | 采购支付关系 | billNo/orderId |
| Payment 相关 32 分片表 | 支付事实 | 采购单和支付单关联 |
erDiagram
FLASH_ACTIVITY ||--o{ FLASH_ACTIVITY_GOODS : contains
FLASH_ACTIVITY ||--o{ FLASH_ACTIVITY_STATION_SCOPE : scopes
FLASH_ACTIVITY ||--o{ FLASH_ACTIVITY_PROVINCE_SCOPE : auto_scope
FLASH_ACTIVITY_GOODS ||--o{ FLASH_CART : cart
FLASH_ORDER ||--o{ FLASH_ORDER_DETAIL : contains
FLASH_ACTIVITY_GOODS ||--o{ FLASH_ORDER_DETAIL : snapshots
FLASH_ORDER ||--o{ SCM_PO_ORDER : creates
SCM_PO_ORDER ||--o{ SCM_PO_ORDER_INFO : contains
5.1 关联采购的稳定条件
po.orderType = 30-Cxx-77
po.srcOrderType = 13
po.srcOrderId = flash_order.id
po.srcOrderNo = flash_order.bill_no
po.sid = flash_order.sid
6. 状态字典
6.1 活动类型和库存范围
| 字段 | 值 | 含义 |
|---|---|---|
activity_type | 1 | 单品售卖 |
activity_type | 2 | 套包售卖 |
stock_scope | 1 | 服务站对应仓 |
stock_scope | 2 | 指定仓 |
stock_scope | 3 | 全局预留 |
6.2 OA 申请状态
| 值 | 含义 |
|---|---|
| 0 | 草稿 |
| 1 | 审批中 |
| 2 | 审批通过 |
| 3 | 审批驳回 |
6.3 设置和页面状态
| 状态维度 | 值 | 含义 |
|---|---|---|
| 设置状态 | 0 | 草稿 |
| 设置状态 | 1 | 启用 |
| 设置状态 | 2 | 提前结束 |
| 页面状态 | 1 | 未开始 |
| 页面状态 | 2 | 进行中 |
| 页面状态 | 3 | 已结束 |
页面状态由设置状态和当前时间推导,不能把“审批通过”“启用”“进行中”当作同一个字段。
6.4 活动商品状态
| 值 | 含义 |
|---|---|
| 0 | 待开始 |
| 1 | 进行中 |
| 2 | 已结束 |
| 3 | 已失效 |
6.5 订单状态
| 值 | 含义 | 数量账 |
|---|---|---|
| 0 | 已创建 | 事务中间态,需继续确认 |
| 1 | 待支付 | 占用 locked_qty |
| 2 | 已支付 | 占用转入 used_qty |
| 3 | 已取消 | 释放待支付锁量 |
| 4 | 已关闭 | 按采购关闭事实释放 |
stateDiagram-v2
[*] --> 已创建
已创建 --> 待支付: 创建采购待支付单
待支付 --> 已支付: 支付成功回调
待支付 --> 已取消: 主动取消
待支付 --> 已关闭: 超时/采购关闭
已支付 --> 已关闭: 采购关闭或明细关闭
已支付 --> [*]: 正常履约
已取消 --> [*]
已关闭 --> [*]
7. OPS 保存规则
7.1 完整快照语义
saveFlashActivity 是活动和商品的完整快照保存:
- 前端每次传完整活动信息。
goodsList存在时表示完整商品集合,不是增量。- 新增、编辑、删除未正式生效商品都通过该快照完成。
- 导入接口只解析/补充商品行,最终仍需调用保存接口落库。
- 人工让已生效商品失效必须调用独立
invalidateFlashActivityGoods。
7.2 脱敏保存请求
{
"id": 1001,
"name": "夏季保养折扣专区",
"activity_type": 1,
"stock_scope": 1,
"start_time": "2026-07-20 00:00:00",
"end_time": "2026-07-31 23:59:59",
"goodsList": [
{
"goods_id": 30001,
"price_mode": 1,
"fixed_price": "88.00",
"date_calc_mode": 2,
"date_from": 0,
"date_to": 90
}
]
}
站点、用户和权限来自内部网关上下文,不应把鉴权信息写进文档示例。
7.3 商品档案校验
goods_id来自复用商品搜索接口。- 后端按
goods_id反查并保存inv_id/sku_id快照。 sku_id为空表示第三方商品,禁止加入秒杀。- 新增/导入还要校验限售档案。
- 保存只拦截真正新增行;活动提交前对全部商品再次兜底。
7.4 单品和套包数量
| 类型 | qty 规则 | 含义 |
|---|---|---|
| 单品 | 后端统一存 0 | 不限制活动总数量,购买看实时库存 |
| 套包 | 必须大于 0 | 套包活动可售总包数 |
单品的 0 是存储哨兵,不是“售罄”。详情、列表导出和模板回显应展示为空。
7.5 套包指定仓
stock_scope = 2时每个套包商品行必须有wms_location_no。wms_location_name是保存时仓库名称快照,详情优先读快照。- 历史名称为空可一次性回填,不能覆盖已有快照。
- 下单创建秒杀采购单时
header.multiWarehouseFlag=true。 - 该标记只能在
srcOrderType=13且活动类型=套包、库存范围=指定仓时透传。
8. 效期规则
8.1 需要效期的物料属性
wmsAttributeCode 为 2/3/19/20 时:
date_calc_mode必须是按月或按天。- 倒推起止值必须提供,允许 0。
- 按月 0 表示当月,日期通常从月初计算。
普通物料允许不配置,也允许显式配置,后端应保留配置。
8.2 不保存绝对生产日期
活动商品表保存倒推规则,不保存 prod_date_start/prod_date_end;服务站列表和库存请求在运行时根据当前日期计算区间。
flowchart LR
CFG["date_calc_mode/from/to"] --> NOW["当前业务日期"]
NOW --> RANGE["计算生产日期区间"]
RANGE --> STOCK["库存中心查询"]
RANGE --> CONFLICT["活动冲突比较"]
9. 跨活动冲突规则
只有活动时间重叠的当前/未来已审批活动进入冲突比较。
| 当前类型 | 对方类型 | 冲突维度 | 结论 |
|---|---|---|---|
| 单品 | 套包 | 不比较 | 允许共存 |
| 单品 | 单品 | 物料 + 效期区间 | 重复或交叉即冲突 |
| 套包 | 套包 | 物料 + 效期区间 + 仓库 | 同仓冲突,不同仓可共存 |
补充规则:
package_group_no不是冲突维度。- 单品仓库不是冲突维度。
- 保存、导入和提交审批必须复用同一比较器。
- 冲突是硬失败,不支持
ignore_conflict或二次确认绕过。 - 失败提交不得修改当前活动状态,也不得自动结束旧活动商品。
flowchart TD
A["提交活动"] --> T{"活动时间重叠?"}
T -->|否| PASS["无冲突"]
T -->|是| TYPE{"活动类型相同?"}
TYPE -->|否| PASS
TYPE -->|单品| S{"物料+效期重复/交叉?"}
TYPE -->|套包| P{"物料+效期+仓库重复/交叉?"}
S -->|是| FAIL["硬失败,can_submit=0"]
P -->|同仓是| FAIL
S -->|否| PASS
P -->|不同仓/否| PASS
10. 服务站范围
10.1 单品黑名单
- 可配置多个服务站。
- 未配置时默认所有符合其他条件的服务站可见。
- 黑名单中的服务站不可见。
- 支持按
depart_code + operate_province对新站自动加入黑名单。
10.2 套包白名单
- 未配置白名单时不展示。
- 只有白名单服务站可见。
- 当前最多一个服务站。
- 不使用省区自动加入能力。
10.3 保存是全量替换
saveFlashActivityStationScope.stations 表示弹窗最终名单:
- 未出现的旧记录软删。
- 空数组表示清空。
- 历史记录可恢复。
- 套包最多一个站只检查本次最终名单,不与旧名单取并集。
flowchart TD
A["活动类型"] --> B{"单品?"}
B -->|是| BL["读取黑名单"]
BL --> V1{"当前sid在黑名单?"}
V1 -->|是| HIDE["不可见"]
V1 -->|否| SHOW["继续其他校验"]
B -->|否| WL["读取套包白名单"]
WL --> V2{"当前sid是唯一白名单站?"}
V2 -->|是| SHOW
V2 -->|否| HIDE
11. OA 提交和回调
11.1 提交前置条件
- 活动和有效商品存在。
- 商品规则、价格、效期、限售、仓库、套包数量通过。
- 跨活动冲突为 0。
- 套包白名单和价格兜底校验通过。
- 直接发布
is_oa=0也要有oa_no,最长 64。
11.2 OA 模板
FlashActivityEnums::OA_TEMPLATE_CODE = callback_20260526
固定审批人属于 OA 生产流程节点配置,不是 DGJ 表单字段。公开文档不记录具体员工编号;不要在活动 OA payload 的 data 中添加“审批人/审批人工号”,否则模板会报字段不存在。
11.3 回调关联
OA 回调由 OaNotify 按模板分流到 FlashActivitySer::callback20260526(),通常用回调 summaryId 匹配活动 oa_no。
sequenceDiagram
participant O as OPS
participant A as FlashActivitySer
participant OA as OA
participant Q as OA MQ
O->>A: submitFlashActivity
A->>A: 全量校验/冲突检查
A->>OA: 提交 callback_20260526
OA-->>A: oa_no/summary
A->>A: apply_status=1
OA->>Q: 审批结果
Q->>A: callback20260526
A->>A: 幂等更新通过/驳回
11.4 OA 排查
- 查活动
oa_no/apply_status/enable_status。 - 查提交请求和 OA 返回。
- 查 template code 是否正确。
- 查消息是否到
OaNotify。 - 查
summaryId是否匹配。 - 查重复回调是否幂等。
- 查回调异常是否被 ACK 丢弃。
12. 服务站商品可见性
商品可见至少满足:
活动未删除
apply_status = 审批通过
enable_status = 启用
start_time <= now <= end_time
活动商品状态可售且未删除
服务站命中黑/白名单规则
价格可解析且大于0
库存/活动可售量大于0
商品档案仍允许销售
flowchart TD
A["goodsList"] --> TIME["审批/启用/时间"]
TIME --> SCOPE["服务站范围"]
SCOPE --> GOODS["商品状态/档案"]
GOODS --> PRICE["结算价格"]
PRICE --> STOCK["库存/活动剩余量"]
STOCK --> FORMAT["单品行或套包卡片"]
12.1 操作主键
前端所有商品动作使用 activity_goods_id 或 activity_goods_ids。不能只传 inv_id,因为同一物料可能因活动、效期、仓库或价格规则形成多条活动商品行。
13. 价格链
13.1 一口价
settlement_price = fixed_price
不得调用普通采购价或调整后结算价覆盖。
13.2 固定折扣
优先:当前服务站普通采购可买商品价 findAvailableItemList
兜底:确认活动选品限制 -> 001货主调整后结算价 findItemGlobalPrice
最终:base_price * discount_rate
普通采购不可买或价格为 0 时,必须先按当前 sid + inv_id/sku_id 精确确认物料满足活动选品核心限制,才可走 001 兜底。
13.3 无价格处理
- 单品不在站管家列表展示。
- 套包保存和提交时对全部白名单站校验并报错。
- 购物车、确认页、下单再次拒绝。
- 不能静默生成 0 元采购单。
flowchart TD
A["价格模式"] --> F{"一口价?"}
F -->|是| FP["fixed_price"]
F -->|否| NORMAL["普通采购可买价"]
NORMAL --> OK{"价格>0?"}
OK -->|是| DISC["乘固定折扣"]
OK -->|否| ELIGIBLE["精确校验活动选品限制"]
ELIGIBLE -->|通过| GLOBAL["001调整后结算价"]
ELIGIBLE -->|失败| REJECT["无价/不可售"]
GLOBAL --> DISC
14. 库存计算
14.1 单品
单品 qty=0 不限制活动数量:
单品可售量 = 满足仓库/效期规则的实时库存可用量
仍需扣除库存中心定义的占用/不可用明细;活动 locked_qty/used_qty 用于订单生命周期审计,但不能把 qty=0 - locked - used 当作可售量。
14.2 套包
活动剩余包数 = qty - locked_qty - used_qty
仓库可组成包数 = min(每个子件可用量 / 单包用量)
最终可购包数 = min(活动剩余包数, 仓库可组成包数)
套包正式购买次数限制:
PACKAGE_PURCHASE_LIMIT = 1
14.3 同 SKU 多效期去重
库存中心批量查询同 SKU 多生产日期范围时可能返回相同明细行。按 skuCode + companyCode 归桶前必须对完全相同明细去重,否则列表会重复累加,而加购使用另一口径后提示库存不足。
flowchart LR
REQ["SKU+多个效期请求"] --> RESP["库存中心明细"]
RESP --> DEDUP["相同明细去重"]
DEDUP --> BUCKET["按SKU+货主+仓库归桶"]
BUCKET --> SUM["汇总allowQty"]
SUM --> LIST["列表"]
SUM --> CART["加购"]
SUM --> ORDER["下单"]
15. 购物车
15.1 规则
- 只有单品进入购物车;套包直接购买。
- 购物车用
activity_goods_id定位规则。 updateCart统一处理数量、勾选、删除和批量删除。- 不新增单独
validateCart;预览和提交都重新校验。 - 今日新增默认勾选,历史商品默认不勾选。
- 单品可跨多个一口价活动一起结算。
- 跨活动订单主表
activity_id=0,明细保存真实活动 ID。
15.2 加购请求
{
"activity_goods_id": 50001,
"qty": 2
}
sid 从登录服务站上下文注入,不由前端传。
15.3 加购校验
- 活动当前可售。
- 商品行可售且未人工失效。
- 当前站不在黑名单。
- 价格有效。
- 实时库存和效期库存足够。
- 数量大于 0 且符合限购。
- 同活动商品购物车记录幂等更新。
16. 订单预览
orderPreview 不是只读回显,它是提交前的完整重算:
- 重新读取活动和商品。
- 重新验证服务站范围。
- 重新取结算价。
- 重新查询库存。
- 过滤失效购物车行。
- 计算收货仓、供应商、总数量和总金额。
- 套包按
package_group_no展开全部子件。
sequenceDiagram
participant U as 站管家
participant S as FlashSaleSer
participant A as 活动库
participant P as 价格服务
participant I as 库存中心
U->>S: orderPreview(ids/立即购买)
S->>A: 查询活动商品快照
S->>S: 可见性/限购校验
S->>P: 解析当前价格
S->>I: 查询仓库效期库存
S-->>U: 当前可提交快照
17. 提交订单和原子锁量
17.1 脱敏请求
单品购物车:
{
"source": 2,
"activity_goods_ids": [50001, 50002],
"payment_type": 1
}
套包立即购买:
{
"source": 1,
"activity_goods_id": 60001,
"qty": 1,
"payment_type": 1
}
17.2 提交流程
sequenceDiagram
participant U as 站管家
participant F as FlashSaleSer
participant AG as activity_goods
participant FO as flash_order
participant PO as 采购域
U->>F: submitOrder
F->>F: 重做预览全部校验
F->>AG: 条件更新 locked_qty
AG-->>F: 影响行数必须符合预期
F->>FO: 写订单主表/明细快照
F->>PO: 创建30-Cxx-77采购单
PO-->>F: 返回采购单号/ID
F->>FO: 订单进入待支付
F-->>U: 秒杀单号和采购单关系
17.3 锁量原则
- 锁量更新必须带剩余量条件,不能先查后无条件加。
- 每条订单明细保留
activity_goods_id/activity_id/qty/price/warehouse快照。 - 任一商品锁量失败,整单应回滚已锁商品。
- 客户端超时重试要按秒杀订单业务键防重复。
- 采购单创建失败必须释放本次锁量或整体回滚。
17.4 多供应商/多采购单
一张秒杀订单可能按货主/供应商拆成多张采购单。支付和关闭补偿不能假设一对一,需按所有关联采购单判断整体状态。
18. 转采购单
18.1 固定来源
| 字段 | 值 |
|---|---|
orderType | 30-Cxx-77 |
srcOrderType | 13 |
srcOrderId | 秒杀订单 ID |
srcOrderNo | 秒杀订单号 |
paymentType | 在线支付为 1 |
前端展示文案可叫“折扣订单”,内部类型编码和来源关系不能改。
18.2 仓库透传
- 单品不传指定仓。
- 套包指定仓按每行
wms_location_no传库存/采购上下文。 - 多仓套包设置
multiWarehouseFlag=true。 - 创建采购单解析不到普通货主,但活动商品已有有效价格且满足选品限制时,货主可按
001兜底。
18.3 采购排查 SQL
select id, sid, billNo, billStatus, orderType, srcOrderType,
srcOrderId, srcOrderNo, paymentType, amount, totalQty,
createTime, modifyTime
from t_scm_po_order
where sid = :sid
and orderType = '30-Cxx-77'
and srcOrderType = 13
and (srcOrderId = :flash_order_id or srcOrderNo = :flash_bill_no);
采购明细按 sid % 32 查 t_scm_po_order_info_{suffix}。
19. 支付链
19.1 支付约束
- 不支持授信支付。
- 支持待退款账户和现金类在线支付方式,具体可用项以支付接口返回为准。
- 支付入口复用采购
getPayInfoNew/noPagePay。 - 支付中心成功回调要求
payStatus = '02'。
19.2 首次成功回调
PayCenterNotify 完成采购支付落账后,对秒杀来源采购单调用:
FlashSaleSer::markPaidByPoOrder(poOrderInfo, payTime)
数量账变化:
locked_qty -= order_detail.qty
used_qty += order_detail.qty
flash_order.status: 1 -> 2
19.3 幂等要求
- 秒杀订单已支付时重复回调不得再次转量。
- 活动商品更新需保证
locked_qty >= qty。 - 支付关系按采购单和支付业务号唯一。
- 多张采购单时避免第一张成功就错误标记整张秒杀订单全部支付。
sequenceDiagram
participant PAY as 支付中心
participant N as PayCenterNotify
participant PO as 采购单
participant F as FlashSaleSer
participant G as 活动商品
PAY->>N: payStatus=02
N->>PO: 支付落账/推进状态
N->>F: markPaidByPoOrder
F->>F: 锁秒杀订单并检查幂等
F->>G: locked减少 used增加
F->>F: 订单状态=已支付
20. 数量账
20.1 三个核心量
| 字段 | 含义 |
|---|---|
qty | 套包活动总包数;单品固定 0 哨兵 |
locked_qty | 待支付订单占用 |
used_qty | 已支付使用量 |
20.2 派生量
ordered_qty = locked_qty + used_qty
套包 available_qty = max(0, qty - locked_qty - used_qty)
单品 available_qty = 满足仓库/效期的实时库存
20.3 生命周期变化
| 动作 | locked_qty | used_qty |
|---|---|---|
| 提交待支付订单 | +q | 不变 |
| 支付成功 | -q | +q |
| 待支付取消/超时 | -q | 不变 |
| 已支付采购关闭 | 不变 | 按关闭量 -q |
| 部分行关闭 | 按原订单状态释放对应字段 | 差量释放 |
flowchart LR
FREE["可售"] -->|"提交 +q"| LOCK["locked_qty"]
LOCK -->|"支付 -q/+q"| USED["used_qty"]
LOCK -->|"取消/超时 -q"| FREE
USED -->|"采购关闭 -q"| FREE
21. 主动取消、超时和关闭
21.1 主动取消
仅待支付订单可直接取消:
- 锁秒杀订单。
- 检查状态仍为 1。
- 关闭/取消关联采购待支付单。
- 按订单明细减少
locked_qty。 - 秒杀订单改为 3。
- 购物车是否恢复由产品交互决定,不自动假设。
21.2 待支付超时
expireWaitPayOrders(limit) 扫描过期状态 1 订单。优先关闭关联采购单;没有有效采购关系时直接释放锁量。必须防止支付回调和超时任务并发。
21.3 采购关闭
- 整单关闭调用
releaseByPoOrder()。 - 行级关闭调用
releaseByPoCloseItems()。 - 待支付来源释放
locked_qty。 - 已支付来源释放
used_qty。 - 部分关闭只释放新增关闭差量,不能重复释放历史已关闭量。
flowchart TD
C["采购关闭事件"] --> FIND["按srcOrderType=13找秒杀订单"]
FIND --> STATUS{"秒杀订单状态"}
STATUS -->|待支付| L["释放locked_qty"]
STATUS -->|已支付| U["释放used_qty"]
L --> DELTA["按整单/行关闭差量"]
U --> DELTA
DELTA --> CLOSE["必要时订单状态=已关闭"]
22. 补偿任务地图
| 命令 | Service | 处理 |
|---|---|---|
expireWaitPayOrders | FlashSaleSer | 关闭过期待支付并释放 |
refreshGoodsStatus | FlashActivitySer | 按时间刷新商品状态 |
refreshInvalidCarts | FlashSaleSer | 失效不可售购物车 |
compensatePaidOrders | FlashSaleSer | 采购已支付但秒杀未标记支付 |
compensateClosedOrders | FlashSaleSer | 采购已关闭但秒杀未释放 |
runCompensate | Controller 汇总 | 顺序执行以上任务 |
22.1 CLI 示例
php index.php tasks/FlashSaleTask/refreshGoodsStatus 500
php index.php tasks/FlashSaleTask/refreshInvalidCarts 500
php index.php tasks/FlashSaleTask/expireWaitPayOrders 100
php index.php tasks/FlashSaleTask/compensatePaidOrders 100
php index.php tasks/FlashSaleTask/compensateClosedOrders 100
php index.php tasks/FlashSaleTask/runCompensate 100
22.2 补偿原则
- 只扫描明确状态集合和有限
limit。 - 每单加数据库锁或条件更新。
- 以采购/支付真实事实为依据。
- 先查是否已经转量/释放。
- 记录处理、跳过、失败数量。
- 可重复运行,第二次结果应主要为跳过/0 变更。
23. 购物车失效
购物车在以下情况应失效:
- 活动结束或提前结束。
- 活动/商品被删除或人工失效。
- 活动未审批通过或不再启用。
- 当前站点不再可见。
- 价格无法解析。
- 商品不再满足销售条件。
库存短时不足是否永久失效要遵循当前代码;通常预览/提交实时提示,不应把可恢复库存不足等同于永久删除购物车。
24. 导入、导出和模板
24.1 导入边界
- 模板第一列是“物料ID”。
- 导入按物料 ID 反查档案并补
inv_id/sku_id。 - 第三方物料报错。
- 运行中活动不允许导入。
- 多行错误一起返回,限售错误带物料编码。
- 导入结果回填页面,最终仍由
saveFlashActivity保存完整快照。
24.2 单品数量显示
单品数据库 qty=0,但以下输出应为空:
- 活动详情。
- 活动商品导出。
- 活动列表导出行。
- 导入后回显。
避免用户把导出的 0 再导入时理解成显式促销数量。
24.3 仓库名称
导出/详情优先读 wms_location_name 快照,不要每次为显示调用 OPS 仓库列表接口。
25. 常用只读 SQL
25.1 活动全景
select id, name, activity_type, stock_scope,
apply_status, enable_status, activity_status,
oa_no, start_time, end_time, total_qty, total_amount,
create_time, modify_time, is_deleted
from t_flash_activity
where id = :activity_id;
select id, activity_id, package_group_no, inv_id, sku_id,
qty, price_mode, fixed_price, discount_rate,
date_calc_mode, date_from, date_to,
wms_location_no, wms_location_name,
goods_status, locked_qty, used_qty, ever_effective, is_deleted
from t_flash_activity_goods
where activity_id = :activity_id
order by package_group_no, id;
25.2 服务站范围
select activity_id, sid, type, is_deleted, create_time, modify_time
from t_flash_activity_station_scope
where activity_id = :activity_id
order by type, sid;
select activity_id, depart_code, operate_province, is_deleted
from t_flash_activity_province_scope
where activity_id = :activity_id;
25.3 购物车和订单
select *
from t_flash_cart
where sid = :sid and activity_goods_id in (:activity_goods_ids)
order by modify_time desc;
select id, bill_no, sid, activity_id, source, status,
total_qty, total_amount, expire_time, pay_time,
create_time, modify_time
from t_flash_order
where sid = :sid and (id = :order_id or bill_no = :flash_bill_no);
select order_id, bill_no, sid, activity_id, activity_goods_id,
package_group_no, inv_id, sku_id, qty, price, amount,
wms_location_no
from t_flash_order_detail
where order_id = :order_id
order by id;
25.4 数量异常候选
select id, activity_id, inv_id, sku_id, qty, locked_qty, used_qty,
qty - locked_qty - used_qty as package_remaining
from t_flash_activity_goods
where is_deleted = 0
and (locked_qty < 0 or used_qty < 0
or (qty > 0 and locked_qty + used_qty > qty));
26. 按现象排查 SOP
26.1 OA 通过但活动不展示
- 查
apply_status=2。 - 查
enable_status=1。 - 查当前时间在窗口内。
- 查商品状态和人工失效。
- 查服务站黑/白名单。
- 查价格是否有效。
- 查库存和效期范围。
- 查列表筛选条件是否把商品过滤。
26.2 列表有库存但加购失败
- 固定
activity_goods_id/sid。 - 查列表返回的仓库、效期、库存明细。
- 查是否同 SKU 多效期重复累加。
- 用加购相同参数重查库存。
- 查价格是否在两次请求间变化。
- 查活动/商品是否跨时间边界。
- 查单品限购或已有购物车数量。
26.3 提交订单后锁量异常
- 查秒杀订单和明细是否唯一。
- 查所有关联采购单。
- 按明细汇总应锁数量。
- 查活动商品当前
locked/used。 - 查提交日志是否重试。
- 查事务是否回滚而条件更新已提交。
- 查取消/补偿是否已反向一次。
26.4 支付成功仍待支付
- 查支付中心回调
payStatus='02'。 - 查采购单是否秒杀来源 13。
- 查 Payment/PoOrderPayInfo 落账。
- 查
markPaidByPoOrder日志。 - 查秒杀订单是否按
srcOrderId/srcOrderNo找到。 - 查锁量是否足够转
used。 - 安全运行
compensatePaidOrders单号候选验证。
26.5 超时仍占量
- 查订单状态和
expire_time。 - 查任务调度和最近执行结果。
- 查关联采购是否仍待支付或已关闭。
- 查支付是否恰好并发成功。
- 查
locked_qty是否已释放。 - 再运行补偿前锁订单并复核支付事实。
26.6 采购关闭但套包不能再买
- 查采购整单/行关闭数量。
- 查秒杀订单支付状态。
- 决定应释放 locked 还是 used。
- 查此前
closed_qty是否已处理。 - 查套包
PACKAGE_PURCHASE_LIMIT=1的已购判断是否仍把关闭单计入。 - 运行关闭补偿并验证第二次幂等。
27. 故障树
flowchart TD
A["秒杀交易异常"] --> B{"活动可见?"}
B -->|否| B1["审批/启用/时间/范围/价格/库存"]
B -->|是| C{"加购/预览通过?"}
C -->|否| C1["库存效期/价格/限购二次校验"]
C -->|是| D{"秒杀订单创建?"}
D -->|否| D1["锁量事务/参数"]
D -->|是| E{"采购单创建?"}
E -->|否| E1["采购提交/货主/多仓标记"]
E -->|是| F{"支付成功?"}
F -->|否| F1["支付或超时释放"]
F -->|是| G{"locked已转used?"}
G -->|否| G1["支付回调/paid补偿"]
G -->|是| H{"关闭释放正确?"}
H -->|否| H1["整单/行关闭差量补偿"]
28. 补偿前安全门槛
- 活动商品 ID、秒杀订单 ID、采购单 ID 全部固定。
- 证明支付中心最终状态。
- 证明采购单最终状态和关闭数量。
- 证明当前
locked_qty/used_qty与订单明细差值。 - 确认没有并发支付、关闭或超时任务。
- 使用条件更新,影响行数必须符合预期。
- 保存前后快照和操作日志。
- 首次补偿后再次执行应无变化。
29. 回归测试矩阵
29.1 OPS
- [ ] 单品/套包新增、编辑、复制。
- [ ] 完整
goodsList删除未生效行。 - [ ] 已生效行只能独立失效。
- [ ] 第三方商品、限售商品拦截。
- [ ] 单品 qty 空保存为 0、导出为空。
- [ ] 套包 qty 必填且大于 0。
- [ ] 指定仓编码和名称快照。
- [ ] 导入多行错误聚合。
29.2 冲突和 OA
- [ ] 单品与套包相同物料允许。
- [ ] 两单品效期重叠硬失败。
- [ ] 两套包同仓效期重叠失败、不同仓允许。
- [ ] 冲突失败不改状态。
- [ ] OA 通过、驳回、重复回调。
- [ ] OA payload 不含不存在的审批人字段。
29.3 服务站范围
- [ ] 单品无黑名单默认可见。
- [ ] 单品黑名单不可见。
- [ ] 省区新站自动加入且战区维度正确。
- [ ] 套包无白名单不可见。
- [ ] 套包唯一白名单可见。
- [ ] 全量替换和空数组清空。
29.4 价格库存
- [ ] 一口价不走普通采购价。
- [ ] 固定折扣普通采购价和 001 兜底。
- [ ] 无价单品隐藏、套包保存/提交失败。
- [ ] 服务站仓、指定仓、全局预留。
- [ ] 同 SKU 多效期库存明细去重。
- [ ] 单品 qty=0 仍按实时库存购买。
- [ ] 套包可组成包数取最小子件。
29.5 购物车和订单
- [ ] 单品加购/修改/勾选/删除。
- [ ] 套包不进购物车、直接购买。
- [ ] 跨活动单品结算,主表 activity_id=0。
- [ ] 预览和提交二次校验。
- [ ] 并发最后一份库存只有一单锁定成功。
- [ ] 客户端超时重试不重复下单。
- [ ] 套包正式限购 1 次。
29.6 采购支付补偿
- [ ] 采购类型
30-Cxx-77、来源 13。 - [ ] 指定仓套包多仓标记只影响秒杀。
- [ ] 支付成功 locked -> used。
- [ ] 重复支付回调幂等。
- [ ] 主动取消/超时释放 locked。
- [ ] 已支付整单/行关闭释放 used 差量。
- [ ] paid/closed 补偿重复执行无副作用。
30. 监控建议
| 指标 | 维度 | 告警 |
|---|---|---|
| OA 回调失败/延迟 | template/oa_no | 超 SLA |
| 活动商品状态刷新延迟 | activity_id | 时间边界后仍旧状态 |
| 列表与加购库存差异 | activity_goods_id | 同请求口径差异 |
| 待支付超时未关闭 | order_id | 超过 expire_time |
| 支付成功未标记 paid | po_order_id | 短延迟后仍不一致 |
| locked/used 超 qty | activity_goods_id | 任意出现 |
| 采购关闭未释放 | po_order_id | 关闭后仍占量 |
| 补偿失败 | task/单号 | 任意持续失败 |
| 重复采购单 | flash_order_id | 多于预期拆单数 |
| 无价商品请求 | activity_goods_id/sid | 突增 |
31. 已确认与待确认
31.1 静态代码和产品规则已确认
- 活动类型 1/2,库存范围 1/2/3。
- OA 状态 0/1/2/3,模板
callback_20260526。 - 活动设置状态和运行页面状态是不同维度。
- 单品
qty=0表示不限制活动量,套包必须qty>0。 - 套包正式购买次数限制为 1。
- 单品黑名单、套包唯一白名单。
- 名单保存按最终快照全量替换。
- 操作商品使用
activity_goods_id,不能只用inv_id。 - 一口价和固定折扣走不同价格链,无价不得下单。
- 同 SKU 多效期库存响应要去重。
- 提交订单增加
locked_qty,支付成功转used_qty。 - 采购类型
30-Cxx-77,来源类型 13。 - 支付中心成功回调后调用
markPaidByPoOrder。 - 采购关闭支持整单和明细差量释放。
- 有超时、商品状态、购物车、支付和关闭五类任务。
- 套包指定仓秒杀采购透传
multiWarehouseFlag=true。 - OA 固定审批人属于流程节点,不是表单字段。
31.2 环境待确认
- OA 生产模板字段、MQ 重试和回调 SLA。
- 库存中心仓库/效期请求和响应唯一明细键。
- 支付中心重复回调与迟到支付策略。
- 秒杀任务生产 cron 频率、单次 limit 和并发实例数。
- 活动/订单表线上唯一索引和锁等待配置。
- 多采购单拆分时整单支付完成判断。
- 采购整单/行关闭消息的生产所有权和重试。
- OPS 菜单、白名单和服务站入口生产配置。
- 前端实际字段与 Apipost 最新合同的一致性。
32. 证据来源
| 主题 | 文件 |
|---|---|
| OPS Controller | application/controllers/inner/activity/FlashActivity.php |
| 活动管理 | application/Services/Activity/FlashActivitySer.php |
| 站管家 Controller | application/controllers/scm/FlashSale.php |
| 移动商城 Controller | application/controllers/inner/moveMall/FlashSale.php |
| 交易 Service | application/Services/Activity/FlashSaleSer.php |
| 枚举 | application/KzData/Enums/FlashActivityEnums.php |
| 采购枚举 | application/KzData/Enums/PoOrderEnums.php |
| 支付回调 | application/controllers/tasks/PayCenterNotify.php |
| OA 回调 | application/controllers/tasks/OaNotify.php |
| 补偿任务 | application/controllers/tasks/FlashSaleTask.php |
| 表设计 | document/flash-sale-table-structure.md |
| API 合同 | document/flash-sale-api-mock.md |
| 需求覆盖 | document/flash-sale-requirement-coverage.md |
| 业务手册 | document/flash-sale-requirement-handbook.md |
33. 一页式执行结论
先用 activity_id/activity_goods_id/sid 固定活动商品,不按 invId 猜规则。
可见性依次查审批、启用、时间、商品、名单、价格和库存。
单品 qty=0 是不限活动量;套包 qty>0,available=qty-locked-used,购买上限1次。
单品与套包不判冲突;单品按物料+效期,套包再加仓库。
下单先原子锁 locked,支付成功 locked 转 used;取消超时释放 locked,已支付关闭释放 used。
采购单必须是 30-Cxx-77、srcOrderType=13,并保留秒杀订单ID/单号。
列表、加购、预览、提交必须复用同一价格和库存口径。
补偿前先证明支付和采购最终事实,补偿后重复执行应为0变化。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| OPS 管理 | /inner/activity/FlashActivity、scm/FlashSale | 活动、商品、站点范围、价格库存 | 两类 Controller | FlashActivitySer | 活动 ID | 活动配置、OA 和启停状态 |
| 站端购买 | /inner/moveMall/FlashSale | sid、活动商品、数量、购物车/地址 | 站端 Controller | FlashSaleSer | 活动+站点+SKU | 锁量、购物车、秒杀单、采购单 |
| OA/支付 | OA/PayCenter MQ | 活动审批;采购支付结果 | OaNotify、PayCenterNotify | 活动/秒杀 Service | 活动 ID;采购/秒杀关系 | 审批生效、支付锁量转已用 |
| 定时补偿 | FlashSaleTask | 过期时间窗、订单/购物车 ID | CLI task | 补偿方法 | 秒杀订单/购物车 | 关闭、释放与状态纠偏 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 活动审批 | OPS + OaNotify | request_id、活动 ID、OA 单号/模板码 | 审批态单向推进 | 未知模板、重复/乱序 | 活动 ID 查主表状态 | | 加购 | FlashSaleSer::addCart | sid、活动 ID、SKU、cart ID | locked_qty +n 与购物车一致 | 库存不足、限购、活动失效 | 活动商品与购物车对账 | | 下单支付 | submitOrder、PayCenterNotify | 秒杀单、采购单、支付单 | 三单关系完整,支付转 used | 采购创建部分失败、回调漏处理 | 关系字段串联三种单号 | | 关闭补偿 | FlashSaleTask/release 方法 | task 批次、订单、采购单 | 锁量释放且重跑 0 变化 | 重复释放、locked 负数 | 数量守恒 + task 批次 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 配置审批 | FlashActivitySer/OaNotify | 本地与 OA 异步边界 | 当前状态、商品/范围 | 活动三表、OA MQ | draft -> pending -> approved/rejected | OA 单号、活动状态历史 |
| 原子锁量 | FlashSaleSer::addCart | 锁量事务 | total-locked-used、限购量 | FLASH_CART、活动商品 | cart +n;locked_qty +n | total >= locked+used |
| 创建三单 | submitOrder/采购 Service | 跨表事务边界需验证 | 有效购物车和供给 | 秒杀订单/明细、采购主明细/关系 | 秒杀待支付;采购类型 30-Cxx-77、来源 13 | 秒杀/采购单号和 SKU 数量 |
| 支付转换 | markPaidByPoOrder | 回调事务 | 支付成功且未处理 | 秒杀订单、活动商品 | locked -n、used +n、订单 -> paid | 支付单、数量守恒 |
| 关闭释放 | release/compensate | 幂等事务 | 最终支付/采购关闭事实 | 订单、购物车、活动商品 | 未支付释放 locked;已支付关闭按代码释放 used | 重跑前后字段 0 变化 |
子模块追踪:flash-save-check 活动保存、效期与冲突规则
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 保存活动 | OPS 新建/编辑秒杀活动 | request ID、activity ID、商品列表、时间/效期 | application/controllers/inner/activity/FlashActivity.php -> application/Services/Activity/FlashActivitySer.php | 活动状态、商品档案、效期属性、同物料冲突范围 | 活动本地事务全量写主表与商品快照,draft old -> saved | request ID + activity ID + SKU/invId | 校验失败零写入;全量替换失败应整单回滚,不保留半份商品 |
| 冲突回查 | 保存失败或商品重复占用 | activity ID、SKU、生产效期、仓库 | application/KzData/Enums/FlashActivityEnums.php | 同期活动、单品/套包类型、物料+效期+仓库冲突键 | 查询只读 不写;冲突活动清单可回查 | activity IDs + conflict key | 单品与套包规则不同;不得通过手改活动状态绕过冲突校验 |
子模块追踪:flash-scope-check 站点范围与商品可见性
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 保存范围 | OPS 保存服务站黑/白名单 | activity ID、activity_goods_id、sid 集合 | application/Services/Activity/FlashActivitySer.php | 商品类型、已有范围、站点有效性 | 本地事务全量替换范围行 old set -> new set | request ID + activity/goods ID + sid count | 空集合语义按商品类型解释;替换失败整批回滚 |
| 可见查询 | 站端秒杀商品列表 | sid、activity ID、时间 | application/controllers/inner/moveMall/FlashSale.php -> application/Services/Activity/FlashSaleSer.php | 审批、启用、时间、商品状态和名单 | 查询只读 不写;按单品黑名单/套包白名单返回 | request ID + sid + activity/goods ID | 列表不可见依次查六层条件;不以库存单一条件下结论 |
子模块追踪:flash-oa-check OA 提交与回调
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 提交 OA | OPS 对已保存活动提交审批 | activity ID、模板码、申请人 | application/controllers/inner/activity/FlashActivity.php -> application/Services/Activity/FlashActivitySer.php | 活动完整性、当前 OA/启用状态、商品数量 | 本地事务 oa_status: draft -> pending 并保存关联;OA 请求在事务外 | request ID + activity ID + process/template code | OA 调用超时先查流程号;未受理再幂等提交,不能生成多流程 |
| 审批回调 | OA 20260526 等模板结果 | message ID、processNo、activity ID、结果 | application/controllers/tasks/OaNotify.php | 模板码、关联活动、当前申请态 | 回调本地事务 pending -> approved/rejected,重复结果 0 变化 | message ID + processNo + activity ID | 未知模板/乱序结果不覆盖终态;缺回调按流程号补发或回查 |
子模块追踪:flash-price-check 秒杀价格链
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 价格计算 | 列表、加购、预览、提交复核 | activity_goods_id、sid、SKU、价格类型 | application/Services/Activity/FlashSaleSer.php | 活动一口价/折扣、站点基础价、套包组成 | 计算阶段查询只读 不写;输出统一成交价和金额 | request ID + activity goods ID + sid + calculated price | 无基础价/活动价按规则不可买;禁止各入口自行实现不同算法 |
| 下单快照 | 提交订单保存最终价格 | 秒杀单、采购单、商品金额 | application/Services/Activity/FlashSaleSer.php | 提交时重算价、购物车价和金额合计 | 下单本地事务写订单/采购价格快照 none -> final price | flash/po orderNo + activity goods ID + amount | 列表价变化以提交复核为准;部分写入时按三单关系回滚/补偿 |
子模块追踪:flash-stock-check 秒杀库存计算
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 可售计算 | 商品列表、加购、预览查询 | activity_goods_id、sid、SKU、效期/仓库 | application/Services/Activity/FlashSaleSer.php | 单品实时/在途/活动口径;套包 qty/locked/used 与组件约束 | 查询只读 不写;单品 qty=0 按不限活动量,套包 available=qty-locked-used | request ID + activity goods ID + inventory keys | 同 SKU 多效期去重后计算;列表口径不能替代提交锁后事实 |
| 数量对账 | 可售异常或 locked/used 不平 | 活动商品、购物车、秒杀/采购单 | application/KzData/Enums/FlashActivityEnums.php | 有效购物车、待支付/已支付/关闭订单和采购最终态 | 查询只读 不写;验证 total >= locked+used 和订单数量守恒 | activity/order/cart IDs | 负数或占量遗漏先定位生命周期步骤,再执行对应释放/转换补偿 |
子模块追踪:flash-cart-check 购物车与失效
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 加购修改 | 站端添加、改量、删除购物车 | sid、activity_goods_id、cart ID、数量 | application/controllers/inner/moveMall/FlashSale.php -> application/Services/Activity/FlashSaleSer.php | 活动有效性、范围、价格、库存、限购和已有 cart | 购物车本地事务 cart old -> new,对应 locked_qty +/- diff | request ID + sid + activity/cart ID | 任一校验失败零变化;并发改量用条件更新防止 locked 超限 |
| 失效释放 | 活动/商品失效或购物车超时任务 | cart ID、activity ID、批次 | application/controllers/tasks/FlashSaleTask.php | cart 当前态、是否已转订单、locked 数量 | 每车本地事务 cart valid -> invalid 且 locked_qty -n,重跑 0 变化 | task batch + cart/activity ID + affected rows | 已转订单购物车不得释放订单占量;失败键小批重跑 |
子模块追踪:flash-preview-submit 预览、提交与原子锁量
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 订单预览 | 站端从有效购物车预览 | sid、cart IDs、地址/供应商上下文 | application/controllers/inner/moveMall/FlashSale.php -> application/Services/Activity/FlashSaleSer.php | 购物车、活动、价格、库存、仓库和配送信息 | 查询只读 不写;返回分供应商预览快照 | request ID + sid + cart/activity IDs | 预览成功不占用新数量;提交必须重新校验全部条件 |
| 原子提交 | 用户确认提交秒杀订单 | cart IDs、client request key、地址 | application/Services/Activity/FlashSaleSer.php | 锁后的 locked/used、限购、购物车状态、价格 | 下单本地事务条件锁量并写秒杀主明细,cart -> ordered、order none -> wait_pay | request ID + flash orderNo + cart IDs | 并发条件失败整单回滚;采购创建失败按关系释放本次锁量 |
子模块追踪:flash-po-pay 转采购与支付
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 转采购 | 秒杀提交按供应商创建采购 | flash orderNo、supplier、仓库、商品 | application/Services/Activity/FlashSaleSer.php -> application/KzData/Enums/PoOrderEnums.php | 秒杀订单明细、供应商分组、仓库透传和来源唯一性 | 本地事务创建采购 orderType=30-Cxx-77/srcOrderType=13 并保存秒杀关系 | flash/po orderNos + source ID | 多采购部分失败按已建关系逐单回滚/关闭,不能重复建成功单 |
| 支付转换 | 采购支付中心成功回调 | payOrderNo、po/flash orderNo、message ID | application/controllers/tasks/PayCenterNotify.php -> application/Services/Activity/FlashSaleSer.php | 首次支付、三单关系、locked/used | 回调本地事务订单 wait_pay -> paid、locked -n, used +n | message ID + pay/po/flash orderNos | 重复回调 0 增量;状态异常先查采购支付和数量账再补偿 |
子模块追踪:flash-cancel-compensate 取消、超时、关闭与补偿
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 取消关闭 | 用户取消、待支付超时、采购关闭 | flash/po orderNo、当前状态、原因 | application/Services/Activity/FlashSaleSer.php | 支付最终态、采购关闭态、订单和 locked/used | 每单本地事务未支付释放 locked;已支付关闭按当前代码释放 used,状态到 closed | orderNos + action/task + affected qty | 支付不确定时禁止释放;并发回调由状态条件保证仅一次 |
| 定时补偿 | expireWaitPayOrders、paid/closed 补偿 | task batch、时间窗、order IDs | application/controllers/tasks/FlashSaleTask.php | 候选状态、更新时间、支付/采购/数量最终事实 | 小批本地事务只修缺失变化;已完成 new -> new 且影响行 0 | task + batch + scanned/success/fail IDs | 保留失败清单;补偿后核对 locked/used 非负及三单状态闭环 |
子模块追踪:flash-import-export 秒杀导入、导出与模板
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 模板导入 | OPS 下载模板并导入活动商品 | activity ID、file/task ID、模板版本 | application/controllers/scm/FlashSale.php -> application/Services/Activity/FlashActivitySer.php | 列头版本、SKU/套包、仓库、价格、数量和效期规则 | 解析校验阶段不写;全部通过后本地事务批量 old rows -> imported rows | request ID + activity/file ID + row/error counts | 任一行错误返回行号原因;不得部分写入后伪装全量成功 |
| 导出核对 | 导出活动商品/错误结果 | activity ID、export task ID | application/controllers/scm/FlashSale.php | 当前活动商品、范围、显示数量和仓库名称 | 查询只读 不写;异步文件任务 pending -> success/failed | request ID + activity/task ID + row count | 单品数量显示和套包口径不同;文件失败只重跑导出任务 |