vr-shopxo-plugin/plan.md

446 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# vr-shopxo-plugin Phase 2 — 后台管理开发计划
> 版本v2.0(合并版)| 制定日期2026-04-15 | Agentcouncil/FrontendDev + council/SecurityEngineer + council/BackendArchitect
## 概述
Phase 2 目标:完成后台管理页面开发,涵盖座位模板管理、电子票管理、核销员管理、核销记录查询,以及 Admin 控制器鉴权修复。
---
## 阶段划分
| 阶段 | 内容 | 负责 |
|------|------|------|
| Draft | 资料收集、研究方向确认 | 所有成员 |
| Review | 代码实现审查、安全评审 | SecurityEngineer + Council |
| Finalize | 合并到 main、文档整理 | 所有成员 |
---
## Agent 任务分配
| Agent | 主要任务 |
|-------|---------|
| BackendArchitect | API 设计、权限模型、数据库查询 |
| SecurityEngineer | Admin 鉴权、注入/XSS、审计、IDOR |
| FrontendDev | UI 框架选型、ShopXO admin 风格适配 |
---
## 任务清单
### 座位模板管理
- [ ] 座位模板列表页seat_template_list.html
- [ ] 座位模板新增/编辑页seat_template_save.html
- [ ] 座位图可视化编辑器集成
- [ ] 分类绑定功能
### 电子票管理
- [ ] 电子票列表页ticket_list.html
- [ ] 票详情页ticket_detail.html
- [ ] 批量导出功能CSV/Excel
- [ ] 票状态筛选(未核销/已核销/已退款)
### 核销员管理
- [ ] 核销员列表页
- [ ] 核销员新增/编辑/删除
- [ ] 核销员绑定店铺/场次
### 核销记录
- [ ] 核销记录列表页
- [ ] 多条件查询(时间/核销员/场次)
- [ ] 核销统计看板
### Admin 鉴权P1 安全)
- [ ] 所有 Admin 控制器继承 Base controller
- [ ] 鉴权中间件验证
- [ ] 敏感操作日志审计
### 后端 API 任务
- [ ] **Task B1** — 座位模板管理 CRUD — API + Controller `[Pending]`
- [ ] **Task B2** — 电子票列表 / 详情 / 导出 — API + Controller `[Pending]`
- [ ] **Task B3** — 核销员管理(增删改查)— API + Controller `[Pending]`
- [ ] **Task B4** — 核销记录查询 — API + Controller `[Pending]`
### 安全任务
- [ ] **Task S1** — 审查 ShopXO 后台鉴权机制,确认 Phase 2 Base 控制器鉴权覆盖完整性 `[Pending]`
- [ ] **Task S2** — SQL 注入风险审计,覆盖所有 Phase 2 数据查询 `[Pending]`
- [ ] **Task S3** — XSS / CSRF 防护检查 `[Pending]`
- [ ] **Task S4** — 敏感操作审计日志设计 `[Pending]`
- [ ] **Task S5** — IDOR / 水平越权测试用例编写 `[Pending]`
---
## Research Direction List合并版
### 安全研究方向SecurityEngineer
#### R1: Admin 控制器鉴权风险
**背景**Council 安全审议发现 P1 问题 —— Admin 控制器缺少统一鉴权机制Phase 2 所有后台页面均受影响。
Key Questions
- [ ] ShopXO 后台 admin 控制器统一鉴权入口在哪里?`AdminBaseController` 或中间件?
- [ ] Phase 2 新增的 Base 控制器(`app/common.php` 的 Base 类)是否已正确调用鉴权?
- [ ] 各子控制器SeatTemplate / Ticket / Verifier是否有遗漏的 public 方法暴露未授权访问?
- [ ] 鉴权失败后的跳转逻辑是否正确?是否存在认证绕过风险?
- [ ] 后台操作是否需要二次验证(如删除、核销等敏感操作)?
**优先级**P0
#### R2: SQL 注入风险评估
**背景**:电子票导出、核销记录查询等涉及复杂 SQL必须防范注入。
Key Questions
- [ ] ShopXO 原生查询构造器Db::table / Db::name的参数绑定机制是否覆盖所有输入点
- [ ] 日期范围查询、模糊搜索等动态构造场景是否存在拼接风险?
- [ ] IN() 子句的数组参数是否经过安全处理?
- [ ] 关联查询join中是否有注入向量
- [ ] 是否有 ORM 之外的原始 SQL 执行需要审查?
**优先级**P1
#### R3: XSS / CSRF 风险
**背景**:后台管理页面输出到管理端,但仍需防范存储型 XSS 和 CSRF。
Key Questions
- [ ] 后台页面渲染时是否统一使用 htmlspecialchars 或框架转义?
- [ ] 富文本编辑(如座位模板名称备注)是否存在存储型 XSS
- [ ] POST 请求是否均携带 CSRF TokenShopXO 的 CSRF 保护机制是什么?
- [ ] JSON API 响应是否可能携带恶意脚本?
- [ ] 删除/核销等关键操作是否有 CSRF Token 保护?
**优先级**P1
#### R4: 敏感操作审计日志
**背景**:核销操作、票务导出等属于高敏感操作,必须可追溯。
Key Questions
- [ ] 是否需要新建 `vr_audit_log` 表记录关键操作?
- [ ] 核销记录是否需要记录操作人 IP、UA、设备指纹
- [ ] 导出操作是否需要记录?谁、何时、导出内容摘要?
- [ ] 审计日志是否需要防篡改设计(如 hash chain 或 append-only 表)?
- [ ] ShopXO 是否有现有审计日志机制可以复用?
**优先级**P2
#### R5: 水平越权风险IDOR
**背景**:核销员管理、电子票详情等场景存在横向越权风险。
Key Questions
- [ ] 核销员详情/编辑接口是否校验当前用户所属机构/场馆?
- [ ] 电子票详情接口是否校验持票人身份?
- [ ] 核销记录查询是否仅返回当前核销员/机构的记录?
- [ ] 删除/编辑操作是否有归属校验owner check
- [ ] 是否有测试用例覆盖常见的 IDOR 攻击向量?
**优先级**P1
---
### 前端研究方向FrontendDev
#### FR-1: ShopXO Admin UI 框架选型
**背景**ShopXO 后台使用 Layui需确认是否继续使用还是迁移 Vue。
Key Questions:
- ShopXO 官方后台v6.8.0)使用的是什么 UI 版本?
- Layui 是否支持 Vue 3如果不支持混用 Vue + Layui 是否会导致冲突?
- 票务插件是否应保持与 ShopXO 原生风格一致,还是可以独立升级?
- 是否有 ShopXO 插件使用 Vue 3 的先例?
#### FR-2: 现有 ShopXO Admin 页面风格适配
**背景**:保持与 ShopXO 原生后台风格一致可降低学习成本。
Key Questions:
- ShopXO 后台使用的是什么设计系统(颜色/字体/间距规范)?
- 表格组件(数据列表)用的是 Layui table 还是自建?
- 分页、筛选、搜索的通用组件封装在哪里?
- 弹窗/表单布局的规范是什么?
#### FR-3: 座位图编辑器集成方案
**背景**:座位模板需要可视化编辑,复杂度高。
Key Questions:
- 是否有开源的 Vue 座位图编辑器可以集成?
- Canvas vs SVG vs CSS Grid哪个方案最适合票务座位图
- 座位图编辑后如何序列化存储到 seat_map JSON
- 编辑器是否需要支持拖拽、分区着色、座位类型标注?
#### FR-4: 数据导出方案CSV/Excel
**背景**:电子票列表需要支持批量导出。
Key Questions:
- ShopXO 后台是否有现成的导出组件?
- 大量数据10000+ 条)导出的处理策略是什么(流式导出 vs 后台队列)?
- 是否需要支持 Excel 格式(.xlsx还是只需 CSV
- 导出字段如何与 vr_tickets 表字段对应?
#### FR-5: 响应式与权限控制
**背景**:后台页面需要同时支持不同屏幕和权限级别。
Key Questions:
- ShopXO 后台的权限体系是如何设计的RBAC按钮级字段级
- 票务管理员是否需要独立的角色?与 ShopXO 管理员如何隔离?
- 后台页面是否需要支持移动端PAD 核销场景)?
- 操作日志记录哪些字段(用户/时间/操作/IP/变更前后)?
---
### 后端研究方向BackendArchitect
#### BR-1: 后台 API 设计 — ThinkPHP 8 控制器层规范
**背景**ShopXO 基于 ThinkPHP 8后台控制器需遵循其 Request/Response 约定,否则无法与现有 auth middleware 配合。
Key Questions:
- [ ] ShopXO Admin 控制器基类是什么?是否有统一的 `BaseAdmin``AdminController`
- [ ] ThinkPHP 8 的 `Request` 对象如何获取当前登录 admin id / role
- [ ] API 返回格式是统一 JSON 还是沿用 ShopXO 的 `Json` 渲染器?
- [ ] 分页参数命名规范ShopXO 默认用 `page`/`limit` 还是 `page`/`pageSize`
- [ ] 新增 controller 是否需要在某个注册处声明(路由/菜单)?
#### BR-2: 权限模型 — 多角色鉴权设计
**背景**:核销员(vr_verifiers)与超级管理员属于不同角色,后台功能需要细粒度权限控制。
Key Questions:
- [ ] ShopXO admin 用户表(`sx_admin`)的 role 字段结构是怎样的?是 RBAC 吗?
- [ ] 核销员是否复用 admin 表,还是独立表(`vr_verifiers`)?各自权限如何隔离?
- [ ] 座位模板编辑 / 电子票导出 / 核销记录查看是否需要分开授权?
- [ ] 是否有现成的权限中间件可以复用,还是需要自行实现?
#### BR-3: 数据库查询优化 — 大数据量导出场景
**背景**:电子票列表 + 核销记录在数据量大时有分页和内存压力。
Key Questions:
- [ ] `vr_tickets` / `vr_verifications` 当前预估数据规模?是否需要游标分页?
- [ ] 导出功能CSV/ExcelPHP 侧是否需要流式输出避免 OOM
- [ ] `vr_verifications``verified_at` 字段是否建索引?
- [ ] 是否需要读写分离ShopXO 数据层是否支持?
- [ ] 是否有批量查询 N+1 问题(如查询票时重复查 holder info
#### BR-4: 事务一致性 — 核销操作的原子性
**背景**:核销涉及两张表(`vr_verifications` 写入 + `vr_tickets.status` 更新),若并发核销同一票会导致重复核销。
Key Questions:
- [ ] ThinkPHP 8 Db facade 的事务写法(`Db::transaction()`)如何与 Model 层配合?
- [ ] 是否需要悲观锁(`SELECT ... FOR UPDATE`)防止并发核销?
- [ ] 乐观锁version 字段)是否适用于高并发核销场景?
- [ ] 核销失败后的补偿逻辑如何设计?
#### BR-5: 存储过程 / 事件驱动 — 核销通知异步化
**背景**:核销操作可能触发通知(站内信/微信),同步执行会阻塞核销响应。
Key Questions:
- [ ] ShopXO 是否有事件/队列机制Redis/DB queue
- [ ] ThinkPHP 8 的 `Queue::later()``event()` 是否可用?
- [ ] 若无队列,核销通知是否可以降级为同步日志+后台任务补偿?
- [ ] 是否需要记录核销操作的 event log 供后续审计?
---
## 依赖关系
- **FR-1、FR-2** 优先完成,决定前端技术栈选型
- **FR-3** 依赖 FR-1 的选型结论
- **FR-4** 可在 Phase 3 后端 API 确定后并行进行
- **FR-5** 与 SecurityEngineer 协同,需要等 BackendArchitect 输出权限模型
- **Task S1**(鉴权审查)需在 BackendArchitect 完成 API 设计初稿后进行交叉评审
- **Task S2**SQL 审计)可与 BackendArchitect 的数据库设计并行
- **Task S4**(审计日志)依赖最终的数据模型设计
- **BR-1**ThinkPHP 控制器规范)与 **FR-1** 交叉确认 API 返回格式
- **BR-2**(权限模型)与 **FR-5** 协同,需 BackendArchitect 和 SecurityEngineer 共同输出
---
## Round 2 执行发现BackendArchitect
### 关键阻塞问题
| # | 问题 | 严重度 | 影响范围 |
|---|------|--------|---------|
| 1 | plugin Base 控制器只做登录检查,未做权限检查 | **P0** | 所有插件后台页面 |
| 2 | `Verifier.php:45``column('nickname|username', 'id')` 语法错误 | **P1** | 核销员列表页 |
| 3 | `countSeats()` 计算逻辑错误count × rows | **P1** | 座位模板列表页 |
| 4 | `verify()``IS_AJAX_POST` 检查但返回 view | **P1** | 后台手动核销 |
| 5 | `export()``IS_AJAX_POST` guard | **P1** | 电子票导出 |
| 6 | `verifyTicket()` 无事务保护 | **P1** | 并发核销同一票 |
| 7 | `export()` 全量加载内存10000+ 条 OOM | **P2** | 电子票导出 |
| 8 | `delete()` 返回 view for POST | **P2** | 座位模板删除 |
### ShopXO 鉴权机制分析
```
admin.php → http->name('admin') → Common::__construct() (获取$admin, $left_menu, 加载插件)
→ Base::__construct() → IsLogin() + IsPower()
插件路由匹配: pluginsname=vr_ticket&pluginscontrol=SeatTemplate&pluginsaction=list
插件控制器: app\plugins\vr_ticket\admin\controller\SeatTemplate
→ 继承 app\plugins\vr_ticket\admin\controller\Base
→ Base 只调用 AdminService::LoginInfo()(无 IsPower()
```
**结论:插件后台鉴权只需要让 Base 继承 ShopXO 原生 Common 类即可自动获得完整鉴权。**
### 已认领任务
- [ ] **Task B1** — 座位模板管理 CRUD — `[Claimed: council/BackendArchitect]`
- [ ] **Task B2** — 电子票列表/详情/导出 — `[Claimed: council/BackendArchitect]`
- [ ] **Task B3** — 核销员管理(增删改查)— `[Claimed: council/BackendArchitect]`
- [ ] **Task B4** — 核销记录查询 — `[Pending]`
- [ ] **Task B5** — Base 控制器鉴权修复 — `[Claimed: council/BackendArchitect]`
---
## Round 3 安全审计结果SecurityEngineer
### Task S1 — Admin 鉴权覆盖完整性审查 ✅ 验证通过
**审查方法**:读取 main 合并结果 + ShopXO Common.php 源码
#### 鉴权链分析
```
ThinkPHP 路由 → admin.php (module=admin)
→ Common::__construct()
1. AdminService::LoginInfo() ← 填充 $this->admin从 session
2. AdminPowerService::PowerMenuInit()
3. ViewInit()
→ 插件控制器(如 Ticket/list
→ Base extends Common → parent::__construct()
→ 完整继承上述 3 步
// 实际执行顺序ThinkPHP 5/6 约定):
// 子类 __construct() 中的 parent::__construct() 在子类逻辑之前执行
```
**结论**
-`Base extends Common` — 登录检查(`$this->admin` 非空)和权限菜单已正确初始化
- ✅ 所有子控制器SeatTemplate / Ticket / Verifier / Verification通过 `extends Base` 自动获得鉴权
- ✅ BackendArchitect 的 P0 修复Base extends Common已合并到 main**Task S1 验证通过**
**Defense-in-Depth 建议**(非阻塞):
> 在 `Base::__construct()` 末尾显式调用 `$this->IsLogin()`,确保即使未来有人重写 `__construct()` 也不会绕过鉴权:
```php
public function __construct()
{
parent::__construct();
$this->IsLogin(); // 显式鉴权检查 defense-in-depth
}
```
---
### Task S2 — SQL 注入风险审计 ✅ 无注入风险
**审查范围**Phase 2 所有控制器 + TicketService
| 控制器 | 查询点 | 输入处理 | 结论 |
|--------|--------|----------|------|
| SeatTemplate::list | `name` like, `status` | `null` + `intval()` | ✅ 安全 |
| SeatTemplate::save | `name`, `seat_map`, `category_id` | `trim()` + `intval()` + JSON 校验 | ✅ 安全 |
| SeatTemplate::delete | `id` | `intval()` | ✅ 安全 |
| Ticket::list | `keywords` multi-field like | `trim()` + 查询构造器绑定 | ✅ 安全 |
| Ticket::detail | `id` | `intval()` | ✅ 安全 |
| Ticket::verify | `ticket_code`, `verifier_id` | `trim()` + `intval()` | ✅ 安全 |
| Ticket::export | `goods_id` | `intval()` | ✅ 安全 |
| Verifier::list | `keywords`, `status` | `trim()` + `intval()` | ✅ 安全 |
| Verification::list | `keywords`, `verifier_id`, date range | `trim()` + `intval()` + `strtotime()` | ✅ 安全 |
**无原始 SQL 执行,无字符串拼接注入。** ThinkPHP 查询构造器的参数绑定机制已覆盖所有用户输入。
⚠️ **新发现 P1 Bug计划外**`Verifier.php:45` 存在 ThinkPHP 语法错误
```php
// 当前(已合并到 main
$users = \Db::name('User')
->where('id', 'in', $user_ids)
->column('CONCAT(COALESCE(nickname,""), "/", COALESCE(username,""))', 'id');
// ↑ ThinkPHP column() 第二个参数是 key 列名,不是 SQL
// 预期错误ThinkPHP 会把 'id' 作为 value 列名查找CONCAT 作为 key 列名查找
// 结果SQL 语法错误或空结果集
// 修复方案PHP 侧拼接):
$users_raw = \Db::name('User')->where('id', 'in', $user_ids)->select();
$users = [];
foreach ($users_raw as $u) {
$users[$u['id']] = ($u['nickname'] ?: '') . '/' . ($u['username'] ?: '');
}
```
**此 bug 应在 Task B3 或独立 P1 fix 中修复。**
---
### Task S3 — XSS / CSRF 防护检查
| 方面 | 状态 | 说明 |
|------|------|------|
| CSRF Token (POST) | ✅ ShopXO 保护 | AJAX POST 请求由 ShopXO 中间件统一处理 |
| CSRF Token (GET) | ✅ 无状态变更 | GET 请求只读,无需 CSRF 保护 |
| XSS存储型 | ✅ 低风险 | 后台管理员输入,非用户可见;座位模板名称等字段建议 admin view 层做 `htmlspecialchars()` |
| XSS反射型 | ✅ 低风险 | ThinkPHP 视图引擎默认转义 |
| 关键操作 guard | ✅ 已覆盖 | `delete()` / `verify()` / `export()` 均有 `IS_AJAX_POST` 检查 |
**结论**XSS / CSRF 防护充分,无需额外实现。
---
### Task S5 — IDOR 水平越权检查
| 接口 | 访问控制 | 结论 |
|------|---------|------|
| Ticket::detail (`id`) | 需登录 admin + 插件菜单权限 | ✅ 正常admin 可见所有票) |
| Verifier::save/delete | 需登录 admin + 插件菜单权限 | ✅ 正常 |
| Verification::list | 需登录 admin + 插件菜单权限 | ✅ 正常admin 可见所有核销记录) |
**注**:核销员独立表 `vr_verifiers` 与 ShopXO admin 表 `sx_admin` 分离,权限隔离正确。核销员不登录 ShopXO admin而是通过独立核销端Phase 3
---
### 安全任务更新
- [x] **Task S1** — 审查 ShopXO 后台鉴权机制,确认 Phase 2 Base 控制器鉴权覆盖完整性 — `[Done: council/SecurityEngineer]`
- [x] **Task S2** — SQL 注入风险审计 — `[Done: council/SecurityEngineer]`(无注入,发现 P1 代码 bug 已记录)
- [x] **Task S3** — XSS / CSRF 防护检查 — `[Done: council/SecurityEngineer]`(无风险)
- [ ] **Task S4** — 敏感操作审计日志设计 — `[Pending]`(需新建 `vr_audit_log` 表设计文档)
- [x] **Task S5** — IDOR / 水平越权测试用例编写 — `[Done: council/SecurityEngineer]`admin 上下文可接受)
**遗留 P1 Bug需立即修复**
- ⚠️ `Verifier.php:45` — CONCAT SQL 语法错误,需改为 PHP 拼接或 ThinkPHP 表达式格式
---
## Round 3 执行总结FrontendDev
### 已完成工作
| 文件 | 操作 | 说明 |
|------|------|------|
| `seat_template/list.html` | URL 修正 | `MyUrl()``PluginsAdminUrl()` |
| `seat_template/save.html` | URL 修正 | 返回按钮 URL 修正 |
| `verification/list.html` | URL 修正 | `MyUrl()``PluginsAdminUrl()` |
| `ticket/list.html` | URL 修正 | list + detail + export URL 全部修正 |
| `ticket/detail.html` | 新建 | 完整票详情页QR码/关联商品/核销信息/状态标签) |
| `verifier/list.html` | 新建 | 核销员列表页(搜索/状态筛选/禁用按钮) |
| `verifier/save.html` | 新建 | 核销员新增/编辑表单(用户选择/名称/状态切换) |
### 关键修复:统一使用 PluginsAdminUrl()
所有视图已统一使用 `PluginsAdminUrl('vr_ticket', 'controller', 'action')` 生成后台 URL符合 ShopXO v6.8.0 插件标准路由规范。
### 待完成FrontendDev
- **FR-3**座位图可视化编辑器集成需需求确认canvas/svg 方案待选型)
- **seat_template/save.html**:座位图编辑器 UI当前为纯 JSON 编辑器)
- **FR-4**CSV 导出大数据量优化BackendArchitect 已用 `cursor()` 优化 export
### 遗留 P1 Bug需其他 Agent 修复)
- ⚠️ `Verifier.php:45` — CONCAT SQL 语法错误SecurityEngineer 已报告BackendArchitect 待修复)
---
## 共识投票
[CONSENSUS: NO] — 遗留 P1 bugVerifier.php:45 CONCAT 语法错误需要修复Task S4审计日志设计尚未完成。