vr-shopxo-plugin/docs/15_FLAT_INVENTORY_QUERY_MAN...

6.2 KiB
Raw Permalink Blame History

扁平数据 + 查询管理器 方案

状态: 已完成
讨论日期2026-05-15
参与:西莉雅、大头


一、核心思想

后端做计算,前端做渲染。

  • 动态层级树:根据 group_by 参数动态生成层级结构
  • 查询管理器Query Manager:按 group_by 参数聚合,生成层级树
  • 自底向上聚合:每个层级节点自动计算 inventorymin_pricemax_pricehas_available
  • 座位嵌入:座位数据直接嵌入树的最深层 seats 属性
  • 模板去重池:同一 venue + room + section 的模板只返回一份,格式为键值对

二、关键设计决策

2.1 模板去重

原则:同一 venue + room + section 的座位图模板在所有场次下共享,与 session 无关。

例如:测试场馆 + 老展厅 1 + A
  → 多个场次 × 1 个模板 = 1 份模板数据
  → template_key = "测试场馆_老展厅 1_A"
  → 在 seat_templates 中只存 1 份,格式为键值对

2.2 层级顺序由前端控制

group_by=venue,session,room,section  →  场馆优先Joery 场景)
group_by=session,venue,room,section  →  场次优先
group_by=section,venue,session,room  →  自定义顺序

2.3 座位数据嵌入树

API 返回:
  ├── tree层级含 seats 嵌入在最深层)
  ├── seat_templates模板去重池键值对格式
  └── meta元数据

前端选座流程(全程零额外 API
  venue → session → room → section
       ↓
  直接从 tree 最深层获取 seats
       ↓
  查 template_key → 查 seat_templates → 渲染座位图

2.4 移除 flat_inventory

flat_inventory 已被移除,因为:

  • 座位数据已嵌入 tree 最深层
  • 前端直接从 seats 获取座位,无需额外遍历

三、API 接口

GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=tree
    ?goods_id=118
    &group_by=venue,session,room,section

返回结构(当前实现):

{
  "code": 0,
  "data": {
    "goods_id": 118,
    "group_by": ["venue", "session", "room", "section"],
    "tree": {
      "venues": {
        "测试场馆": {
          "name": "测试场馆",
          "min_price": 0,
          "max_price": 0,
          "has_available": true,
          "inventory": 31,
          "sessions": {
            "07:00-09:59": {
              "name": "07:00-09:59",
              "inventory": 31,
              "rooms": {
                "老展厅 1": {
                  "name": "老展厅 1",
                  "inventory": 16,
                  "sections": {
                    "A": {
                      "name": "A",
                      "min_price": 0,
                      "max_price": 0,
                      "has_available": true,
                      "inventory": 10,
                      "template_key": "测试场馆_老展厅 1_A",
                      "price": 0,
                      "seats": {
                        "1排1座": {
                          "spec_key": "$vr-分区=A|...",
                          "venue": "测试场馆",
                          "session": "07:00-09:59",
                          "room": "老展厅 1",
                          "section": "A",
                          "seat": "1排1座",
                          "price": 0,
                          "inventory": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "seat_templates": {
      "测试场馆_老展厅 1_A": {
        "template_key": "测试场馆_老展厅 1_A",
        "name": "测试场馆",
        "room_name": "老展厅 1",
        "section_name": "A",
        "seat_map": { ... },
        "layout_cols": 10,
        "layout_rows": 10
      }
    },
    "meta": {
      "seat_count": 59,
      "template_count": 6,
      "cache_hit": false,
      "computed_at": 1778861766
    }
  }
}

四、与现有系统衔接

vr_goods_config
  ↓
SeatSkuService::buildSeatSpecMap() → 扁平 SKU
  ↓
QueryManager新增
  ├── buildTree():按 group_by 聚合,嵌入 seats
  ├── buildTemplatePool():模板去重
  └── transformTemplatePool():转换为键值对格式
  ↓
缓存层TTL=60s
  ↓
API Response

现有组件不变

  • SeatMapService / SeatSkuService继续作为数据源
  • vr_seat_templates 表:继续存储座位模板
  • vr_goods_config继续作为商品配置

五、实现状态

任务 状态 说明
QueryManager 核心逻辑 已完成 buildTree, computeStatsRecursive
动态层级树生成 已完成 根据 group_by 动态构建
Seats 嵌入最深层 已完成 seats 属性嵌入 tree 最深层
自底向上统计 已完成 inventory, min_price, max_price, has_available
seat_templates 键值对 已完成 transformTemplatePool()
移除 flat_inventory 已完成 seats 已嵌入 tree
缓存机制 已完成 Cache::set/get + 标签失效
API 文档 已完成 docs/api/VR_TICKET_TREE_API.md
peer_goods 多场次关联 已完成 按 coding 关联同演出不同日期商品
session_meta 场次元数据 已完成 从 SKU extends 提取停售时间戳
SKU batch_expire_ts 写入 已完成 BatchGenerate 时写入 extends
BuyCheck 停售验证钩子 已完成 开场前5分钟禁止下单
字段修正 已完成 goods_code→coding, produce_date→batch_number_expire

六、注意事项

  1. template_key 生成算法venue_room_section,前端不感知算法
  2. 缓存失效:订单支付成功后同时清除 SeatMapService 缓存和 tree 缓存
  3. inventory=0 的座位:仍保留在 seats 中,用于前端标记"已售"状态
  4. 缓存 TTL:默认 60s通过 cache_ttl 参数可调整

七、相关文档

文档 说明
api/VR_TICKET_TREE_API.md 完整 API 文档
14_TREE_API_DESIGN.md Tree API 设计文档