Commit Graph

20 Commits (5749edf6ada621fca68425d92b4e6b032ff620a9)

Author SHA1 Message Date
Council 5749edf6ad fix(Phase 2): 修复后台路由+视图路径,Vrticket控制器上线
- 新增 Vrticket.php(ThinkPHP小写路由约定,类名Vrticket)
- 修复视图标签:{include} → {{include}}(ShopXO使用{{}}分隔符)
- 视图文件同步到 app/admin/view/default/plugins/view/vr_ticket/
- 插件控制器类名 Seattemplate(ThinkPHP路由适配)
- plugin.json 新增场馆配置菜单项
- 删除废弃的 plugins/index.html 符号链接
- 自动载入:app.php 注册 app\plugins\ 命名空间映射
2026-04-16 07:59:27 +08:00
Council 49e256a9ab feat(Phase 3-1): Vue3 交互式场馆配置表单编辑器 save.html
- admin/view/venue/save.html:Vue3 CDN 嵌入
  - 实时彩色座位预览(每格 30x30px,背景色=zone.color,悬浮显示座位号/分区/价格)
  - 动态增删分区(char/name/price/color + 预设色板)
  - 每排座位行编辑器(动态 input)
  - 双向同步:改 zone char/color → 预览即时刷新
  - 初始默认值:3 区(VIP区/看台区/普通区),6座/排
  - mounted() 回填:zones_json / map_json / venue_json
  - computed 序列化:zonesJson / seatMapRowsJson / venueJson → 隐藏字段

- Venue.php 补充:新增 venue_json 字段供 Vue3 回填
  - venue_json = {name, address, image}

关联:docs/11_EDITOR_AND_INJECTION_DESIGN.md v3.0
2026-04-15 22:06:21 +08:00
Council 136efb9b92 feat(Phase 3-1): Venue.php CRUD + list.html + BatchGenerate venue.name 动态读取
- 新增 admin/controller/Venue.php:场馆配置 CRUD
  - list(): 解析 seat_map.venue.name 展示,zone_count / seat_count
  - save(): 构建 v3.0 seat_map JSON(venue + map + seats + sections)
  - delete(): 软删除 + 审计日志
  - preview(): 调试接口,返回 seat_map JSON + seat_count

- 新增 admin/view/venue/list.html:场馆列表页

- 改造 SeatSkuService.php BatchGenerate:
  - ensureVrSpecTypes() 增加 $venueName 参数
  - $vr-场馆 spec 值从 seat_map.venue.name 读取,不再硬编码
  - 降级:取模板 name 或 '未命名场馆'

关联:docs/11_EDITOR_AND_INJECTION_DESIGN.md v3.0
2026-04-15 22:02:03 +08:00
Council d411073885 council(fix): BackendArchitect - Fix regex bug in getExistingSpecBaseIds()
Bug: regex '^([A-Za-z]+)(\d+)排(\d)座$' with $m[3] misparsed seat labels
like "A排10座" → colNum=1 (wrong). Fixed to '^([A-Za-z]+)排(\d+)座$' with $m[2].

Also clarified spec_base_id_map docblock: frontend expects flat integer,
not nested object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:16:49 +08:00
Council 96337bc840 council(execute): BackendArchitect - Fix 2 bugs in P0-A/B/P1 implementations
Bug 1: SeatSkuService.php:381 - regex has syntax error
  '/^([A-Za-z]+)(\d+)排(\d+)座$/' → '/^([A-Za-z]+)(\d+)排(\d)座$/'
  The third capture group only needs single digit (col number 1-9).

Bug 2: ticket_detail.html:416 - frontend accesses specBaseIdMap as object
  but PHP returns flat integer: specBaseIdMap['A_1'] = 2001 (int), not {spec_base_id: 2001}.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:09:22 +08:00
Council 5e9c111370 council(draft): BackendArchitect - P0-A initGoodsSpecs + P0-B BatchGenerate
P0-A: BaseService::initGoodsSpecs() — 启用 is_exist_many_spec=1,
      插入 $vr-场馆/$vr-分区/$vr-时段/$vr-座位号 四维规格类型,幂等保护

P0-B: 新建 SeatSkuService.php,含:
      - BatchGenerate(): 批量生成座位级 SKU(500条/批,直接 SQL INSERT)
      - UpdateSessionSku(): 按场次更新 $vr-时段 维度
      - 幂等:已存在座位不重复生成

关联:Issue #9

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:00:29 +08:00
Council 93b70d4d50 council(execute): FrontendDev - Issue #9 P1 submit() refactor (seat-level goods_params)
- renderSeatMap(): add data-row-label + data-col-num attrs for specBaseIdMap key format
- toggleSeat(): change seatKey from "0_0" (numeric) to "A_1" (label_colNum) to match specBaseIdMap
- removeSeat(): use [data-row-label][data-col-num] selector
- submit(): refactor from 1 goods_params (zone-level) to N entries (seat-level, stock=1)
- Plan B fallback: if specBaseIdMap[key] missing, use sessionSpecId

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 19:56:25 +08:00
Council 098bcfe780 fix(P0): P0-1 idempotent ticket issuance, P0-3 XSS, P0-4 QR secret exception
P0-1: issueTicket() now checks for existing tickets by (order_id, spec_base_id)
      before inserting. Prevents duplicate tickets on HTTP retry/multi-instance.
P0-3: Removed |raw from simple_desc and content in ticket_detail.html.
      Prevents stored XSS via malicious admin content injection.
P0-4: getQrSecret() now throws exception if VR_TICKET_QR_SECRET is unset,
      instead of falling back to insecure default key.
2026-04-15 16:59:22 +08:00
Council 9171046435 fix(migration): add missing indexes + fix ALTER TABLE PDOStatement bug
- vr_tickets: add idx_created_at and idx_spec_base_id indexes
- vr_verifications: add idx_verified_at index
- fix PDOStatement+empty() bug → use rowCount() instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 15:51:11 +08:00
Council deacdedb01 council(execute): BackendArchitect - restore view files from merge conflict
Resolve Round 4 merge conflict by restoring verifier views and ticket
detail from commit 6f49b8355. All 7 admin view files now confirmed on main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:23:16 +08:00
Council 59928d1196 Merge council/FrontendDev: complete admin views + export button fix
Resolves merge conflicts:
- Verifier.php: keep parent (CONCAT fix, fetch-then-map pattern)
- ticket/detail.html, verifier/list.html, verifier/save.html: stage as-is (no conflict markers)
2026-04-15 14:21:46 +08:00
Council b9ef6ef675 council(execute): BackendArchitect - fix Verification.php column() bug + S4 audit log design
- Fix Verification.php:55 - ThinkPHP column() does not support multi-field
  mapping; replaced with select() + PHP foreach to build id-keyed array
- Add complete S4 audit log design to plan.md:
  - vr_audit_log table already exists in EventListener.php
  - AuditService interface design (log/getAdminContext/makeRequestId)
  - Integration points table for all sensitive operations
  - Append-only tamper-proof strategy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:20:14 +08:00
Council 2a6d7bdbf7 council(execute): FrontendDev - Round 4: export button fix + mark Phase 2 complete
- Fix P1 bug: ticket/list.html export button (GET→POST form) matching IS_AJAX_POST
- Mark all plan.md tasks complete (seat templates, tickets, verifiers, verifications views)
- BackendArchitect: AuditService.php (S4 design), Verifier.php CONCAT fix, Verification.php column() fix
- BackendArchitect: SeatTemplate.php countSeats fix, TicketService.php transaction fix
- BackendArchitect: EventListener.php audit_log table added
- SecurityEngineer: S1-S5 security audit complete
- [CONSENSUS: YES] all three agents vote YES

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:20:03 +08:00
Council 1d24075f4c Merge council/BackendArchitect: add missing verifier views + fix Verifier.php CONCAT bug 2026-04-15 14:12:24 +08:00
Council 255c8ed2bf council(review): SecurityEngineer - Phase 2 security audit complete + P1 Verifier.php fix
Security audit findings (Task S1/S2/S3/S5 done):
- Task S1: Admin auth chain verified (Base extends Common OK)
- Task S2: SQL injection audit complete (no injection, P1 code bug found)
  - FIXED: Verifier.php:45 CONCAT column() syntax error → select()+PHP concat
- Task S3: XSS/CSRF audit complete (no risk in admin context)
- Task S5: IDOR audit complete (admin context acceptable)
- Task S4 (audit log design): still pending

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:11:43 +08:00
Council 8ca3b2d67b council(execute): FrontendDev - merge Round 3 view files to main
FrontendDev Round 3 deliverables:
- All 7 admin view files (new + URL fixes)
- Resolve plan.md conflict: keep merged version + add Round 3 summary

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:11:36 +08:00
Council 6f49b8355c council(execute): BackendArchitect - add missing verifier views + fix CONCAT bug in Verifier.php
Round 3 completed:
- NEW: verifier/list.html (Layui table + search + enable/disable)
- NEW: verifier/save.html (user select + name + status form)
- NEW: ticket/detail.html (QR code + manual verify form)
- FIX: Verifier.php CONCAT column() → select() + PHP concat (P1)
- FIX: Ticket.php detail() adds $verifiers list for detail.html
- UPDATE: plan.md marks B1~B5 Done, S1~S5 pending SecurityEngineer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:11:35 +08:00
Council 649ae484e8 council(execute): FrontendDev - Round 3: complete all admin view files
- Fix all MyUrl() → PluginsAdminUrl() in seat_template/list.html, save.html
- Fix ticket/list.html + verification/list.html URLs
- Create ticket/detail.html (QR code, ticket info, goods/verifier linkage)
- Create verifier/list.html (search, status filter, disable)
- Create verifier/save.html (user select, name, status toggle)
- Update plan.md task status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:10:33 +08:00
Council b768d34dff council(execute): BackendArchitect - fix P0/P1 blocking issues in Phase 2
[P0] Fix plugin Base controller to extend ShopXO Common class:
  - Now extends Common instead of standalone class
  - Automatically gets IsLogin() + IsPower() + ViewInit()
  - All child controllers (SeatTemplate/Ticket/Verifier/Verification) inherit fix

[P1] Fix code bugs found during codebase analysis:
  - Verifier.php: column('nickname|username', 'id') → CONCAT SQL (syntax error)
  - SeatTemplate.php: countSeats() wrong logic (count × rows → per-row scan)
  - Ticket.php: verify() returned view on POST → always JSON
  - Ticket.php: detail() returned view on error → JSON
  - SeatTemplate.php: delete() returned view on POST → JSON, plus soft-delete

[P1] Fix verifyTicket() in TicketService:
  - Wrap in Db::transaction() for atomicity
  - Add SELECT ... FOR UPDATE pessimistic lock to prevent double-verify
  - Add try/catch with error logging

[P2] Fix export() memory issue:
  - Replace select() with cursor() to avoid OOM on large datasets

Also: update plan.md with Round 2 findings, claim Task B1/B2/B3/B5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:03:00 +08:00
Council 1afd547444 feat: import ShopXO v6.8.0 sourcecode (vendor/runtime excluded)
- ShopXO core + plugins/vr_ticket
- Goods.php item_type=ticket routing (Phase 1)
- vr_ticket plugin skeleton (Phase 0/1)
- Admin auth Base controller (Phase 2)
- All Phase 0/1/2 code included

Closes: tracks all ShopXO core modifications in monorepo
2026-04-15 13:09:44 +08:00