Issue #15 — AdminGoodsSaveHandle.php: 1. 读取优先级调换:data['vr_goods_config'] 优先,DB 兜底(从源头避免脏数据) 2. 模板不存在时 unset($configs[$i]) 移除无效 config 块(防御层) 3. array_values 重排索引 + 写回前判空(防御层) Issue #16 — SeatSkuService.php GetGoodsViewData: 1. 多模板模式:遍历所有配置块过滤有效块 2. 模板不存在时清理无效块并写回有效配置(而非覆盖) 参考:reports/GHOST_SPEC_INVESTIGATION_REPORT.md 参考:docs/PLAN_GHOST_SPEC_FIX.mdcouncil/ProductManager
parent
44120a7e2c
commit
2311f17b90
|
|
@ -58,11 +58,11 @@ class AdminGoodsSaveHandle
|
||||||
|
|
||||||
if ($goodsId > 0 && ($data['item_type'] ?? '') === 'ticket') {
|
if ($goodsId > 0 && ($data['item_type'] ?? '') === 'ticket') {
|
||||||
// 直接从数据库读 vr_goods_config(全量查询,不加 field 限制,避免 ThinkPHP 软删除过滤导致查不到)
|
// 直接从数据库读 vr_goods_config(全量查询,不加 field 限制,避免 ThinkPHP 软删除过滤导致查不到)
|
||||||
$goodsRow = Db::name('Goods')->find($goodsId);
|
// 前端已过滤无效 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'] ?? '') : '';
|
$rawConfig = is_array($goodsRow) ? ($goodsRow['vr_goods_config'] ?? '') : '';
|
||||||
// 如果 DB 里没有( goodsRow 为空或 vr_goods_config 字段为空),fallback 到 params[data]
|
|
||||||
if (empty($rawConfig)) {
|
|
||||||
$rawConfig = $data['vr_goods_config'] ?? '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rawConfig)) {
|
if (!empty($rawConfig)) {
|
||||||
|
|
@ -83,9 +83,10 @@ class AdminGoodsSaveHandle
|
||||||
$template = Db::name('vr_seat_templates')->find($templateId);
|
$template = Db::name('vr_seat_templates')->find($templateId);
|
||||||
|
|
||||||
// 模板不存在时(硬删除场景):
|
// 模板不存在时(硬删除场景):
|
||||||
// - 跳过 snapshot 重建,保持 template_id=null 状态
|
// - 移除无效 config 块,避免脏数据写回
|
||||||
// - 前端下次打开时将看到选单为空,用户可重新选择或清空配置
|
// - 前端下次打开时将看到选单为空,用户可重新选择或清空配置
|
||||||
if (empty($template)) {
|
if (empty($template)) {
|
||||||
|
unset($configs[$i]); // 移除无效 config 块
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,11 +144,14 @@ class AdminGoodsSaveHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($config); // 解除引用,避免后续误改
|
unset($config); // 解除引用,避免后续误改
|
||||||
|
$configs = array_values($configs); // 重排数组索引
|
||||||
|
|
||||||
// 将填充后的完整 config 写回 goods 表
|
// 将填充后的完整 config 写回 goods 表(仅在有有效配置时)
|
||||||
|
if (!empty($configs)) {
|
||||||
Db::name('Goods')->where('id', $goodsId)->update([
|
Db::name('Goods')->where('id', $goodsId)->update([
|
||||||
'vr_goods_config' => json_encode($configs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
'vr_goods_config' => json_encode($configs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// a) 清空原生规格数据 —— 避免列偏移
|
// a) 清空原生规格数据 —— 避免列偏移
|
||||||
Db::name('GoodsSpecType')->where('goods_id', $goodsId)->delete();
|
Db::name('GoodsSpecType')->where('goods_id', $goodsId)->delete();
|
||||||
|
|
|
||||||
|
|
@ -365,8 +365,22 @@ class SeatSkuService extends BaseService
|
||||||
return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
|
return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取第一个配置块(单模板模式)
|
// 过滤有效配置块(多模板模式)
|
||||||
$config = $vrGoodsConfig[0];
|
$validConfigs = [];
|
||||||
|
foreach ($vrGoodsConfig as $cfg) {
|
||||||
|
$tid = intval($cfg['template_id'] ?? 0);
|
||||||
|
if ($tid <= 0) continue;
|
||||||
|
$tpl = \think\facade\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];
|
||||||
$templateId = intval($config['template_id'] ?? 0);
|
$templateId = intval($config['template_id'] ?? 0);
|
||||||
if ($templateId <= 0) {
|
if ($templateId <= 0) {
|
||||||
return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
|
return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
|
||||||
|
|
@ -377,20 +391,23 @@ class SeatSkuService extends BaseService
|
||||||
->where('id', $templateId)
|
->where('id', $templateId)
|
||||||
->find();
|
->find();
|
||||||
|
|
||||||
// 模板不存在时(硬删除场景):
|
// 模板不存在时(硬删除场景):清理无效配置块,写回有效配置
|
||||||
// - 将 template_id 置 null,让前端选单显示为空
|
|
||||||
// - 同时清掉 template_snapshot,下次保存时整块 config 干净地失效
|
|
||||||
if (empty($seatTemplate)) {
|
if (empty($seatTemplate)) {
|
||||||
$config['template_id'] = null;
|
$validConfigs = array_filter($validConfigs, function ($cfg) use ($templateId) {
|
||||||
$config['template_snapshot'] = null;
|
$tid = intval($cfg['template_id'] ?? 0);
|
||||||
|
if ($tid <= 0 || $tid === $templateId) return false;
|
||||||
|
$tpl = \think\facade\Db::name(self::table('seat_templates'))->where('id', $tid)->find();
|
||||||
|
return !empty($tpl);
|
||||||
|
});
|
||||||
|
$validConfigs = array_values($validConfigs);
|
||||||
|
if (!empty($validConfigs)) {
|
||||||
\think\facade\Db::name('Goods')->where('id', $goodsId)->update([
|
\think\facade\Db::name('Goods')->where('id', $goodsId)->update([
|
||||||
'vr_goods_config' => json_encode([$config], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
'vr_goods_config' => json_encode($validConfigs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||||
]);
|
]);
|
||||||
return [
|
} else {
|
||||||
'vr_seat_template' => null,
|
\think\facade\Db::name('Goods')->where('id', $goodsId)->update(['vr_goods_config' => '']);
|
||||||
'goods_spec_data' => [],
|
}
|
||||||
'goods_config' => $config,
|
return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解码 seat_map JSON(存储时是 JSON 字符串)
|
// 解码 seat_map JSON(存储时是 JSON 字符串)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue