# VR-ShopXO-Plugin 删除逻辑审查报告

**审查时间:** 2026-04-20
**审查人:** 西莉雅审查助手
**审查文件:**
- `shopxo/app/plugins/vr_ticket/admin/Admin.php`
- `shopxo/app/plugins/vr_ticket/view/venue/list.html`

---

## 一、发现的问题列表

### 🔴 问题 1：`SeatTemplateDelete` 中使用了不存在的 `is_delete` 列（严重）

| 属性 | 值 |
|------|-----|
| **文件** | `Admin.php` |
| **方法** | `SeatTemplateDelete` (第 227 行) |
| **行号** | 第 249-251 行 |
| **问题** | Goods 查询使用 `is_delete = 0`，但 ShopXO 的 Goods 表使用 `is_delete_time` 进行软删除判断 |

```php
// ❌ 错误代码（第 249-251 行）
$goods = \think\facade\Db::name('Goods')
    ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
    ->where('is_delete', 0)   // ← ShopXO 没有 is_delete 列！
    ->find();
```

**错误信息:**
```
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'is_delete' in 'where clause'
```

**根本原因:** 复制粘贴自 ShopXO 原型代码时遗留的错误字段名。ShopXO 的商品软删除使用 `is_delete_time`（时间戳），`is_delete` 列根本不存在。

---

### 🔴 问题 2：`VenueDelete` 中同样使用了不存在的 `is_delete` 列

| 属性 | 值 |
|------|-----|
| **文件** | `Admin.php` |
| **方法** | `VenueDelete` (第 857 行) |
| **行号** | 第 885-889 行 |
| **问题** | 与 `SeatTemplateDelete` 相同的问题 |

```php
// ❌ 错误代码（第 885-889 行）
$goods = \think\facade\Db::name('Goods')
    ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
    ->where('is_delete', 0)   // ← 同样错误！
    ->find();
```

---

### 🟡 问题 3：`list.html` 残留了旧的 custom JS 删除弹窗代码

| 属性 | 值 |
|------|-----|
| **文件** | `view/venue/list.html` |
| **行号** | 第 137-168 |
| **问题** | 删除按钮已改用 `submit-ajax`（正确），但旧的 custom JS 弹窗和 `.btn-open-delete-confirm` 处理器仍残留，造成代码污染 |

删除按钮已正确更新为：
```html
<!-- ✅ 正确：已使用 submit-ajax -->
<button class="am-btn am-btn-danger am-btn-xs am-radius am-margin-left-xs submit-ajax"
    data-url="{{:PluginsAdminUrl('vr_ticket', 'admin', 'VenueDelete')}}"
    data-id="{{$v.id}}"
    data-value="hard"
    data-view="reload"
    data-msg="确定要删除此场馆？删除后关联商品的场馆信息将被自动清除。">
    <i class="am-icon-trash-o"></i> 删除
</button>
```

但页面底部仍残留：
- `#venue-confirm-delete-modal` 弹窗 HTML（第 137-151 行）
- `.btn-open-delete-confirm` 点击处理器（第 155-158 行）
- `.btn-do-real-delete` 弹窗确认处理器（第 160-175 行）

---

### 🟢 确认正常：方法命名问题（非 bug）

| 观察 | 说明 |
|------|-----|
| `vr_seat_templates` 表 | 实际上存储的就是"场馆/场地模板"数据，不是"座位模板" |
| `VenueDelete` / `SeatTemplateDelete` | 两个方法名不同，但都操作同一张表 `vr_seat_templates` |
| **结论** | 表名和方法名的命名不一致是历史遗留的设计问题，但不影响功能 |

---

## 二、根本原因分析

### 为什么会发生？

1. **复制粘贴错误** — 两个方法从 ShopXO 原型代码复制过来时，`Goods` 表的软删除字段用了错误的名称 `is_delete`，而 ShopXO 实际使用 `is_delete_time`
2. **残留代码未清理** — 前端删除按钮从 custom JS 方式迁移到 `submit-ajax` 后，旧的弹窗 HTML 和 JS 事件处理器没有一起移除
3. **缺少 Code Review** — 没有在 PR 阶段发现字段名错误

### 软删除 vs 硬删除逻辑梳理

| 方法 | 软删除触发条件 | 硬删除触发条件 | 表 |
|------|---------------|---------------|-----|
| `SeatTemplateDelete` | `hard_delete=0`（默认） | `hard_delete=1` 或 `value=hard` | `vr_seat_templates` |
| `VenueDelete` | `hard_delete=0`（默认） | `hard_delete=1` 或 `value=hard` | `vr_seat_templates` |
| `VerifierDelete` | **仅软删除**（无硬删除选项） | 无 | `vr_verifiers` |

**注意:** `VerifierDelete` 只有软删除（设置 `status=0`），这是正确的业务设计。

---

## 三、修复代码（diff 格式）

### 修复 1：`SeatTemplateDelete` 的 `is_delete` 错误

**文件:** `Admin.php` 第 249-251 行

```diff
         if ($hardDelete) {
-            $goods = \think\facade\Db::name('Goods')
-                ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
-                ->where('is_delete', 0)
+            // 检查是否有关联商品（ShopXO 使用 is_delete_time 做软删除判断）
+            $goods = \think\facade\Db::name('Goods')
+                ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
+                ->where('is_delete_time', 0)
                 ->find();
```

### 修复 2：`VenueDelete` 的 `is_delete` 错误

**文件:** `Admin.php` 第 885-889 行

```diff
         if ($hardDelete) {
-            $goods = \think\facade\Db::name('Goods')
-                ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
-                ->where('is_delete', 0)
+            // 检查是否有关联商品（ShopXO 使用 is_delete_time 做软删除判断）
+            $goods = \think\facade\Db::name('Goods')
+                ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
+                ->where('is_delete_time', 0)
                 ->find();
```

### 修复 3：清理 `list.html` 残留的 old custom JS 代码

**文件:** `view/venue/list.html` 第 137-175 行

删除以下残留代码块：

```diff
-        <!-- 全局删除确认弹窗（单个，供所有行共用） -->
-        <div class="am-modal am-modal-confirm" id="venue-confirm-delete-modal">
-            <div class="am-modal-dialog">
-                ...
-            </div>
-        </div>
-
-        <script>
-        $(function() {
-            // 删除按钮：打开弹窗并记录当前行 ID
-            $(document).on('click', '.btn-open-delete-confirm', function() {
-                ...
-            });
-            // 弹窗确认删除：构造 hard_delete=1 参数并提交
-            $(document).on('click', '.btn-do-real-delete', function() {
-                ...
-            });
-        });
-        </script>
```

---

## 四、验证步骤

### Step 1: 本地验证

```bash
# 1. 应用修复
# - Admin.php 第 249-251 行：is_delete → is_delete_time
# - Admin.php 第 885-889 行：is_delete → is_delete_time
# - list.html 删除残留的 modal 和 JS 代码

# 2. 清除 ShopXO 缓存
cd /path/to/shopxo
php think clear
```

### Step 2: 功能测试

**测试用例 1：场馆硬删除（有关联商品）**
1. 创建一个商品，关联到某场馆
2. 进入场馆列表，点击"删除"按钮
3. 确认弹窗出现，点击"确认删除"
4. **预期:** 删除成功，页面 reload，商品 `vr_goods_config` 中的场馆信息被清除
5. **验证 SQL 无报错**

**测试用例 2：场馆硬删除（无关联商品）**
1. 找一个没有任何商品关联的场馆
2. 点击删除 → 确认
3. **预期:** 删除成功，场馆记录从表中移除

**测试用例 3：场馆软删除（禁用）**
1. 点击"禁用"按钮
2. **预期:** 场馆 `status` 变为 0，页面 reload 后显示"禁用"状态
3. **验证:** 禁用后，删除按钮仍然显示（不再因条件判断消失）

**测试用例 4：场馆启用**
1. 在已禁用的场馆行点击"启用"
2. **预期:** `status` 变为 1，页面 reload

**测试用例 5：Verifier 删除**
1. 进入核销员列表，点击某核销员的删除
2. **预期:** 软删除，`status` 变为 0，无 SQL 报错

### Step 3: 数据库验证

```sql
-- 验证硬删除后商品表中的 vr_goods_config 已被清理
SELECT id, name, vr_goods_config FROM vrt_goods WHERE vr_goods_config LIKE '%template_id":X%';

-- 验证场馆软删除后 status 正确
SELECT id, name, status FROM vrt_vr_seat_templates WHERE id = X;
```

---

## 五、修复状态汇总

| # | 问题 | 严重度 | 状态 |
|---|------|--------|------|
| 1 | `SeatTemplateDelete` 使用错误的 `is_delete` 列 | 🔴 严重 | **待修复** |
| 2 | `VenueDelete` 使用错误的 `is_delete` 列 | 🔴 严重 | **待修复** |
| 3 | `list.html` 残留 old custom JS 代码 | 🟡 轻微 | **待清理** |

**注意:** 大头通过 antigravity 手动修复了前端删除按钮和 `VenueDelete` 的 `value=hard` 参数支持，这些部分已正确。但 `is_delete` → `is_delete_time` 的修复仍需应用。

---

## 六、完整修复后代码对照

### `SeatTemplateDelete` 硬删除块（修复后）

```php
if ($hardDelete) {
    // 检查是否有关联商品（ShopXO 使用 is_delete_time 做软删除判断）
    $goods = \think\facade\Db::name('Goods')
        ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
        ->where('is_delete_time', 0)
        ->find();
    \think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
    // ... audit log
}
```

### `VenueDelete` 硬删除块（修复后）

```php
if ($hardDelete) {
    // 检查是否有关联商品（ShopXO 使用 is_delete_time 做软删除判断）
    $goods = \think\facade\Db::name('Goods')
        ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
        ->where('is_delete_time', 0)
        ->find();
    \think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
    // ... audit log
}
```
