# Plan: 幽灵 Spec 问题修复

> 日期：2026-04-20 | Issue: #15 + #16 | 状态：待实施

---

## 问题概览

| Issue | 问题 | 优先级 | 根因文件 |
|--------|------|--------|---------|
| #15 | save_thing_end 脏数据写回 | P1 | AdminGoodsSaveHandle.php |
| #16 | GetGoodsViewData 单模板模式 | P1 | SeatSkuService.php |

---

## Issue #15 — save_thing_end 脏数据写回

### 根因

`AdminGoodsSaveHandle.php` 第 60-65 行：DB 值优先（`$goodsRow['vr_goods_config']`），前端过滤后的值（`$data['vr_goods_config']`）只是 fallback。脏 config 块（含已删除场馆的 template_id）被写回 DB，幽灵 spec 累积。

### 修复计划

#### 步骤 1：调换读取优先级（主要修复）

**文件**：`shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php`
**行号**：第 60-65 行

```php
// 修改前
$rawConfig = is_array($goodsRow) ? ($goodsRow['vr_goods_config'] ?? '') : '';
if (empty($rawConfig)) {
    $rawConfig = $data['vr_goods_config'] ?? '';
}

// 修改后（主要修复）
// 前端已过滤无效 template_id，优先使用 data。若无前端数据再 fallback 到 DB
if (!empty($data['vr_goods_config'])) {
    $rawConfig = $data['vr_goods_config'];
} else {
    $rawConfig = is_array($goodsRow) ? ($goodsRow['vr_goods_config'] ?? '') : '';
}
```

**验证**：
- 提交后 `git diff` 确认读取顺序调换
- `git status` 确认只有 AdminGoodsSaveHandle.php 被修改

#### 步骤 2：无效 config 块移除（防御层）

**文件**：同上
**行号**：第 88-89 行

```diff
  if (empty($template)) {
-     continue;
+     unset($configs[$i]);  // 移除无效 config 块
+     continue;
  }
```

**行号**：第 145 行（`unset($config);` 之后）

```php
$configs = array_values($configs);  // 重排数组索引
```

**验证**：
- grep 确认 `unset($configs[$i])` 在第 89 行附近
- grep 确认 `array_values` 在 `unset($config)` 之后

#### 步骤 3：写回前判空（防御层）

**行号**：第 148 行之前

```diff
+ if (!empty($configs)) {
  Db::name('Goods')->where('id', $goodsId)->update([
      'vr_goods_config' => json_encode($configs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
  ]);
+ }
```

---

## Issue #16 — GetGoodsViewData 单模板模式

### 根因

`SeatSkuService.php` 第 368 行：只取 `$vrGoodsConfig[0]`（第一个配置块）。多模板商品时其余配置块被完全忽略；写回时只写 `[$config]` 单元素，可能覆盖其他有效配置块。

### 修复计划

#### 步骤 1：过滤有效配置块

**文件**：`shopxo/app/plugins/vr_ticket/service/SeatSkuService.php`
**行号**：第 365-373 行（在 `$config = $vrGoodsConfig[0];` 之前）

```php
// 过滤有效配置块
$validConfigs = [];
foreach ($vrGoodsConfig as $cfg) {
    $tid = intval($cfg['template_id'] ?? 0);
    if ($tid <= 0) continue;
    $tpl = Db::name(self::table('seat_templates'))->where('id', $tid)->find();
    if (!empty($tpl)) {
        $validConfigs[] = $cfg;
    }
}
if (empty($validConfigs)) {
    return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
}
$config = $validConfigs[0];  // 取第一个有效配置块用于前端展示
```

#### 步骤 2：修改写回逻辑

**行号**：第 386-388 行（原写回 `[$config]`）

```diff
- Db::name('Goods')->where('id', $goodsId)->update([
-     'vr_goods_config' => json_encode([$config], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
- ]);
+ Db::name('Goods')->where('id', $goodsId)->update([
+     'vr_goods_config' => json_encode($validConfigs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
+ ]);
```

**验证**：
- grep 确认 `validConfigs` 被用于写回
- grep 确认不再有 `[$config]` 写回模式

---

## 实施顺序

```
1. Issue #15（AdminGoodsSaveHandle.php）— 三步修改
2. Issue #16（SeatSkuService.php）— 两步修改
3. 验证：grep + git diff 确认修改正确
4. commit + push
```

---

## 禁止事项

- 不改其他文件（尤其是 Admin.php）
- 不改 Hook.php 或其他无关文件
- commit 前执行 `git status` 确认只有目标文件