243 lines
6.7 KiB
Markdown
243 lines
6.7 KiB
Markdown
|
|
# VR Ticket 插件 Phase 2 - 会话工作报告
|
|||
|
|
|
|||
|
|
**会话时间**: 2026-04-21
|
|||
|
|
**会话 ID**: 9ef6fb5b-c23e-477a-b139-339d172fe223
|
|||
|
|
**主要任务**: 修复购买提交流程 + 实现 4 维规格选择器
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、本次完成的工作
|
|||
|
|
|
|||
|
|
### 1.1 数据库层修复
|
|||
|
|
|
|||
|
|
#### 问题:错误的 `type` 字段
|
|||
|
|
- **原因**: 之前为了区分规格维度,尝试在 `vrt_goods_spec_value` 表添加了 `type` 字段
|
|||
|
|
- **用户要求**: 禁止修改数据库结构,必须使用现有字段
|
|||
|
|
- **解决方案**: 完全回滚 `type` 字段,通过 `GoodsSpecType.name` + 值匹配来确定维度
|
|||
|
|
|
|||
|
|
#### 最终方案
|
|||
|
|
```
|
|||
|
|
GoodsSpecType.name = "$vr-场次"
|
|||
|
|
GoodsSpecType.value = '[{"name":"08:00-23:59",...}]'
|
|||
|
|
|
|||
|
|
GoodsSpecValue.value = "08:00-23:59" // 通过值匹配确定属于哪个维度
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.2 后端修改 (SeatSkuService.php)
|
|||
|
|
|
|||
|
|
#### 修改 1: 移除 `BatchGenerate()` 中的 type 字段插入
|
|||
|
|
```php
|
|||
|
|
// 修复前(错误)
|
|||
|
|
$valueBatch[] = [
|
|||
|
|
'type' => self::SPEC_DIMS[$idx] ?? '', // ❌ 数据库没有 type 列
|
|||
|
|
'value' => (string)$specVal,
|
|||
|
|
...
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// 修复后(正确)
|
|||
|
|
$valueBatch[] = [
|
|||
|
|
'value' => (string)$specVal, // ✅ 只插入 value
|
|||
|
|
'md5_key' => md5((string)$specVal),
|
|||
|
|
'add_time' => $now,
|
|||
|
|
];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改 2: `buildSeatSpecMap()` - 通过值匹配确定维度
|
|||
|
|
```php
|
|||
|
|
// 从 GoodsSpecType 读取维度定义
|
|||
|
|
$specTypes = Db::name('GoodsSpecType')
|
|||
|
|
->where('goods_id', $goodsId)
|
|||
|
|
->order('id', 'asc')
|
|||
|
|
->select();
|
|||
|
|
|
|||
|
|
// 构建 name => [values] 映射
|
|||
|
|
$dimValuesByName = [];
|
|||
|
|
foreach ($specTypes as $type) {
|
|||
|
|
$values = json_decode($type['value'] ?? '[]', true);
|
|||
|
|
foreach ($values as $v) {
|
|||
|
|
if (isset($v['name'])) {
|
|||
|
|
$dimValuesByName[$type['name']][] = $v['name'];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 通过值匹配确定维度
|
|||
|
|
foreach ($specValues as $sv) {
|
|||
|
|
$value = $sv['value'];
|
|||
|
|
foreach ($dimValuesByName as $name => $values) {
|
|||
|
|
if (in_array($value, $values)) {
|
|||
|
|
// $value 属于维度 $name
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改 3: `GetGoodsViewData()` - 返回 `specTypeList`
|
|||
|
|
```php
|
|||
|
|
return [
|
|||
|
|
'vr_seat_template' => $seatTemplate ?: null,
|
|||
|
|
'goods_spec_data' => $goodsSpecData,
|
|||
|
|
'seatSpecMap' => $seatSpecMap,
|
|||
|
|
'specTypeList' => $specTypeList, // 新增:4维规格类型列表
|
|||
|
|
'goods_config' => $config,
|
|||
|
|
];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改 4: 修复缺失的 `buildSeatSpecMap()` 调用
|
|||
|
|
```php
|
|||
|
|
// ❌ 问题:函数定义了但从未调用
|
|||
|
|
private static function buildSeatSpecMap(...) { ... } // 第522行定义
|
|||
|
|
|
|||
|
|
// ✅ 修复:在 GetGoodsViewData 中调用
|
|||
|
|
$seatSpecMap = self::buildSeatSpecMap($goodsId, $seatTemplate);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.3 前端修改 (ticket_detail.html)
|
|||
|
|
|
|||
|
|
#### 新增:场馆和分区选择器
|
|||
|
|
```html
|
|||
|
|
<!-- HTML 容器 -->
|
|||
|
|
<div id="venueSelector"><!-- JS 动态渲染 --></div>
|
|||
|
|
<div id="sectionSelector"><!-- JS 动态渲染 --></div>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 新增:JavaScript 函数
|
|||
|
|
- `renderAllSelectors()` - 渲染场次/场馆/分区选择器
|
|||
|
|
- `selectVenue()` - 选择场馆
|
|||
|
|
- `selectSection()` - 选择分区
|
|||
|
|
- `filterSeats()` - 根据选择过滤座位
|
|||
|
|
|
|||
|
|
### 1.4 样式修复
|
|||
|
|
|
|||
|
|
#### 问题:CSS 不生效
|
|||
|
|
- **原因**: ShopXO 静态文件必须在 `public/plugins/` 目录,不在 `app/plugins/`
|
|||
|
|
- **解决**: 同步 CSS 到 `public/plugins/vr_ticket/static/css/ticket.css`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、关键经验教训
|
|||
|
|
|
|||
|
|
### 2.1 数据库设计原则
|
|||
|
|
| ❌ 错误做法 | ✅ 正确做法 |
|
|||
|
|
|-----------|-----------|
|
|||
|
|
| 添加 `type` 列来区分维度 | 使用 `GoodsSpecType.name` 区分 |
|
|||
|
|
| 依赖插入顺序匹配维度 | 通过值匹配确定维度 |
|
|||
|
|
| 修改数据库结构 | 适配现有数据库结构 |
|
|||
|
|
|
|||
|
|
### 2.2 ShopXO 插件开发规范
|
|||
|
|
```
|
|||
|
|
✅ 静态文件位置: public/plugins/vr_ticket/static/
|
|||
|
|
❌ 静态文件位置: app/plugins/vr_ticket/static/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重要**: 修改 `app/` 目录后,需要同步到 `public/` 目录才能生效!
|
|||
|
|
|
|||
|
|
### 2.3 代码调试经验
|
|||
|
|
1. **Undefined variable 错误**: 检查变量是否在使用前被赋值
|
|||
|
|
2. **CSS 不生效**: 检查浏览器缓存、文件路径、实际访问路径
|
|||
|
|
3. **数据库错误**: 确保 SQL 语句与实际表结构匹配
|
|||
|
|
|
|||
|
|
### 2.4 函数调用遗漏问题
|
|||
|
|
```php
|
|||
|
|
// ❌ 容易犯的错误:定义函数但不调用
|
|||
|
|
private static function buildSeatSpecMap(...) { ... }
|
|||
|
|
|
|||
|
|
// ✅ 正确做法:确保在正确的位置调用
|
|||
|
|
public static function GetGoodsViewData(...) {
|
|||
|
|
$seatSpecMap = self::buildSeatSpecMap(...); // 调用!
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、技术架构决策
|
|||
|
|
|
|||
|
|
### 3.1 规格维度识别方案
|
|||
|
|
```
|
|||
|
|
维度识别流程:
|
|||
|
|
GoodsSpecType.name → 确定有哪些维度(场馆、分区、座位号、场次)
|
|||
|
|
GoodsSpecType.value → 每个维度的可选值列表
|
|||
|
|
GoodsSpecValue.value → 具体 SKU 的值
|
|||
|
|
↓
|
|||
|
|
在 dimValuesByName 中匹配
|
|||
|
|
↓
|
|||
|
|
确定属于哪个维度
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 数据流
|
|||
|
|
```
|
|||
|
|
商品保存 → BatchGenerate()
|
|||
|
|
→ 生成 GoodsSpecBase (含 extends.seat_key)
|
|||
|
|
→ 生成 GoodsSpecValue (只有 value)
|
|||
|
|
→ 生成 GoodsSpecType (name + value 数组)
|
|||
|
|
|
|||
|
|
商品展示 → GetGoodsViewData()
|
|||
|
|
→ buildSeatSpecMap() (通过值匹配构建 seatSpecMap)
|
|||
|
|
→ 返回 specTypeList (前端选择器用)
|
|||
|
|
→ 返回 seatSpecMap (座位映射用)
|
|||
|
|
|
|||
|
|
用户选择 → filterSeats()
|
|||
|
|
→ 根据 currentSession/currentVenue/currentSection 过滤
|
|||
|
|
|
|||
|
|
用户提交 → submit()
|
|||
|
|
→ 从 seatSpecMap[key].spec 获取 4 维规格
|
|||
|
|
→ POST 提交完整规格数组
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、已删除的临时文件
|
|||
|
|
|
|||
|
|
| 文件 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `sql/fix_spec_value.sql` | 不再需要的 SQL 修复脚本 |
|
|||
|
|
| `sql/fix_spec_type.sql` | 不再需要的 SQL 修复脚本 |
|
|||
|
|
| `shopxo/app/plugins/vr_ticket/regenerate_spec.php` | 临时数据生成脚本 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、后续待办
|
|||
|
|
|
|||
|
|
### P0 (阻塞)
|
|||
|
|
- [ ] 完整购买流程测试
|
|||
|
|
- [ ] 验证 POST 提交 4 维 spec 数组
|
|||
|
|
|
|||
|
|
### P1
|
|||
|
|
- [ ] 场次/场馆/分区选择器联动过滤
|
|||
|
|
- [ ] 缩放时舞台跟随
|
|||
|
|
|
|||
|
|
### P2
|
|||
|
|
- [ ] 商品详情图片展示
|
|||
|
|
- [ ] 多场次支持
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、相关文档
|
|||
|
|
|
|||
|
|
| 文档 | 路径 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 完整开发计划 | `docs/FULL_PLAN.md` | Phase 2 完整方案 |
|
|||
|
|
| Agent 执行 Prompt | `docs/AGENT_PROMPT.md` | 执行指南 |
|
|||
|
|
| 阶段评估报告 | `docs/COUNCIL_PHASE2_ASSESSMENT_CORRECTED.md` | 之前的问题评估 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、Git 提交记录
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
fix: 移除 type 字段插入(数据库已回滚)
|
|||
|
|
fix: GetGoodsViewData 使用 GoodsSpecType.name 通过值匹配确定维度
|
|||
|
|
chore: 删除不再需要的 SQL 修复文件
|
|||
|
|
chore: 删除临时脚本
|
|||
|
|
feat: 添加场馆和分区选择器 + specTypeList 支持
|
|||
|
|
fix: 添加缺失的 buildSeatSpecMap() 调用
|
|||
|
|
fix: 优化规格选择器样式 - 处理长名称显示和添加 tooltip
|
|||
|
|
fix: CSS 文件路径 - 同步到 public/plugins/ 目录
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*报告生成时间: 2026-04-21T14:03:33+08:00*
|