# vr-shopxo-plugin P0 修复执行计划 — plan.md > 版本:v2.0 | 日期:2026-04-15 | Agent:BackendArchitect + FrontendDev > 关联:Issue #9 | 状态:执行中 --- ## 任务背景 方案 A 已全票通过(见 `council-output/ARCHITECTURE_DECISION.md`)。现在进入**执行阶段**,按优先级实施三个任务。 --- ## 任务清单 - [x] **P0-A**: `BaseService::initGoodsSpecs()` — 商品 112 最小修复集 `[Done: BackendArchitect]` - [x] **P0-B**: `SeatSkuService::BatchGenerate()` — 批量生成座位级 SKU `[Done: BackendArchitect]` - [x] **P1**: `ticket_detail.html` submit() 重构 — seat-level 逐座提交 `[Done: FrontendDev]` --- ## 阶段划分 | 阶段 | 内容 | 负责 | |------|------|------| | Draft | 各成员编写执行代码 | BackendArchitect (P0-A/P0-B), FrontendDev (P1) | | Review | 代码互审,验证 SQL 正确性 | BackendArchitect 审 P1, FrontendDev 审 P0-A/P0-B | | Finalize | 合并到 main,实测验证 | 所有成员 | --- ## P0-A 详细设计 **文件**: `plugins/vr_ticket/service/BaseService.php` **方法**: `public static function initGoodsSpecs(int $goodsId): array` **逻辑**: 1. UPDATE `is_exist_many_spec=1` WHERE `id=$goodsId`(幂等) 2. 检查 `$vr-场馆`/`$vr-分区`/`$vr-时段`/`$vr-座位号` 是否存在(按 name 查 `goods_spec_type`),不存在则 INSERT 3. 使用 `Db::name('GoodsSpecType')->insert()` 防止重复(先查后插) **验证**: 执行后 `SELECT * FROM sxo_goods_spec_type WHERE goods_id=112` 确认 4 条 spec_type 记录。 --- ## P0-B 详细设计 **文件**: `plugins/vr_ticket/service/SeatSkuService.php`(新建) **方法**: `public static function BatchGenerate(int $goodsId, int $seatTemplateId): array` **返回值**: ```php [ 'code' => 0, 'msg' => '...', 'data' => [ 'total' => 100, // 座位总数 'generated' => 50, // 本次生成数 'batch' => 1, // 批次数 'spec_base_id_map' => ['0_0' => 2001, '0_1' => 2002, ...] // seatId => spec_base_id ] ] ``` **核心逻辑**: 1. 从 `vr_seat_templates` 读取 seat_map(map → rows → seats) 2. 从 zone 配置获取 price(seat_info.price 或 section.price) 3. 遍历每个座位,生成 `goods_spec_base` 行(inventory=1,price 从 zone.price 获取) 4. 同时写入 `goods_spec_value`(4 维度 × N 座位 = 4N 行) 5. **旁路 `GoodsSpecificationsInsert()`** — 直接 SQL INSERT 6. 分批:500 条/批,10000 座位约 20 批 **幂等**: 已存在的座位(通过 $vr-座位号 spec_value 的 extends.seat_id 判断)不重复生成。 --- ## P1 详细设计(FrontendDev) **文件**: `plugins/vr_ticket/view/goods/ticket_detail.html` **逻辑**: 1. `submit()` 改为遍历 `this.selectedSeats` 2. 每个座位从 `app.specBaseIdMap[seatKey]` 获取 `spec_base_id`(seatKey = `row_col`) 3. 构造 `goods_params` 数组,每个座位一行 4. 降级策略:`spec_base_id` 不存在时走原逻辑 --- ## 依赖关系 - P0-A 和 P0-B 可并行开发 - P1 依赖 P0-B 完成后注入 `specBaseIdMap` 数据 - P0-A 完成后需在 ShopXO 容器实测验证 --- ## Claim 状态 | 任务 | Claim 状态 | |------|-----------| | P0-A | [Done: BackendArchitect] | | P0-B | [Done: BackendArchitect] | | P1 | [Done: FrontendDev] | --- ## 执行顺序 1. [Done] BackendArchitect: P0-A 代码 + SQL 验证 2. [Done] BackendArchitect: P0-B SeatSkuService::BatchGenerate() 3. [Done] FrontendDev: P1 submit() 重构 4. [In Progress] BackendArchitect: 合并到 main 5. [Pending] 容器实测:商品 112 `initGoodsSpecs(112)` → 验证 is_exist_many_spec=1 + 4条spec_type 6. [Pending] 容器实测:`BatchGenerate(112, $templateId)` → 验证座位级 SKU 生成 --- ## 已交付文件 | 文件 | 状态 | |------|------| | `shopxo/app/plugins/vr_ticket/service/BaseService.php` | ✅ 含 `initGoodsSpecs()` | | `shopxo/app/plugins/vr_ticket/service/SeatSkuService.php` | ✅ 新建,含 `BatchGenerate()` + `UpdateSessionSku()` | | `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` | ✅ submit() 已重构(FrontendDev) |