# vr-shopxo-plugin 项目启动报告 > 生成时间:2026-04-15 00:16 CST > 生成方式:西莉娅 Council 协助(模型路由:opus,MiniMax 路由) > 关联 Issue:#TBD(创建后填入) --- ## 一、当前状态确认(Status Report) ### 1.1 代码 vs 文档差异 | 项目 | 文档状态(ARCHITECTURE.md v2.2) | 实际代码 | |------|---|---| | 核心架构 | spec = 场次,venue_data 精简 | ✅ 一致 | | 插件表 | vr_seat_templates / vr_tickets / vr_verifiers / vr_verifications | ✅ 一致 | | plugin.json | 应删除 event/session 管理菜单 | ❌ 仍为旧版菜单(vr_events/vr_sessions) | | plan.md | 应删除 vr_events/vr_sessions 表 | ❌ 仍为旧 phase 结构 | | PHP 代码 | 应有完整骨架(service/EventListener.php) | ❌ 零 PHP 代码 | | 前端代码 | shopxo-uniapp 定制页面 | ❌ 零 Vue 代码 | | 数据库迁移 | 应有 001-004 迁移文件 | ❌ 无 SQL 文件 | ### 1.2 待清理的旧内容 - [ ] `plan.md` 中的 Phase 1(旧 vr_events/vr_sessions 表结构) - [ ] `docs/04_IMPLEMENTATION_ROADMAP.md` 中的旧 phase(旧表名) - [ ] `plugin.json` 中的 `活动管理/场次管理` 菜单 → 改为 `座位模板/电子票/核销记录` ### 1.3 环境现状 | 组件 | 状态 | 地址 | |------|------|------| | ShopXO PHP 后端 | ✅ 运行中 | http://localhost:10000/ | | ShopXO MySQL | ✅ 运行中 | localhost:10001 | | shopxo-uniapp | ❌ 未安装 | — | | 插件目录 | ✅ 存在 | `{SHOPXO_SRC}/app/plugins/` | | vr_ticket 插件目录 | ❌ 为空 | — | > `SHOPXO_SRC` = `/Users/bigemon/.openclaw/workspace/council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src` --- ## 二、实施路线图(Revised v2.2) > 基于 ARCHITECTURE.md v2.2,删除 vr_events/vr_sessions/vr_venues 全部内容 ### 精简后的 Phase 列表 | Phase | 内容 | 交付物 | 预估 | 可并行 | |-------|------|--------|------|--------| | **Phase 0** | 环境搭建 + 插件骨架 | 可安装的插件目录结构 | 1天 | — | | **Phase 1** | 数据库迁移 | 4个 SQL 迁移文件 + 执行 | 0.5天 | ✅ Phase 0 | | **Phase 2** | 后台 CRUD + API | SeatTemplate + Ticket 后台 + API | 3天 | ✅ Phase 1 | | **Phase 3** | 前端选座 UI | 座位地图组件 + 选座页 | 3天 | ✅ Phase 2 | | **Phase 4** | 订单钩子 + 观演人 | `plugins_service_buy_order_*` 钩子 | 2天 | ✅ Phase 3 | | **Phase 5** | 支付回调 + QR 票 | `order.paid` 监听 → QR 生成 | 1天 | ✅ Phase 4 | | **Phase 6** | B 端核销页 | 扫码核销页面 + API | 2天 | ✅ Phase 5 | | **Phase 7** | 票夹 + C 端 | 用户票夹页 + 订单列表 | 2天 | ✅ Phase 6 | | **Phase 8** | 联调 + 测试 + 部署 | 微信小程序审核上线 | 2天 | 串行 | **预估**:Agent 集群并行 → **Phase 0-7 共约 7 天**,Phase 8 收尾共 9 天 ### 核心变更说明(vs 旧版 roadmap) 1. **删除 vr_events 表**:活动信息直接用 ShopXO 商品替代(商品名称 = 活动名称) 2. **删除 vr_sessions 表**:场次 = ShopXO spec_value(每个规格值 = 一个演出时间) 3. **删除 vr_venues 表**:场馆/座位配置合并到 `vr_seat_templates.venue_data` JSON 4. **Phase 2 简化**:不需要独立的 Event/Session CRUD,商家直接用 ShopXO 商品管理 5. **Phase 5 新增**:QR 票生成 + 支付回调分离(Phase 4 只处理下单钩子,Phase 5 处理支付成功) --- ## 三、任务分配方案(Task Allocation) ### Agent 分工建议 | Phase | 主要负责 | 辅助 | 说明 | |-------|---------|------|------| | Phase 0 | **大头(手动)** | AI 生成清单 | 环境需要本地操作,AI 生成步骤清单 | | Phase 1 | 妮可 | — | 数据库迁移脚本,CRUD 模式 | | Phase 2 | 李狗蛋 | 妮可 | 后台 CRUD + API,可并行 | | Phase 3 | 李狗蛋 | 大头(验收) | 前端选座组件,shopxo-uniapp | | Phase 4 | 李狗蛋 | — | 订单钩子 + 观演人表单 | | Phase 5 | 西莉娅 | — | 支付回调 + QR 生成(熟悉 ShopXO Hook) | | Phase 6 | 西莉娅 | — | B 端核销页(参考 realstore/check.vue) | | Phase 7 | 西莉娅 | 大头(验收) | 票夹 + C 端 | | Phase 8 | 大头 | 全员 | 联调 + 微信审核上线 | ### 大头(Bigemon)职责 - **Phase 0**:本地操作(安装 shopxo-uniapp、配置 HBuilderX、安装插件到 ShopXO) - **Phase 3/7 验收**:前端 UI/UX 体验把关 - **Phase 8 主协调**:联调问题定位 + 微信审核沟通 - **全局**:架构决策拍板、技术债务审查 ### 优先级排序(先做什么价值最大) ``` P0(立即可做) └── Phase 0:环境 + 插件骨架 → 无此则后续无法运行 P1(骨架完成后立即启动) ├── Phase 1:数据库迁移 → 提供所有表的 SQL └── Phase 2:后台 API → 其他 phase 依赖 P2(Phase 2 完成后并行) ├── Phase 3:前端选座 → 用户核心体验 ├── Phase 4:订单钩子 → 核心购票流程 └── Phase 5:QR 票 → 购票交付物 P3(后半程) ├── Phase 6:B 端核销 → 商家核心功能 └── Phase 7:票夹 → 用户核心功能 ``` --- ## 四、Phase 0 详细实施计划 ### 环境信息 ``` ShopXO 后台:http://localhost:10000/admin ShopXO 前台:http://localhost:10000/ MySQL:localhost:10001(root/ShopXO@2026) SHOPXO_SRC:/Users/bigemon/.openclaw/workspace/council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src 插件目录:{SHOPXO_SRC}/app/plugins/vr_ticket/ shopxo-uniapp:需新建(目录 TBD,建议放在 shopxo-env/ 下) ``` ### Step by Step - [ ] **Step 0.1**:验证 ShopXO 后台可访问 → http://localhost:10000/admin - [ ] **Step 0.2**:下载 shopxo-uniapp(HBuilderX → Git 克隆 or 下载 zip) - [ ] **Step 0.3**:配置 `shopxo-uniapp/common/config.js` 的 `request_url` = `http://localhost:10000/` - [ ] **Step 0.4**:HBuilderX 导入 shopxo-uniapp → 本地 H5 预览验证 - [ ] **Step 0.5**:创建插件目录结构(见下方目录树) - [ ] **Step 0.6**:写入 `plugin.json`(更新版,对齐 v2.2) - [ ] **Step 0.7**:写入 `EventListener.php` + `service/BaseService.php`(骨架) - [ ] **Step 0.8**:后台 → 插件管理 → 找到 vr_ticket → 点击安装 - [ ] **Step 0.9**:后台左侧菜单是否出现「VR票务」菜单 ### AI 可参与度 | Step | AI 可做 | 需要大头手动 | |------|---------|------------| | 0.1 | ✅ | 验证 URL | | 0.2 | ✅(生成 git clone 命令) | HBuilderX 操作 | | 0.3 | ✅(生成配置代码) | 打开 HBuilderX | | 0.4 | ❌ | HBuilderX 预览 | | 0.5 | ✅(生成 mkdir 命令) | 执行命令 | | 0.6 | ✅(生成完整 JSON) | 上传到服务器 | | 0.7 | ✅(生成完整 PHP) | 上传到服务器 | | 0.8 | ❌ | 浏览器操作 | | 0.9 | ✅ | 人工验收 | ### 验收标准 1. 后台左侧菜单出现「VR票务」→「座位模板/电子票/核销记录」 2. 点击「座位模板」不报错(可为空列表) 3. 访问 `/plugins/vr_ticket/admin/seat_template/list` 返回有效 JSON --- ## 五、Phase 1 — 数据库迁移文件 ### `database/migrations/001_vr_seat_templates.sql` ```sql -- ===================================================== -- VR票务插件 - 座位模板表 -- 座位模板通过分类ID绑定到 ShopXO 商品 -- AI 参与度:100%(标准建表语句) -- ===================================================== CREATE TABLE IF NOT EXISTS `vr_seat_templates` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '模板ID', `name` VARCHAR(180) NOT NULL COMMENT '模板名称(如:鸟巢-A区)', `category_id` BIGINT UNSIGNED NOT NULL COMMENT '绑定的 ShopXO 分类ID', `seat_map` LONGTEXT NOT NULL COMMENT '座位地图JSON(见 venue_data 结构)', `spec_base_id_map` LONGTEXT DEFAULT NULL COMMENT '座位ID→spec_base_id 映射JSON', `status` TINYINT UNSIGNED DEFAULT 1 COMMENT '状态:0禁用 1启用', `add_time` INT UNSIGNED DEFAULT 0 COMMENT '创建时间戳', `upd_time` INT UNSIGNED DEFAULT 0 COMMENT '更新时间戳', PRIMARY KEY (`id`), UNIQUE KEY `uk_category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='VR演唱会座位模板'; -- seat_map JSON 结构示例: -- { -- "map": ["aaaaaaaaaaaa", "aaaaaaaaaaaa", "bbbbbb__bb"], -- "row_labels": ["A", "B", "C"], -- "seats": { -- "a": { "price": 599, "label": "VIP区", "classes": "seat-vip" }, -- "b": { "price": 399, "label": "普通区", "classes": "seat-normal" }, -- "_": null -- }, -- "sections": [ -- { "name": "VIP区", "color": "#FF6B6B", "rows": [0, 1] }, -- { "name": "普通区", "color": "#4ECDC4", "rows": [2, 3] } -- ] -- } ``` ### `database/migrations/002_vr_tickets.sql` ```sql -- ===================================================== -- VR票务插件 - 电子票表 -- 支付成功后由 TicketService 写入 -- AI 参与度:100%(标准建表语句) -- ===================================================== CREATE TABLE IF NOT EXISTS `vr_tickets` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '票ID', `order_id` BIGINT UNSIGNED NOT NULL COMMENT 'ShopXO 订单ID', `order_no` CHAR(60) NOT NULL COMMENT '订单号', `goods_id` BIGINT UNSIGNED NOT NULL COMMENT '商品ID', `goods_snapshot` TEXT DEFAULT NULL COMMENT '商品快照JSON(规格名/场次名)', `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID', `ticket_code` CHAR(36) NOT NULL COMMENT 'UUID 票码', `qr_data` TEXT DEFAULT NULL COMMENT '加密QR内容', `seat_info` VARCHAR(255) DEFAULT NULL COMMENT '座位信息(如 A区-3排-5座)', `spec_base_id` BIGINT UNSIGNED DEFAULT 0 COMMENT 'spec_base_id(关联ShopXO规格)', `real_name` VARCHAR(60) DEFAULT NULL COMMENT '观演人姓名', `phone` CHAR(15) DEFAULT NULL COMMENT '观演人手机', `id_card` CHAR(18) DEFAULT NULL COMMENT '观演人身份证(选填)', `verify_status` TINYINT UNSIGNED DEFAULT 0 COMMENT '核销状态:0未核销 1已核销 2已退款', `verify_time` INT UNSIGNED DEFAULT 0 COMMENT '核销时间戳', `verifier_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '核销员ID(vr_verifiers.id)', `issued_at` INT UNSIGNED DEFAULT 0 COMMENT '票发放时间戳(支付成功后)', `created_at` INT UNSIGNED DEFAULT 0 COMMENT '记录创建时间', `updated_at` INT UNSIGNED DEFAULT 0 COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_ticket_code` (`ticket_code`), KEY `idx_order_id` (`order_id`), KEY `idx_user_id` (`user_id`), KEY `idx_goods_id` (`goods_id`), KEY `idx_verify_status` (`verify_status`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='VR演唱会电子票'; ``` ### `database/migrations/003_vr_verifiers.sql` ```sql -- ===================================================== -- VR票务插件 - 核销员表 -- AI 参与度:100%(标准建表语句) -- ===================================================== CREATE TABLE IF NOT EXISTS `vr_verifiers` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '核销员ID', `user_id` BIGINT UNSIGNED NOT NULL COMMENT '对应的 ShopXO 用户ID', `name` VARCHAR(60) NOT NULL COMMENT '核销员名称', `status` TINYINT UNSIGNED DEFAULT 1 COMMENT '状态:0禁用 1启用', `created_at` INT UNSIGNED DEFAULT 0 COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='VR票务核销员'; ``` ### `database/migrations/004_vr_verifications.sql` ```sql -- ===================================================== -- VR票务插件 - 核销记录表 -- AI 参与度:100%(标准建表语句) -- ===================================================== CREATE TABLE IF NOT EXISTS `vr_verifications` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '核销记录ID', `ticket_id` BIGINT UNSIGNED NOT NULL COMMENT '票ID(vr_tickets.id)', `ticket_code` CHAR(36) NOT NULL COMMENT '票码(冗余存储)', `verifier_id` BIGINT UNSIGNED NOT NULL COMMENT '核销员ID', `verifier_name` VARCHAR(60) DEFAULT NULL COMMENT '核销员名称(冗余)', `goods_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '商品ID', `created_at` INT UNSIGNED DEFAULT 0 COMMENT '核销时间', PRIMARY KEY (`id`), KEY `idx_ticket_id` (`ticket_id`), KEY `idx_verifier_id` (`verifier_id`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='VR票务核销记录'; ``` --- ## 六、插件骨架代码 ### `plugin.json`(更新版,对齐 v2.2) ```json { "name": "vr-ticket", "title": "VR票务", "description": "为ShopXO添加VR演唱会票务功能:座位模板、观演人收集、QR电子票、扫码核销", "version": "1.0.0", "author": "sileya-ai", "author_url": "http://xmhome.ow-my.com:3000/sileya-ai/vr-shopxo-plugin", "shopxo_version": ">=1.0.0", "dependencies": [], "menus": [ { "title": "VR票务", "icon": "icon icon-ticket", "submenus": [ { "title": "座位模板", "url": "/plugins/vr-ticket/admin/seat_template/list" }, { "title": "电子票管理", "url": "/plugins/vr-ticket/admin/ticket/list" }, { "title": "核销记录", "url": "/plugins/vr-ticket/admin/verification/list" } ] } ], "listen_events": [ "order.paid" ], "hooks": [ "onOrderPaid" ] } ``` ### `EventListener.php` ```php getMessage(), $params); return false; } } ``` ### `service/BaseService.php` ```php $expire, 'iat' => time(), ]), JSON_UNESCAPED_UNICODE); $iv = random_bytes(16); $encrypted = openssl_encrypt($payload, 'AES-256-CBC', $secret, OPENSSL_RAW_DATA, $iv); $combined = $iv . $encrypted; return base64_encode($combined); } /** * 解密票务数据 * * @param string $encoded base64 编码的密文 * @return array|null 解密失败返回 null */ public static function decryptTicketData($encoded) { $secret = self::getTicketSecret(); $combined = base64_decode($encoded); if (strlen($combined) < 16) { return null; } $iv = substr($combined, 0, 16); $encrypted = substr($combined, 16); $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $secret, OPENSSL_RAW_DATA, $iv); if ($decrypted === false) { return null; } $data = json_decode($decrypted, true); // 检查过期 if (isset($data['exp']) && $data['exp'] < time()) { return null; } return $data; } /** * 获取票务加密密钥 * 优先从环境变量读取,否则用 ShopXO app_secret * @return string */ private static function getTicketSecret() { $secret = env('VR_TICKET_SECRET', ''); if (!empty($secret)) { return $secret; } // 回退:使用 ShopXO 应用密钥 return config('shopxo.app_key', 'shopxo_default_secret_change_me'); } /** * 安全的日志写入 * * @param string $message 日志信息 * @param array $context 上下文数据 * @param string $level info|error|warning */ public static function log($message, $context = [], $level = 'info') { $tag = '[vr-ticket]'; $ctx = empty($context) ? '' : ' ' . json_encode($context, JSON_UNESCAPED_UNICODE); log_{$level}($tag . $message . $ctx); } /** * 判断商品是否为票务商品 * * @param int $goods_id 商品ID * @return bool */ public static function isTicketGoods($goods_id) { $goods = \app\model\Goods::find($goods_id); if (empty($goods)) { return false; } return !empty($goods['venue_data']); } /** * 获取商品 venue_data * * @param int $goods_id * @return array */ public static function getGoodsVenueData($goods_id) { $goods = \app\model\Goods::find($goods_id); if (empty($goods) || empty($goods['venue_data'])) { return []; } return is_string($goods['venue_data']) ? json_decode($goods['venue_data'], true) : $goods['venue_data']; } } ``` --- ## 七、立即可执行的下一步 1. **大头手动**:安装 shopxo-uniapp(HBuilderX → Git 克隆 or 下载) 2. **西莉娅生成**:Phase 0 插件骨架 → 上传到 ShopXO 插件目录 3. **大头验收**:后台安装插件 → 验证菜单出现 4. **妮可生成**:Phase 1 数据库迁移 SQL(001-004) 5. **李狗蛋**(待大头搭好环境后):Phase 2 后台 CRUD --- ## 八、分工确认(待大头回复) 请确认以下分工是否符合预期: | Phase | 主要负责人 | 预计开始 | |-------|----------|---------| | Phase 0 | 大头(手动操作) | **立即** | | Phase 1 | 妮可(SQL 迁移) | Phase 0 完成后 | | Phase 2 | 李狗蛋(后台 CRUD) | Phase 1 完成后 | | Phase 3 | 李狗蛋 + 大头(验收) | Phase 2 完成后 | | Phase 4 | 李狗蛋(订单钩子) | Phase 3 中 | | Phase 5 | 西莉娅(QR 生成) | Phase 4 中 | | Phase 6 | 西莉娅(B端核销) | Phase 5 中 | | Phase 7 | 西莉娅 + 大头(票夹) | Phase 6 中 | | Phase 8 | 大头(联调上线) | Phase 7 完成后 |