vr-shopxo-plugin/docs/DEVELOPMENT_LOG.md

21 KiB
Raw Blame History

VR 票务插件开发日志

vr-shopxo-plugin 项目全量记录 仓库:http://xmhome.ow-my.com:3000/sileya-ai/vr-shopxo-plugin 最后更新2026-04-15


一、项目背景与决策

1.1 需求来源2026-04-13

大头受朋友委托,为其合作伙伴调研轻量级商城小程序解决方案。

核心需求:

  • 订单:外卖配送 / 包邮 / 自提
  • 会员:充值、积分、优惠券
  • 约束:无程序员/无前端/无后端,要求直接可用,后期能用 AI 改动,架构清晰,部署简单

调研结论4 个方案对比):

项目 Stars 功能 部署 会员体系 AI友好 综合
ShopXO 8.5k 9/10
Bagisto 14k 5/10
Saleor 22.4k 5/10
Medusa 23k+ 4/10

ShopXO 断层第一,原因:功能完整 + 虚拟主机可部署 + 有 uni-app 前端配套 + MIT 协议可商用。

票务插件定位: 票务 = ShopXO 商品类型扩展。item_type = 'ticket' 时走插件逻辑处理座位图/场次/QR票。

1.2 技术栈决策

技术选型 说明
商城底座 ShopXO v6.8.0 ThinkPHP 8虚拟主机可部署
前端 uni-app 微信小程序 + H5
票务插件 PHP 原生 插件机制 + Hook 系统
票务详情页 独立 HTML 模板 完全独立 UI绕过 ShopXO 主题限制
数据库 MySQL与 ShopXO 共用) 表前缀 vrt_
QR 票 AES 加密 防伪造
核销 扫码枪 + RLS B 端小程序扫码核销

核心原则(已固化):

怎么快怎么来,怎么方便怎么来,少改动少复杂度,完全允许改 ShopXO 核心代码(自己部署)。


二、技术调研2026-04-13 白天)

2.1 ShopXO 插件机制调研

调研文件:

  • docs/07_SHOPXO_PLUGIN_MECHANISM.md — 插件开发机制完整手册
  • docs/08_SHOPXO_REQUIREMENTS_MAPPING.md — 票务需求 → ShopXO 机制对照矩阵
  • docs/09_SHOPXO_HOOKS_REFERENCE.md — 100+ 钩子清单

插件核心机制:

  1. config.json — 插件元数据(名称/版本/依赖/菜单/权限/静态资源)
  2. BaseService — 插件业务服务基类GetDb / 参数校验 / 日志)
  3. EventListener.php — 生命周期钩子Install/Uninstall/Upgrade/Index
  4. URL 路由 — 后台控制器 plugins_admin 前缀,前台 plugins 前缀
  5. 视图 — admin/view/default/plugins_admin/ + view/default/plugins/ 目录

关键发现(票务用途):

钩子 用途
plugins_service_order_pay_success_handle_end 支付成功 → 生成 QR 票
plugins_view_goods_detail_base_sku_top 商品详情页顶部(选座 UI
plugins_view_user_various_inside_top 用户中心(票夹)
plugins_service_goods_delete_end 商品删除 → 清理票务数据
plugins_admin_goods_info_init_end 后台商品编辑 → 加载票务字段

2.2 票务详情页方案抉择

方案 AURL 劫持 放弃

  • 缺点:无法继承商品详情页基础样式,改动 ShopXO 核心代码量大

方案 BCSS 隐藏标准 SKU 放弃

  • 缺点Hook 链过长,不可控

方案 C插件模板替换 不可行

  • 调研结论MyView() 源码确认 ShopXO 插件系统是纯钩子系统config.json 无权覆盖 Goods 控制器模板路径goods/detail.html 写死在控制器里

方案 D最终Goods.php 1 行判断

  • 在 app/index/controller/Goods.php 的 return MyView(); 前插入判断
  • item_type == 'ticket' → 加载插件模板路径 + 预查询座位模板数据
  • 改动量1 行条件判断 + ~10 行数据注入
  • 实测验证:浏览器访问商品详情页,座位图渲染正常

三、ShopXO 环境配置2026-04-15 凌晨)

3.1 Docker 环境

服务 端口 说明
shopxo-web :10000 Nginx 前端
shopxo-mysql :10001 MySQL 8.0
shopxo-php :9000 PHP-FPM

3.2 关键配置

  • 后台入口:adminufgeyw.php安装时随机生成6位字符串
  • 表前缀:vrt_
  • 数据库名:vrticket
  • 数据库凭证root=shopxo_root_2024 / user=shopxo_user / pass=shopxo_pass_2024
  • 源码路径:/Users/bigemon/WorkSpace/vr-shopxo-plugin/shopxo/
  • is_developtrueconfig/shopxo.php 第41行
  • 自定义侧边栏配置:config/vrt_custom_menu.php(菜单入口,不依赖插件系统)

3.3 自定义侧边栏快速入口2026-04-16

ShopXO 后台 sidebar 菜单通过 AdminPowerService.phpAdminPowerMenuData() 生成(第 598 行 return 前)。

配置文件: shopxo/config/vrt_custom_menu.php

机制: 直接 include 配置文件,遍历 $custom_menu_config['menus'],追加到 $admin_left_menu[]。菜单项的 icon 字段只填图标名(无需 iconfont 前缀,模板会自动加)。

图标来源: shopxo/public/static/common/iconfont/iconfont.css,选 534 个中的任意 .icon-xxx:before 名称。

示例配置:

return [
    'menus' => [
        [
            'id'       => 'my-test-plugin-menu',
            'name'     => '我的测试插件',
            'icon'     => 'icon-label',   // 模板自动渲染为 iconfont icon-label
            'url'      => '/adminufgeyw.php?s=plugins/index/pluginsname/my_test_plugin/pluginscontrol/admin/pluginsaction/index.html',
            'control'  => 'plugins',
            'action'   => 'index',
            'is_show'  => 1,
        ],
    ],
];

生效方式: 修改配置文件后,重启 PHP 容器即可(docker restart shopxo-php)。

3.3 后台权限修复

admin 用户role_id=1默认缺少插件权限。手动写入 38 条权限到 vrt_role_power

  • 应用管理链路340 / 341 及子权限 342-591

3.4 模板目录冲突

ThinkPHP 路由用 plugins_admin下划线格式但实际目录为 pluginsadmin无下划线。通过创建符号链接解决。


四、Phase 0插件骨架2026-04-15 04:36 起)

4.1 完成内容

数据库建表(手动 SQL

-- 座位模板 CREATE TABLE vrt_vr_seat_templates ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT '模板名称', category_id INT DEFAULT 0 COMMENT '绑定分类ID', spec_base JSON COMMENT '座位规格基数据', qr_data VARCHAR(64) NOT NULL COMMENT 'QR数据前缀', is_enable TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

-- 电子票 CREATE TABLE vrt_vr_tickets ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, order_no VARCHAR(64), goods_id INT NOT NULL, user_id INT NOT NULL, qr_code TEXT NOT NULL COMMENT 'AES加密QR数据', status ENUM('pending','active','used','cancelled') DEFAULT 'pending', qr_data VARCHAR(128), seat_label VARCHAR(32), verified_at DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

-- 核销员 CREATE TABLE vrt_vr_verifiers ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, name VARCHAR(50), status TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

-- 核销记录 CREATE TABLE vrt_vr_verifications ( id INT PRIMARY KEY AUTO_INCREMENT, ticket_id INT NOT NULL, verifier_id INT NOT NULL, verified_at DATETIME DEFAULT CURRENT_TIMESTAMP, location VARCHAR(200) );

插件文件结构:

app/plugins/vr_ticket/ ├── plugin.json # 插件配置3个子菜单 ├── EventListener.php # 生命周期Install建表/Uninstall/Upgrade ├── service/ │ ├── BaseService.php # 工具AES/QrData/UUID/日志) │ ├── TicketService.php # onOrderPaid/verifyTicket/getUserTickets │ └── SeatTemplateService.php ├── admin/ │ ├── controller/ │ │ ├── SeatTemplate.php # 座位模板 CRUD │ │ ├── Ticket.php # 电子票列表+详情+导出 │ │ ├── Verifier.php # 核销员管理 │ │ └── Verification.php # 核销记录 │ └── view/default/ # Layui 列表页 └── view/goods/ └── ticket_detail.html # 前端票务详情页独立UI

测试数据:

  • 商品 ID 112VR演唱会电子票 2024item_type=ticket
  • 分类 ID 911VR演唱会
  • 座位模板 ID 1Bird Nest - Zone A绑定 category_id=911
  • 3排座位A排AAAAAA红色VIP区/ B排BBBBBB蓝色看台区/ C排CCCCCC绿色普通区

五、Phase 1Goods.php 改法 + 前端验证2026-04-15 白天)

5.1 修改内容

文件app/index/controller/Goods.php 位置detail() 方法return MyView(); 前约第 137-139 行

代码改动:

// --- VR 票务处理 start --- $goods = $result['data']['goods']; if (!empty($goods['item_type']) && $goods['item_type'] === 'ticket') { // 加载座位模板 $spec_base = Db::table('vr_seat_templates') ->where('category_id', $goods['category_id']) ->where('is_enable', 1) ->find(); $goods['vr_seat_template'] = $spec_base; // 加载 goods_spec_data座位动态价格 $goods_spec_data = empty($goods['spec_base']) ? [] : json_decode($goods['spec_base'], true); $goods['vr_spec_data'] = $goods_spec_data; // 使用票务专用模板 $this->set_title($goods['title'].' - VR电子票'); return MyView('public/../../../plugins/vr_ticket/view/goods/ticket_detail', [ 'common' => $common, 'header' => $header, 'goods' => $goods, ]); } // --- VR 票务处理 end ---

5.2 前端票务详情页渲染结果

URLhttp://localhost:10000/?s=index/goods/index/id/1商品1改为 item_type=ticket 测试)

渲染效果:

  • 舞台(舞 台)
  • 座位图三行A排AAAAAA红色VIP区/ B排BBBBBB蓝色看台区/ C排CCCCCC绿色普通区
  • 图例VIP区 / 看台 / 普通)
  • 选座 UI已选座位计数 + 合计价格)
  • 场次选择
  • 观演人表单(姓名+手机号)

六、Council 审议记录2026-04-14

6.1 Architect Round 1已合并

评审结论Q2+Q4

  • Q2spec 座位共用 vs 独立 → 确认方案
  • Q4spec 复用粒度 → 确认粒度

6.2 PM Round 2已合并

  • 解决 plan.md 合并冲突

6.3 待 Council 审议的遗留问题

问题 状态 说明
Q2spec座位共用vs独立 已解决 见 ARCHITECTURE.md
Q3观演人存储位置 待 Council 尚未最终确认
Q4spec复用粒度 已解决 见 ARCHITECTURE.md

七、关键决策固化

决策 结论 备注
改 ShopXO 核心可以吗 可以,自己部署 原则已写入 README
票务详情页方案 Goods.php 1行判断 → ticket_detail.html 已验证
spec = 场次 确认 无需 vr_sessions 表
座位模板绑定分类 确认 Q1 已解决
item_type 字段 ticket / normal 触发票务逻辑开关
座位图渲染 HTML Table + CSS Grid 不依赖第三方库
QR 安全 AES_Encrypt 防伪造
AI 介入程度 90%+ 模板/Hook/PHP/Vue 均为标准技术

八、当前状态快照2026-04-15 09:00 CST

8.1 Git Commit 历史

7508bed docs: 追加 vr-shopxo-plugin Phase 0/1 状态记录 0f5a82d feat(Phase 1): ShopXO Goods.php 修改(实际验证通过) 34f7045 feat(Phase 0): vr_ticket plugin skeleton complete d5edb76 docs: add guiding principle + Goods.php modification guide 1c6d32b docs: add ShopXO hooks reference (v6.8.0) - extracted from source e7b7bf9 docs: add plugin mechanism + requirements mapping docs 536ef9e docs: add 项目启动报告 REPORT-KICKOFF.md (issue #5) 8c6878e council(draft): Architect - 合并 Round 1 架构评审结论 9eae259 council(draft): Architect - Round 1 架构评审结论 (Q2+Q4)

8.2 Phase 完成度

Phase 状态 说明
Phase 0骨架 完成 14个文件4张表插件已注册
Phase 1前端票务详情页 完成 Goods.php验证通过座位图渲染正常
Phase 2后台管理页面 待开始 场次管理/座位管理/票务订单列表
Phase 3支付回调 + 发票 待开始 钩子联调 + QR 票生成
Phase 4B端扫码核销 待开始 核销员管理 + 扫码 API

8.3 关键文件路径

ShopXO 容器: 源码:~/.openclaw/workspace/council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src/ 插件shopxo-src/app/plugins/vr_ticket/ Goods.phpshopxo-src/app/index/controller/Goods.php

vr-shopxo-plugin 仓库: 插件代码app/plugins/vr_ticket/ ShopXO 修改shopxo-modifications/app/index/controller/Goods.php 文档docs/


九、下一步计划

Phase 2后台管理页面

  1. 座位模板管理admin/controller/SeatTemplate.php

    • Layui 列表页(已生成 view
    • 创建/编辑/删除操作
  2. 电子票管理admin/controller/Ticket.php

    • 票列表(支持按订单号/手机号搜索)
    • 票详情(显示 QR 码)
    • 导出功能
  3. 核销员管理admin/controller/Verifier.php

    • 增删改查
  4. 核销记录admin/controller/Verification.php

    • 核销历史列表

Phase 3支付回调 + QR 票生成

  1. 实现 TicketService::onOrderPaid() → 支付成功时生成票
  2. Hookplugins_service_order_pay_success_handle_end
  3. AES 加密 QR 数据
  4. ShopXO 站内通知或 Realtime 推送

Phase 4B 端扫码核销

  1. 核销 APIB 端小程序调用)

    • POST /api/ticket/verify扫码枪调用
    • RLS 策略profiles.role = 'staff' 可核销
  2. 核销员注册

    • 后台添加核销员(手机号)
    • 绑定 user_id

十、已知问题与待验证项

问题 优先级 状态
后台插件菜单无权限 P1 admin 已有 340/341vr_ticket 控制器权限未单独分配
观演人存储位置Q3 P2 待 Council 审议
spec_base JSON 结构最终版 P2 已确认 Q4 方案,待落地
支付回调联调 P2 等待 Phase 2 后台完成后测试
核销 API RLS P2 待实现

十一、Phase 2 前台展示层完成2026-04-20

11.1 完成内容

Commit 7bd896764

  • Goods.phpitem_type=ticket → 绝对路径 View::fetch(ticket_detail.html) + 数据注入
  • SeatSkuService.php:新增 GetGoodsViewData(),从 goods.vr_goods_config JSON + ShopXO 原生平表读取座位图+场次
  • TicketService.phponOrderPaid 改用 sxo_order_detail + JSON spec 解析座位号,幂等改为 seat_info

docs/14 修正:

  • 修正了数据流描述vrt_vr_goods_config → goods.vr_goods_config
  • 修正了表名vrt_order_detail → sxo_order_detail
  • 删除了错误的 Think 驱动修改说明

新文档:

  • docs/PHASE2_PLAN.md本文件Phase 2 当前状态 + 下一步计划

11.2 模板渲染问题说明

Goods.php 绝对路径方案已实现,但 {include file="public/head"} 标签是否能在容器内正确解析待实测

详见 docs/PHASE2_PLAN.md 第二章。

11.3 当前 Git 状态

7bd896764 feat(Phase 2): 完成票务商品前端展示层  ← HEAD
dc63cff77 chore: clean up my_test_plugin residual hooks

11.4 Phase 2 剩余工作

任务 状态
模板渲染实测(容器内) ⚠️ 待大头操作
loadSoldSeats() 实现 未开始
vr_ticket Hook.php 补充 未开始
4 个后台控制器联调 未开始
核销 API 未开始

11.5 清理记录2026-04-20

  • shopxo/test_ticket.php → 移至 _backup_20260420/test_ticket.php(临时测试脚本,不入仓库)
  • docs/14_TEMPLATE_RENDER_INVESTIGATION.md → 重写修正版(删除错误信息,保留调查价值)
  • 核心代码Goods.php / SeatSkuService.php / TicketService.php→ 全部提交推送

十二、模板渲染修复 + JSON 格式升级2026-04-20 白天)

12.1 模板渲染修复v2.0 路线 B

问题ThinkTemplate 的 {include file="public/head"} 标签在 Linux 下因 view_depr=/ 导致路径拼接错误,页面以纯文本输出。

解决方案(路线 B

  1. {include} / {:} ThinkTemplate 标签 → <?php echo ModuleInclude(...) ?> 原生 PHP
  2. {$var|default='...'}<?php echo $var ?? '...' ?>
  3. {json_decode(...)|raw}<?php echo json_encode(...) ?>
  4. 复制 ShopXO app/index/view/default/public/plugins/vr_ticket/view/goods/public/

提交记录

349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
c894e7018 fix: 复制 ShopXO public 模板 + 修复 footer_page 不存在问题
1b0ac3276 fix: 替换为票务专用精简 footer449行→53行

渲染结果 商品详情页正常渲染,但场次为空(待适配新 JSON 格式)。

12.2 vr_goods_config JSON 格式重新设计(重大变更)

背景:大头 + Gemini 重新设计了 vr_goods_config 规格,从依赖 vr_seat_templates 表实时查询,改为商品发布时快照模式。

新格式核心

  • goods.vr_goods_config 包含完整的 rooms[] 快照(座位图+sections+seats
  • 不再需要实时查 vr_seat_templates
  • spec_base_id_map 格式:{room_id}_{row}_{colNum}spec_base_id

设计原则

  • 商品发布时快照 → 已发布商品与 vr_seat_templates 解耦
  • 修改模板不影响已发布商品 → 绝对一致性
  • SKU 和 config 一起过时、一起更新

新文档

  • docs/VR_GOODS_CONFIG_SPEC.md — 新 JSON 格式完整规格说明(已确认)
  • docs/PHASE2_PLAN.md v2.0 — 同步更新,下一步工作计划

12.3 当前 Git 状态

1b0ac3276 fix: 替换为票务专用精简 footer  ← HEAD
c894e7018 fix: 复制 ShopXO public 模板
349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
7bd896764 feat(Phase 2): 完成票务商品前端展示层

12.4 接下来需要实现

任务 负责人 依赖
重写 GetGoodsViewData() 适配新格式 待定 VR_GOODS_CONFIG_SPEC.md 已确认
更新 ticket_detail.html JSrooms[] 结构) 待定 GetGoodsViewData() 输出确定后
AdminGoodsSaveHandle SKU 生成 待定 新格式已确认
loadSoldSeats() 实现 待定 vr_tickets 有数据后

十三、模板渲染修复 + vr_goods_config v3.0 格式确认2026-04-20 上午)

13.1 模板渲染修复

问题ThinkTemplate 的 {include file="public/head"} 在 Linux 下因 view_depr=/ 导致路径拼接错误。

方案(路线 BThinkTemplate → PHP ModuleInclude

  • {include file=...}<?php echo ModuleInclude(...) ?>
  • {:Config()} / {:IsMobileLogin()}<?php echo Config() ?> / <?php echo IsMobileLogin() ?>
  • {$var|default='...'}<?php echo $var ?? '...' ?>
  • {if}...{/if}<?php if():?>...<?php endif;?>

复制 ShopXO app/index/view/default/public/plugins/vr_ticket/view/goods/public/

提交记录

349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
c894e7018 fix: 复制 ShopXO public 模板 + 修复 footer_page 不存在问题
1b0ac3276 fix: 替换为票务专用精简 footer449行→53行

13.2 vr_goods_config JSON 格式 v3.0 最终确认

变更历程

  • v2.0rooms 直接嵌入,但 selected_sections 格式不确定
  • v3.0(最终):增加 template_snapshot 字段selected_sections 确认为对象格式

最终 v3.0 结构

{
  "version": 3.0,
  "template_id": 4,
  "selected_rooms": ["room_id_xxx"],
  "selected_sections": { "room_id_xxx": ["A", "B"] },
  "sessions": [{ "start": "15:00", "end": "16:59" }],
  "template_snapshot": {
    "venue": { ... },
    "rooms": [{ "id": "...", "map": [...], "sections": [...], "seats": {...} }]
  }
}

核心设计决策

  • template_id:发布/编辑时读取最新 vr_seat_templates 的依据
  • template_snapshot:发布时从 vr_seat_templates 读取并存储的快照,前端渲染数据来源
  • selected_sections:对象格式 { room_id: ["A","B"] }(每个房间各自的选择)
  • spec_base_id_map不入库GetGoodsViewData 从 goods_spec_base.extends->seat_key 动态构建
  • seat_key 格式:{roomId}_{rowLabel}_{colNum}(无 MD5
  • 现有前端编辑体验完全不受影响前端只提交选择项template_snapshot 由后端保存时填充)

13.3 spec_base_id_map 断路问题

根因BatchGenerate 生成 GoodsSpecBase.id 后,从未写入 spec_base_id_map。

解决方案

  • BatchGenerate 写入 goods_spec_base.extends.seat_key = "roomId_rowLabel_colNum"
  • GetGoodsViewData 从 extends.seat_key 动态构建 spec_base_id_map

13.4 Git 状态

741f25451 docs: v3.0 最终规格 - template_snapshot 字段 + selected_sections 对象格式
6daa33232 docs: new vr_goods_config spec + Phase 2 v3.0 plan
1b0ac3276 fix: 替换为票务专用精简 footer
c894e7018 fix: 复制 ShopXO public 模板
349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
7bd896764 feat(Phase 2): 完成票务商品前端展示层

13.5 Issue 记录

  • Issue #13[P0] vr_goods_config v3.0 落地实现
    • Step 1AdminGoodsSaveHandle 填充 template_snapshot
    • Step 2BatchGenerate 写入 extends.seat_key
    • Step 3GetGoodsViewData 重写
    • Step 4ticket_detail.html JS seatKey 格式更新