6.1 KiB
6.1 KiB
Phase 2 — 研究资料归档
来源:Council 讨论(BackendArchitect + SecurityEngineer + FrontendDev,5轮) 整理:西莉雅(独立执行,非 Council 输出) 时间:2026-04-15 CST
一、后台架构研究(BR-1 ~ BR-5)
BR-1:ThinkPHP 8 控制器层规范 ✅ 已验证
Key Questions 结论:
| 问题 | 答案 |
|---|---|
| ShopXO Admin 控制器基类 | app\admin\controller\Common,非 BaseController |
| 获取当前 admin id / role | $this->admin['id'],由 Common::__construct() 注入 |
| API 返回格式 | \Json::成功() / \Json::fail() 渲染器 |
| 分页参数规范 | page + limit(ThinkPHP 默认) |
| 新增 controller 路由 | 无需声明,ShopXO 自动扫描 |
插件 Base 正确写法:
abstract class Base extends \app\admin\controller\Common {
public function __construct() {
parent::__construct(); // 触发 IsLogin + IsPower + ViewInit
}
}
BR-2:多角色鉴权模型 ⚠️ 部分实现
| 问题 | 现状 | 待 Phase 3 |
|---|---|---|
| ShopXO admin 角色结构 | sx_admin + sx_admin_role,RBAC |
— |
| 核销员复用 admin 表? | 否,独立表 vr_verifiers |
— |
| 权限隔离方案 | 目前所有 admin 可访问所有插件功能 | 实现 RBAC 细化 |
BR-3:数据库查询优化 ✅ 已实现
| 场景 | 解决方案 |
|---|---|
| 大数据量导出 | cursor() 流式游标(防 OOM) |
| 模糊搜索 | ThinkPHP 查询构造器(自动绑定参数) |
| 分页 | paginate(20) |
| N+1 | column() / select() + PHP 拼接 |
BR-4:核销操作事务一致性 ✅ 已实现
Db::transaction(function() {
$ticket = Db::name('tickets')
->where('ticket_code', $ticket_code)
->lock(true) // FOR UPDATE SKIP LOCKED
->find();
// 状态校验 + 更新 + 写入 vr_verifications
});
BR-5:事件驱动/异步队列 ❌ 待 Phase 3
- ShopXO 有
think_queue(Redis/DB) - 核销通知同步执行暂无问题,队列化留 Phase 3
二、安全研究方向(R-1 ~ R-5)
R1:Admin 控制器鉴权风险 ✅ 已解决
根因: 插件 Base 只调 LoginInfo(),未调用 IsLogin() + IsPower() + FormTableInit()。
修复后鉴权链:
ThinkPHP → admin.php → Common::__construct()
1. AdminService::LoginInfo() — 填充 $this->admin
2. AdminPowerService::PowerMenuInit() — 权限菜单
3. ViewInit() — 视图初始化
→ 插件控制器(extends Base)→ parent::__construct()
→ 完整继承上述 3 步
Defense-in-Depth 建议: 在 Base::__construct() 末尾显式调用 $this->IsLogin()。
R2:SQL 注入风险 ✅ 无注入
| 控制器 | 查询点 | 输入处理 | 结论 |
|---|---|---|---|
| SeatTemplate::list | name like, status |
null + intval() |
✅ |
| Ticket::list | keywords multi-field like |
trim() + 查询构造器绑定 |
✅ |
| Ticket::verify | ticket_code, verifier_id |
trim() + intval() |
✅ |
| Verification::list | date range | strtotime() 绑定 |
✅ |
R3:XSS / CSRF ✅ 低风险
| 方面 | 状态 |
|---|---|
| CSRF Token (POST) | ✅ ShopXO 保护 |
| XSS(存储型) | ✅ 低风险(admin 上下文) |
| 关键操作 guard | ✅ delete() / verify() / export() 均有 IS_AJAX_POST |
R4:敏感操作审计日志 ✅ 已实现
vr_audit_log 字段:
action / operator_id / operator_name(冗余) / target_type / target_id / target_desc(冗余) / client_ip(IPv6) / user_agent / request_id / extra(JSON快照) / created_at
索引: idx_action / idx_operator_id / idx_target / idx_created_at
防篡改: append-only 表,无 UPDATE/DELETE 接口。
R5:水平越权(IDOR)✅ Admin 上下文安全
潜在盲区(待 Phase 3):
- 核销员详情/编辑接口是否校验归属机构?
- 电子票详情接口是否校验持票人身份?
- 核销记录查询是否仅返回当前核销员/机构记录?
三、前端研究方向(FR-1 ~ FR-5)
FR-1:ShopXO Admin UI 框架 ✅ Layui
| 选项 | 结论 |
|---|---|
| Layui 2.x | ✅ 继续使用,零迁移成本 |
| Vue 3 | ❌ 不支持,混用会导致冲突 |
FR-2:ShopXO Admin 风格适配 ✅ 已适配
// ✅ 正确
PluginsAdminUrl('vr_ticket/ticket/list')
// ❌ 错误
MyUrl('ticket/list')
FR-3:座位图编辑器 ⚠️ 待 Phase 3
- 需产品需求确认(Canvas/SVG/Grid?)
seat_mapJSON 字段已预留
FR-4:数据导出方案 ✅ CSV 已实现,.xlsx 待 Phase 3
| 项目 | 现状 |
|---|---|
| CSV 导出 | ✅ cursor() 流式,防 OOM |
| 导出按钮 | ✅ POST-only form |
| Excel .xlsx | ❌ 待 Phase 3 |
FR-5:响应式与权限控制 ⚠️ 部分实现
- 权限体系复用 ShopXO RBAC
- 移动端 PAD 核销场景待 Phase 3
四、Phase 3 待办清单
需产品确认
- FR-3:座位图可视化编辑器需求
- FR-5:PAD 核销场景是否需要独立移动端页面?
- BR-2:核销员账号体系(复用 admin 还是独立?)
技术待办
- Defense-in-Depth:
Base::__construct()末尾显式调用IsLogin() - R5 盲区验证:IDOR 归属校验
- BR-5:核销通知队列化(think_queue)
- FR-4:CSV 导出携带搜索条件
- FR-4:Excel .xlsx 格式支持
- BR-2:插件独立权限(菜单级/操作级)
五、关键文件索引
| 文件 | 说明 |
|---|---|
app/plugins/vr_ticket/admin/controller/Base.php |
鉴权基类(已修复) |
app/plugins/vr_ticket/service/AuditService.php |
审计日志服务 |
app/plugins/vr_ticket/service/TicketService.php |
核销事务(含 FOR UPDATE) |
app/plugins/vr_ticket/admin/controller/Ticket.php |
电子票 CRUD + export |
app/plugins/vr_ticket/admin/controller/Verifier.php |
核销员管理 |
app/plugins/vr_ticket/EventListener.php |
vr_audit_log 建表 SQL |
shopxo/app/admin/controller/Common.php |
ShopXO 鉴权链源码 |