From b9da3e6fb7c57bac914373450e66e457582fea39 Mon Sep 17 00:00:00 2001 From: Council Date: Sun, 19 Apr 2026 07:38:27 +0800 Subject: [PATCH] docs: add category_id empty bug analysis report (P0) --- docs/BUG_ANALYSIS_CATEGORY_ID_EMPTY.md | 203 +++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 docs/BUG_ANALYSIS_CATEGORY_ID_EMPTY.md diff --git a/docs/BUG_ANALYSIS_CATEGORY_ID_EMPTY.md b/docs/BUG_ANALYSIS_CATEGORY_ID_EMPTY.md new file mode 100644 index 0000000..8aa76e1 --- /dev/null +++ b/docs/BUG_ANALYSIS_CATEGORY_ID_EMPTY.md @@ -0,0 +1,203 @@ +# Bug 分析报告:商品编辑保存时 `category_id` 为空 + +**状态**:调研完成,待修复 +**严重性**:🔴 P0 — 所有票务/普通商品编辑后无法保存 +**影响范围**:vr-shopxo-plugin Phase 2 以降的所有商品编辑场景 +**调研人**:西莉雅 +**日期**:2026-04-19 + +--- + +## 一、现象描述 + +| 场景 | 结果 | +|------|------| +| 商品列表 → 点编辑 | 分类正常显示 ✅ | +| 编辑页手动选择分类 | UI 正常选中 ✅ | +| 点击保存 | 弹窗提示"至少需要选择一个商品分类" ❌ | +| 反复重试 | 每次都报错,即使选了多个分类也报错 | + +**关键特征**:分类在表单上显示正常(说明 DB 有数据、模板渲染正确),但保存时服务端收到的 `category_id` 为空。 + +--- + +## 二、数据流追踪 + +### 2.1 正常保存流程 + +``` +浏览器 POST 提交 + → category_id=911&category_id=912&... + → ThinkPHP input() 接收 + → GoodsService::GoodsSave($params) + → $category_ids = $params['category_id'] (转数组) + → ParamsChecked: empty($params['category_id']) → 【若为空则报错】 + → GoodsCategoryInsert($category_ids, $goods_id) (写入 goods_category_join) +``` + +### 2.2 关键代码节点现状 + +| 文件 | 路径 | 是否被 antigravity 修改 | +|------|------|------------------------| +| GoodsController | `app/admin/controller/Goods.php` | ❌ 未改动 | +| GoodsService | `app/service/GoodsService.php` | ❌ 未改动 | +| saveinfo.html | `app/admin/view/default/goods/saveinfo.html` | ❌ 未改动 | +| common.js (验证逻辑) | `public/static/common/js/common.js` | ❌ 未改动 | +| AdminGoodsSave.php | `app/plugins/vr_ticket/hook/AdminGoodsSave.php` | ✅ 新引入 | +| AdminGoodsSaveHandle.php | `app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php` | ✅ 新引入 | +| config.json | `app/plugins/vr_ticket/config.json` | ✅ 新增 hooks 注册 | + +**结论**:Bug 不在 ShopXO Core,也不在 AdminGoodsSaveHandle(该钩子只处理 `item_type`,不碰 `category_id`)。问题指向 `AdminGoodsSave.php`。 + +--- + +## 三、根因分析 + +### 3.1 `AdminGoodsSave.php` 的注入内容 + +```php +// GoodsService::SaveInfo() 中调用 +$assign[$hook_name.'_data'] = MyEventTrigger($hook_name, [ + 'hook_name' => 'plugins_view_admin_goods_save', + 'data' => &$data, // 商品数据(含 category_ids) + 'params' => &$params, +]); +// 模板中 {{$hook|raw}} 将其输出到表单底部 +``` + +`AdminGoodsSave.php` 的 `handle()` 返回一段 HTML,**直接嵌入商品编辑表单底部**: + +```html +
+ + + + + +
+``` + +### 3.2 冲突机制(推断) + +**时序**: + +``` +1. PHP 渲染表单 → ` 的 `option.selected` 属性 + 自定义 UI。浏览器 layout 抖动会导致 chosen-select 的内部缓存与原生 DOM 状态短暂失去同步。 + +3. **ShopXO 表单提交时的取值路径** + ```javascript + // common.js FromInit() 提交验证 + $temp_form.find('select.chosen-select').each(function() { + var value = $(this).val(); // ← 这里返回 null 或空数组 + if ((value || null) == null) { + is_success = false; // ← 客户端验证失败 + } + }); + ``` + 如果 `$(category_select).val()` 返回 `null`(jQuery 的 `val()` 读的是原生 `