council(merge): resolve conflict — adopt execution plan (Round 1 P0 fix)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>refactor/vr-ticket-20260416
commit
a2fb70d216
378
plan.md
378
plan.md
|
|
@ -1,33 +1,21 @@
|
|||
# vr-shopxo-plugin 架构决策评议 — plan.md
|
||||
# vr-shopxo-plugin P0 修复执行计划 — plan.md
|
||||
|
||||
> 版本:v1.2(最终合并版)| 日期:2026-04-15 | Agent:council/FrontendDev + BackendArchitect + SecurityEngineer
|
||||
> 关联:Issue #9 | 状态:FINAL
|
||||
> 版本:v1.0 | 日期:2026-04-15 | Agent:BackendArchitect + FrontendDev
|
||||
> 关联:Issue #9 | 状态:执行中
|
||||
|
||||
---
|
||||
|
||||
## 任务背景
|
||||
|
||||
Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱会票务商品中 ShopXO SPEC 与 SKU 的绑定方案。
|
||||
|
||||
**已知事实:**
|
||||
- ShopXO `goods_spec_base`(SKU表)当前为空,商品 112 的 `is_exist_many_spec=0`
|
||||
- `spec_base_id_map` 中的 ID(如 1001/1002/1003)在 DB 中不存在
|
||||
- ShopXO 防超卖机制(原子扣 inventory)完全未启用
|
||||
|
||||
**两种架构方向:**
|
||||
- **方案 A**:每个座位 = 一个 SKU(stock=1),ShopXO 原生防超卖
|
||||
- **方案 B**:每个 Zone = 一个 SKU(stock=Zone座位数),自建 FOR UPDATE 防超卖
|
||||
方案 A 已全票通过(见 `council-output/ARCHITECTURE_DECISION.md`)。现在进入**执行阶段**,按优先级实施三个任务。
|
||||
|
||||
---
|
||||
|
||||
## 核心问题(4问)
|
||||
## 任务清单
|
||||
|
||||
| # | 问题 | 负责 |
|
||||
|---|------|------|
|
||||
| Q1 | 方案 A 后台批量生成 SKU 路径是否可行?ShopXO 是否有批量 API? | BackendArchitect |
|
||||
| Q2 | 当前商品 112 的 broken 状态(is_exist_many_spec=0 + spec_base 空)是否需要紧急修复?最小修复集? | BackendArchitect |
|
||||
| Q3 | $vr- 前缀方案是否有隐患?ShopXO 内部是否对 $ 有特殊处理? | SecurityEngineer + FrontendDev |
|
||||
| Q4 | 方案 A vs 方案 B 最终推荐(实现成本 / 安全性 / 可维护性) | 所有成员 |
|
||||
- [ ] **P0-A**: `BaseService::initGoodsSpecs()` — 商品 112 最小修复集 `[Claimed: BackendArchitect]`
|
||||
- [ ] **P0-B**: `SeatSkuService::BatchGenerate()` — 批量生成座位级 SKU `[Claimed: BackendArchitect]`
|
||||
- [ ] **P1**: `ticket_detail.html` submit() 重构 — seat-level 逐座提交 `[Claimed: FrontendDev]`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -35,19 +23,85 @@ Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱
|
|||
|
||||
| 阶段 | 内容 | 负责 |
|
||||
|------|------|------|
|
||||
| Round 1 | 独立评议 + plan.md 合并 | 所有成员 |
|
||||
| Round 2 | 各成员深入分析(后台实现路径、安全评估、前端方案) | 所有成员 |
|
||||
| Round 3 | 综合推荐 + 输出最终决策报告 + `council-output/ARCHITECTURE_DECISION.md` | FrontendDev 主笔 |
|
||||
| Draft | 各成员编写执行代码 | BackendArchitect (P0-A/P0-B), FrontendDev (P1) |
|
||||
| Review | 代码互审,验证 SQL 正确性 | BackendArchitect 审 P1, FrontendDev 审 P0-A/P0-B |
|
||||
| Finalize | 合并到 main,实测验证 | 所有成员 |
|
||||
|
||||
---
|
||||
|
||||
## 任务清单
|
||||
## P0-A 详细设计
|
||||
|
||||
- [x] **Q1**: 方案 A 批量生成 SKU 路径 `[Done: BackendArchitect]` ✅
|
||||
- [x] **Q2**: 商品 112 broken 状态紧急修复 `[Done: BackendArchitect]` ✅
|
||||
- [x] **Q3**: $vr- 前缀安全评估 `[Done: SecurityEngineer + FrontendDev]` ✅
|
||||
- [x] **Q4**: 方案 A vs 方案 B 最终推荐 `[Done: 所有成员]` ✅ — 三方一致推荐方案 A
|
||||
- [x] **Final**: `council-output/ARCHITECTURE_DECISION.md` `[Done: FrontendDev]` ✅
|
||||
**文件**: `plugins/vr_ticket/service/BaseService.php`
|
||||
|
||||
**方法**: `public static function initGoodsSpecs(int $goodsId): bool`
|
||||
|
||||
**逻辑**:
|
||||
1. UPDATE `is_exist_many_spec=1` WHERE `id=$goodsId`(幂等)
|
||||
2. 检查 `$vr-场馆`/`$vr-分区`/`$vr-时段` 是否存在(按 name 查 `goods_spec_type`),不存在则 INSERT
|
||||
3. 使用 `INSERT IGNORE` 或 `ON DUPLICATE KEY` 防止重复
|
||||
|
||||
**关键 SQL**:
|
||||
```sql
|
||||
UPDATE sxo_goods SET is_exist_many_spec = 1 WHERE id = $goodsId;
|
||||
|
||||
INSERT IGNORE INTO sxo_goods_spec_type (goods_id, name, value, add_time) VALUES
|
||||
($goodsId, '$vr-场馆', '[{"name":"国家体育馆","images":""}]', UNIX_TIMESTAMP()),
|
||||
($goodsId, '$vr-分区', '[{"name":"A区","images":""},{"name":"B区","images":""},{"name":"C区","images":""}]', UNIX_TIMESTAMP()),
|
||||
($goodsId, '$vr-时段', '[{"name":"2026-05-01 19:00","images":""}]', UNIX_TIMESTAMP());
|
||||
```
|
||||
|
||||
**验证**: 执行后 `SELECT * FROM sxo_goods_spec_type WHERE goods_id=112` 确认 3 条 spec_type 记录。
|
||||
|
||||
---
|
||||
|
||||
## P0-B 详细设计
|
||||
|
||||
**文件**: `plugins/vr_ticket/service/SeatSkuService.php`(新建)
|
||||
|
||||
**方法**: `public static function BatchGenerate(int $goodsId, int $seatTemplateId): array`
|
||||
|
||||
**返回值**:
|
||||
```php
|
||||
[
|
||||
'total' => 100, // 生成的 SKU 总数
|
||||
'batch' => 1, // 批次数
|
||||
'spec_base_id_map' => ['A1_1' => 2001, 'A1_2' => 2002, ...] // seatId => spec_base_id
|
||||
]
|
||||
```
|
||||
|
||||
**核心逻辑**:
|
||||
1. 从 `vr_seat_template` 读取 seat_map(zones → rows → seats)
|
||||
2. 从 zone 配置获取 price
|
||||
3. 遍历每个座位,生成 `goods_spec_base` 行(inventory=1,price 从 zone.price 获取)
|
||||
4. 同时写入 `goods_spec_value`(spec_type_id × 4 维度 = 4 行/座位)
|
||||
5. **必须旁路 `GoodsSpecificationsInsert()`** — 直接 SQL INSERT
|
||||
6. 分批:500 条/批,10000 座位约 20 批
|
||||
|
||||
**关键表结构**:
|
||||
- `sxo_goods_spec_base`: id (PK auto), goods_id, spec_base, price, inventory, color, images, weight, stock
|
||||
- `sxo_goods_spec_value`: id (PK auto), goods_id, spec_base_id (FK), spec_type_id (FK), `spec_value` (JSON)
|
||||
|
||||
**幂等**: 先 DELETE 已存在的座位级 SKU(spec_type_id IN (venue,zone,time,seat_num)),再重建。
|
||||
|
||||
---
|
||||
|
||||
## P1 详细设计(FrontendDev)
|
||||
|
||||
**文件**: `plugins/vr_ticket/view/goods/ticket_detail.html`
|
||||
|
||||
**逻辑**:
|
||||
1. `submit()` 改为遍历 `this.selectedSeats`
|
||||
2. 每个座位从 `app.specBaseIdMap[seatId]` 获取 `spec_base_id`
|
||||
3. 构造 `goods_params` 数组,每个座位一行
|
||||
4. 降级策略:`spec_base_id` 不存在时走原 Plan B 逻辑
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系
|
||||
|
||||
- P0-A 和 P0-B 可并行开发
|
||||
- P1 依赖 P0-B 完成后注入 `specBaseIdMap` 数据
|
||||
- P0-A 完成后需在 ShopXO 容器实测验证
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -55,261 +109,17 @@ Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱
|
|||
|
||||
| 任务 | Claim 状态 |
|
||||
|------|-----------|
|
||||
| Q1 | [Done: BackendArchitect] |
|
||||
| Q2 | [Done: BackendArchitect] |
|
||||
| Q3 | [Done: SecurityEngineer] + [Done: FrontendDev] |
|
||||
| Q4 | [Done: BackendArchitect] + [Done: FrontendDev] + [Done: SecurityEngineer] |
|
||||
| 最终输出 | [Done: FrontendDev] |
|
||||
| P0-A | [Claimed: BackendArchitect] |
|
||||
| P0-B | [Claimed: BackendArchitect] |
|
||||
| P1 | [Claimed: FrontendDev] |
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系
|
||||
|
||||
- Q1(BackendArchitect)先完成,后 Q4 才能给出完整推荐
|
||||
- Q3(SecurityEngineer)可与 Q1 并行
|
||||
- Q2 可独立完成,紧急程度由 BackendArchitect 判定
|
||||
- 三方分析完成后,FrontendDev 主笔 Round 3 最终报告
|
||||
|
||||
---
|
||||
|
||||
## 各成员 Round 1 初判
|
||||
|
||||
### BackendArchitect 初判
|
||||
|
||||
**Q1 初步判断**:Plan A 后台批量生成 SKU **可行**。ShopXO 的 `goods_spec_base` 是标准 MySQL 表,插件可直接 INSERT。
|
||||
|
||||
**Q2 初步判断**:当前 broken 状态**暂不需要立即修复**。购买流程走的是裸商品逻辑(is_exist_many_spec=0),需要明确购买流程最终走哪条路后再修。
|
||||
|
||||
**Q4 初步判断**:倾向 **方案 A**。ShopXO 原生防超卖机制比自建锁更可靠(DB 层面原子操作)。
|
||||
|
||||
### FrontendDev 初判(Q1-Q4 分析)
|
||||
|
||||
**Q1**:结论:**可行,但实现路径复杂。** 无现成批量 API,需要插件自管(Hook 隐藏)。SKU 数量 = 座位数(10000+)。
|
||||
**Q2**:结论:**需要立即修复,推荐最小方案。**
|
||||
**Q3**:结论:**低风险,但需实测确认。**
|
||||
**Q4 推荐**:**方案 A(每个座位一个 SPEC/SKU)**。安全性+数据一致性优先。
|
||||
|
||||
### SecurityEngineer 初判(Q2/Q3/Q4)
|
||||
|
||||
**Q2**:依赖 Q1/Q4,标记为 blocked。
|
||||
**Q3**:ThinkPHP View 层可能对 `$` 有变量插值行为,需要代码验证(Round 2 执行)。
|
||||
**Q4**:初步倾向 **方案 A**。
|
||||
|
||||
---
|
||||
|
||||
## 各成员 Round 2 深入分析
|
||||
|
||||
### BackendArchitect Round 2 深入分析(Q1+Q2)
|
||||
|
||||
详细分析见 `docs/ROUND2_ANALYSIS.md`。
|
||||
|
||||
**Q1 结论:可行,但必须旁路 `GoodsSpecificationsInsert()`**
|
||||
|
||||
- `GoodsSpecificationsInsert()` 每次商品保存时 DELETE 所有现有 spec 后重建,10K+ 座位场景不可用
|
||||
- 可行路径:**直接 SQL INSERT** 到 `sxo_goods_spec_type`、`sxo_goods_spec_base`、`sxo_goods_spec_value` 三表
|
||||
- 关键代码:`BuyService.php:1677-1681` 的 `dec()` 机制 = MySQL 条件原子扣减 `UPDATE SET inventory = inventory - N WHERE inventory >= N`
|
||||
- TOCTOU 窗口极小(选座模式并发低 + InnoDB 行锁),**推荐接受此风险**
|
||||
- 性能:10000 座位 ≈ 3-4 秒(需分批 500 条/批提交)
|
||||
|
||||
**Q2 结论:推荐方案乙(最小修复集)**
|
||||
|
||||
- `UPDATE goods SET is_exist_many_spec=1 WHERE id=112`
|
||||
- 写入 `$vr-` 规格维度到 `sxo_goods_spec_type`
|
||||
- 幂等保护:票生成逻辑已有 `spec_base_id` 冗余
|
||||
|
||||
**Q4 初步推荐:方案 A**
|
||||
|
||||
- 原子性已验证(BuyService dec 机制)
|
||||
- 数据完整性高(每个座位 inventory=1)
|
||||
- 票务链路清晰(spec_base_id → 座位直接映射)
|
||||
|
||||
### SecurityEngineer Round 2 分析(Q3)
|
||||
|
||||
SecurityEngineer 在 Round 2 进行了 ThinkPHP View 层的 $vr- 前缀安全审计,结论:**无高危风险**。
|
||||
|
||||
### FrontendDev Round 2 深入分析(Q3+Q4)
|
||||
|
||||
**Q3 结论:$vr- 前缀安全** ✅
|
||||
- ThinkPHP `{$var}` 默认做 HTML 转义,$vr- 不会被解析为 PHP 变量
|
||||
- `|raw` 仅跳过 HTML 转义,不会执行变量插值
|
||||
- ThinkPHP parseVar 正则对连字符 `-` 的处理会阻断 $vr- 的完整解析
|
||||
- ShopXO spec name 存 DB 无过滤,但渲染层安全
|
||||
|
||||
**Q4 最终推荐:方案 A(每个座位一个 SPEC/SKU)—— 明确推荐**
|
||||
|
||||
**核心发现**:
|
||||
1. 当前 `ticket_detail.html` submit() 是 Plan B 模式,`specBaseIdMap` 已声明但**未接入** submit 逻辑
|
||||
2. ShopXO 购买流程从 `spec_base` 表读取库存并原子扣减
|
||||
|
||||
**方案 A vs B 最终对比**:
|
||||
|
||||
| 维度 | 方案 A(每座=SKU) | 方案 B(每 Zone=SKU) |
|
||||
|------|-------------------|---------------------|
|
||||
| **防超卖** | ShopXO 原生原子扣库存(stock=1),DB 层保证 | 自建 FOR UPDATE 锁,需自己写并发逻辑 |
|
||||
| **实现复杂度** | 后端需批量生成 1 万+ SKU;前端 `submit()` 需改为逐座提交 | 后端简单;前端按 Zone 分组即可 |
|
||||
| **多 Zone 混买** | 每座一行 goods_params,后端原子处理 | 前端分组但后端共享 Zone 库存,复杂度高 |
|
||||
| **后台可维护性** | 10000+ SKU 行,但可 Hook 隐藏 | Zone 数量少,后台友好 |
|
||||
| **调试/故障排查** | 每个 SKU 独立,可追溯 | 共享库存,出问题难以定位 |
|
||||
| **与 ShopXO 生态** | 完全对齐,无缝集成 | 绕过 spec 校验,部分 ShopXO 功能失效 |
|
||||
|
||||
**Plan A 前端实现路径**:
|
||||
|
||||
关键修改:将 `submit()` 从"session-level 提交"改为"seat-level 逐座提交":
|
||||
|
||||
```javascript
|
||||
// Plan A: 每座一行 goods_params,逐座购买
|
||||
this.selectedSeats.forEach(function(seat) {
|
||||
var seatSpecBaseId = app.specBaseIdMap[seat.row + '_' + seat.col]?.spec_base_id;
|
||||
// 如果 spec_base_id 存在,走 ShopXO 原生购买
|
||||
// 否则走 Plan B 回退逻辑
|
||||
});
|
||||
```
|
||||
|
||||
`specBaseIdMap` 数据结构已就位(从后端 PHP 注入),前端只需接入即可。
|
||||
|
||||
---
|
||||
|
||||
## 各成员 Round 3 最终推荐
|
||||
|
||||
### BackendArchitect Round 3 最终推荐(Q1+Q2+Q4)
|
||||
|
||||
**Q1 最终结论**:可行。必须旁路 `GoodsSpecificationsInsert()`,走**直接 SQL INSERT** 路径。性能:10000 座位 ≈ 3-4 秒(分批 500 条/批)。关键:`spec_base_id_map[seat_id] → actual_db_id` 映射必须在 INSERT 后即时重建。
|
||||
|
||||
**Q2 最终结论**:推荐**方案乙**(最小修复集):
|
||||
1. `UPDATE sxo_goods SET is_exist_many_spec=1 WHERE id=112`
|
||||
2. `INSERT $vr- spec_type`(场馆/分区/时段三行)
|
||||
3. 幂等保护:`TicketService::issueTicket()` 中对 `spec_base_id=0` 做 fallback
|
||||
|
||||
**Q3 最终结论**(汇入 SecurityEngineer + FrontendDev 确认):低风险。ThinkPHP `{$var}` 默认 HTML 转义,`$vr-` 不会触发变量解析。
|
||||
|
||||
**Q4 最终推荐:方案 A**,理由汇总:
|
||||
1. **ShopXO 原生原子防超卖**:`BuyService::dec()` = MySQL 条件原子扣减,无需自建锁
|
||||
2. **TOCTOU 风险可接受**:选座模式并发窗口极小,InnoDB 行锁提供最后保护
|
||||
3. **票务链路清晰**:`spec_base_id` 直接映射座位,票生成无需反向解析
|
||||
4. **方案 B 优势不成立**:插件自管 SKU(Hook 隐藏),不走 ShopXO 后台,无"管理困难"问题
|
||||
|
||||
### FrontendDev Round 3 最终推荐(Q3+Q4)
|
||||
|
||||
三方一致推荐 **方案 A(每个座位一个 ShopXO SKU)**。
|
||||
|
||||
最终决策报告:`council-output/ARCHITECTURE_DECISION.md`
|
||||
|
||||
---
|
||||
|
||||
## 行动项(优先级排序)
|
||||
|
||||
| 优先级 | 行动项 | 负责 |
|
||||
|--------|--------|------|
|
||||
| P0 | 创建 `SeatSkuService::BatchGenerate()` — 直接 SQL INSERT 批量生成 SKU(分批 500 条) | BackendArchitect |
|
||||
| P0 | 执行 Q2 最小修复集:`UPDATE is_exist_many_spec=1` + `INSERT $vr- spec_type` | BackendArchitect |
|
||||
| P1 | `TicketService::issueTicket()` 添加 `spec_base_id=0` 幂等保护 | BackendArchitect |
|
||||
| P1 | 重构 `ticket_detail.html` submit():接入 `specBaseIdMap`,改为 seat-level 逐座提交 | FrontendDev |
|
||||
| P2 | 实现 `loadSoldSeats()`:查询各 seat spec_base 的库存状态 | FrontendDev |
|
||||
| P2 | Hook 隐藏插件专用 SKU(隔离 ShopXO 原生规格管理页) | FrontendDev |
|
||||
| P3 | 设计插件独立 SKU 管理页面 | FrontendDev |
|
||||
|
||||
---
|
||||
|
||||
## 共识投票
|
||||
|
||||
| 成员 | CONSENSUS |
|
||||
|------|-----------|
|
||||
| BackendArchitect | `[CONSENSUS: YES]` — 推荐方案 A,Round 2/3 分析完成 |
|
||||
| SecurityEngineer | `[CONSENSUS: YES]` — $vr- 前缀低风险,方案 A 推荐 |
|
||||
| FrontendDev | `[CONSENSUS: YES]` — 方案 A 推荐,前端配合方案清晰 |
|
||||
|
||||
**全票通过:采纳方案 A**
|
||||
|
||||
---
|
||||
|
||||
## Issue #9 执行计划 — Round 4(P0 修复)
|
||||
|
||||
> 执行日期:2026-04-15 | 目标:方案 A 全量落地
|
||||
|
||||
### 任务清单
|
||||
|
||||
- [ ] **P0-A**: `BaseService::initGoodsSpecs()` — 修复商品 112 broken state `[Claimed: BackendArchitect]`
|
||||
- [ ] **P0-B**: `SeatSkuService::BatchGenerate()` — 批量生成座位级 SKU `[Claimed: BackendArchitect]`
|
||||
- [ ] **P1**: `ticket_detail.html` submit() 重构 — seat-level goods_params `[Claimed: FrontendDev]`
|
||||
- [ ] **P1-Verification**: 前端实测验证(商品 112 购买流程) `[Claimed: FrontendDev]`
|
||||
|
||||
### 阶段划分
|
||||
|
||||
| 阶段 | 内容 | 负责 |
|
||||
|------|------|------|
|
||||
| **Draft** | BackendArchitect: P0-A + P0-B 实现;FrontendDev: submit() 重构 | 双线并行 |
|
||||
| **Review** | 互相 review对方代码,确认接口对齐 | 双线并行 |
|
||||
| **Finalize** | 合并到 main,实测验证 | 共同 |
|
||||
|
||||
### 依赖关系
|
||||
|
||||
- P0-A 完成后,P0-B 才能验证 spec_type 维度是否存在
|
||||
- P0-A + P0-B 完成后,前端 submit() 重构才有正确的 spec_base_id 可用
|
||||
- 前端实测依赖后端 SKU 已生成
|
||||
|
||||
### P1 详细执行计划
|
||||
|
||||
**当前状态(ticket_detail.html 第 413-418 行)**:
|
||||
```javascript
|
||||
var goodsParams = JSON.stringify([{
|
||||
goods_id: this.goodsId,
|
||||
spec_base_id: this.sessionSpecId, // ← Zone 级别,只有 1 个
|
||||
stock: this.selectedSeats.length, // ← 数量,但 ShopXO 不知道具体是哪些座位
|
||||
extension_data: extensionData
|
||||
}]);
|
||||
```
|
||||
|
||||
**重构目标**:每座一行 goods_params:
|
||||
```javascript
|
||||
// 每座一行,逐座提交
|
||||
var goodsParamsList = [];
|
||||
this.selectedSeats.forEach(function(seat) {
|
||||
var seatKey = seat.row + '_' + seat.col;
|
||||
var specBaseId = app.specBaseIdMap[seatKey]?.spec_base_id || app.sessionSpecId;
|
||||
goodsParamsList.push({
|
||||
goods_id: app.goodsId,
|
||||
spec_base_id: specBaseId,
|
||||
stock: 1,
|
||||
extension_data: JSON.stringify({
|
||||
attendee: attendees.find(function(a) { return a._seat === seatKey; }),
|
||||
seat: seat
|
||||
})
|
||||
});
|
||||
});
|
||||
var goodsParams = JSON.stringify(goodsParamsList);
|
||||
```
|
||||
|
||||
**关键改动点**:
|
||||
1. `submit()` 改为遍历 `selectedSeats`,每座一行 goods_params
|
||||
2. `spec_base_id` 从 `specBaseIdMap[seatKey]` 获取(Plan A:座位级 SKU)
|
||||
3. `stock` 固定为 1(每个 SKU 对应一个座位)
|
||||
4. `extension_data` 改为 seat-level,每座携带自己的信息
|
||||
5. 保留 `sessionSpecId` 作为 fallback(Plan B 回退)
|
||||
|
||||
### P0-B 返回值接口约定
|
||||
|
||||
BackendArchitect 的 `BatchGenerate()` 返回值需包含:
|
||||
```php
|
||||
[
|
||||
'total' => 100, // 生成数量
|
||||
'spec_base_id_map' => [ // seatKey → spec_base_id 映射
|
||||
'A_1' => ['spec_base_id' => 2001, 'zone_id' => 'zone1', 'row' => 'A', 'col' => 1],
|
||||
'B_2' => ['spec_base_id' => 2002, 'zone_id' => 'zone1', 'row' => 'B', 'col' => 2],
|
||||
...
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
前端期望 `specBaseIdMap` 格式:
|
||||
- Key: `row_col`(如 `"A_1"`)
|
||||
- Value: `{spec_base_id: number, zone_id: string, row: string, col: number}`
|
||||
|
||||
---
|
||||
|
||||
## Round 3 安全审计结果(保留,仅供参考)
|
||||
|
||||
### Task S1 — Admin 鉴权覆盖完整性审查 ✅ 验证通过
|
||||
### Task S2 — SQL 注入风险审计 ✅ 无注入风险
|
||||
### Task S3 — XSS / CSRF 防护检查 ✅ 通过
|
||||
### Task S5 — IDOR 水平越权检查 ✅ 通过
|
||||
### Task S4 — 敏感操作审计日志设计 ✅ 设计完成
|
||||
## 执行顺序
|
||||
|
||||
1. BackendArchitect: P0-A 代码 + SQL 验证
|
||||
2. BackendArchitect: P0-B SeatSkuService::BatchGenerate()
|
||||
3. FrontendDev: P1 submit() 重构
|
||||
4. BackendArchitect: 合并到 main
|
||||
5. 容器实测:商品 112 `initGoodsSpecs(112)` → 验证 is_exist_many_spec=1 + 3条spec_type
|
||||
6. 容器实测:`BatchGenerate(112, $templateId)` → 验证座位级 SKU 生成
|
||||
|
|
|
|||
Loading…
Reference in New Issue