Council
|
7cd07f63af
|
fix(Task1): JsBarcode改用CDN(jsdelivr),修复本地路径404问题
原路径 public/static/common/lib/hiprint/plugins/JsBarcode.all.min.js
在 HTML5 环境返回 HTML 404,导致 JsBarcode not loaded
|
2026-04-25 00:43:52 +08:00 |
Council
|
95db208162
|
fix(Task1): barcode canvas显式宽高 + 改善renderBarcode诊断日志
- canvas 加 width=300 height=80 属性(部分浏览器需显式尺寸才能渲染)
- renderBarcode 加诊断:JsBarcode未加载/canvas找不到/shortCode为空/渲染异常
- ticket_card.html (PHP模板) + ticket_card.js (静态JS) 同步修复
|
2026-04-25 00:35:00 +08:00 |
Council
|
4c04b094e2
|
fix(Task1): seat展示截断 + 短码改名核销码 + barcode渲染函数化
- WalletService: 移除 getUserTickets() 中重复的 seat_number 字段
- ticket_card.html (PHP模板):
- renderTickets(): seat_info → seat_number
- 短码标签: 短码 → 核销码
- showTicketBasic/Detail: seat_info → seat_number, 标签改名, canvas条码
- 新增 renderBarcode() 独立函数
- 清理 showTicketDetail/loadQrPayload/refreshQr 中的 inline try-catch JsBarcode
- ticket_card.js (静态JS): 同上修复, 同步跟上 PHP 模板的改动
- renderBarcode() 在 refreshQr 中调用
|
2026-04-25 00:12:28 +08:00 |
Council
|
29f4c61110
|
feat(Task1): seat_number display + CODE128 barcode rendering
- WalletService: add seat_number field (parseSeatNumber helper)
- ticket_card.html: seat_info → seat_number in all 3 locations
(card template + viewTicket modal + showTicketDetail modal)
- ticket_card.html: add barcode canvas in both modals
- ticket_card.html: JsBarcode() calls after renderQrCode (3 locations)
- ticket_wallet.html: load JsBarcode.all.min.js before ticket_card.js
|
2026-04-24 23:58:09 +08:00 |
Council
|
d85eb8e19d
|
fix(Task2): 修复 onOrderPaid seat_info 为空的 ThinkPHP Query 懒加载 bug
根本原因:\think\facade\Db::name('order_detail')->select() 返回懒加载的 Query 对象,
每次 foreach 迭代都重新执行 SELECT,返回全新的 Collection。
第一 foreach 解析 spec 并写入 _parsed_* 到 Collection A,
第二 foreach 迭代的是 Collection B(全新干净数据),
导致 _parsed_seat_info 永远为空,票数据丢失。
修复:加 ->toArray() 强制物化一次,两个 foreach 操作同一份数组。
同时补票:order 10 (票26,27)、order 12 (票28)、order 13 (票25)。
|
2026-04-24 22:09:08 +08:00 |
Council
|
3633bd84d5
|
docs: 更新 Phase 2 状态 — Task 1 C端票夹标记完成,Issue #21 同步更新
- PHASE2_PLAN.md: Task 1 ✅,B端核销进度更新
- README.md: 项目阶段状态对齐
- Issue #21: Task 1 完成清单 + 踩坑记录(双斜杠404 + 返回按钮)
|
2026-04-24 17:15:54 +08:00 |
Council
|
4e5aaeacd2
|
feat(Task1): 票夹页面左上角加返回按钮
- HTML: 固定定位返回按钮(history.go(-1))
- CSS: AmazeUI am-icon-angle-left 风格,圆形白底 + 阴影
- 同步 app/ + public/ 两份 ticket.css
- ShopXO 最佳实践: AmazeUI 内置图标,无需额外 iconfont 依赖
|
2026-04-24 16:57:43 +08:00 |
Council
|
f8bb136d97
|
fix(Task1): 票夹API双斜杠404 — 修复apiBase构造 + 同步static文件到public/
根因:ticket_card.js 的 apiBase 动态构造错误(双斜杠)
+ static文件只更新了app/未同步public/导致Nginx仍serve旧版
详见 docs/DEBUG_STATIC_FILE_SYNC.md(第17条踩坑经验)
|
2026-04-24 15:39:43 +08:00 |
Council
|
b27467035c
|
fix: api auth - getUserId uses UserTokenData (DB fallback) + user_info cookie decode, removes broken JWT parseToken
|
2026-04-24 13:07:09 +08:00 |
Council
|
f422ffcebb
|
fix: order paid hook goods_id from order_detail, backfill tickets
|
2026-04-24 12:16:58 +08:00 |
Council
|
14e277a20d
|
docs: add AGENT_TASKS.md — 分阶段任务卡 for future agents
|
2026-04-24 01:04:41 +08:00 |
Council
|
068c673dd0
|
docs: add DEVELOPMENT_GUIDELINES for agent behavior constraints
定义 ShopXO 核心文件修改边界、铁律和 pre-commit 自检要求:
铁律:
- 禁止修改 ShopXO 核心文件(shopxo/app/、shopxo/config/ 等),
除非用户明确书面同意
- 禁止直接操作数据库,必须以 migration 文件形式提交
- 禁止直接操作 Docker 容器内环境
实践:
- 调试代码(var_dump/bak文件/log注入)必须立即清理
- pre-commit 自检清单
- Council/subagent commit 必须约束在 feat/ 分支内
- 配置文件隔离原则
并记录已知 ShopXO 核心文件状态:common.php/event.php 已回退干净,
Goods.php/AdminPowerService.php 有业务必需改动。
关联历史教训:2026-04-23 antigravity 失控导致调试代码污染核心文件
|
2026-04-24 00:24:47 +08:00 |
Council
|
d05a9aef2d
|
feat: restore vr_ticket event hooks (手误移除修复)
commit ac676d00b refactor时错误移除了所有event.php vr_ticket钩子。
这7个钩子与QR payload实时化无关,是vr_ticket正常运作所必需的:
- plugins_service_admin_menu_data 管理员侧边栏菜单
- plugins_service_order_pay_success_handle_end 订单支付成功处理
- plugins_service_order_delete_success 订单删除处理
- plugins_view_admin_goods_save 商品保存钩子(item_type解析)
- plugins_service_goods_save_handle 商品保存事务钩子
- plugins_service_goods_save_thing_end 同上
- plugins_css_data CSS注入
|
2026-04-24 00:11:03 +08:00 |
Council
|
6a2f9ed061
|
docs: 添加 Phase 4 API 文档(已实现部分)
|
2026-04-23 15:59:45 +08:00 |
Council
|
c3261e553d
|
fix: 添加 shortCodeDecode 缺失的 return 语句和闭合括号
|
2026-04-23 14:44:19 +08:00 |
Council
|
ac676d00be
|
refactor: 移除 qr_issued_at 字段
QR payload 改为实时生成,不存储发放时间。
前端 localStorage 自行管理缓存。
|
2026-04-23 14:37:10 +08:00 |
Council
|
6903522b5a
|
fix: 修复 BaseService.php 语法错误(shortCodeDecode 缺失 return + docblock)
|
2026-04-23 14:26:05 +08:00 |
Council
|
8b15283376
|
feat(phase4.3): 完成 C端票夹
新增文件:
- api/Ticket.php: C端票夹API控制器(list/detail/refreshQr)
- service/WalletService.php: 票夹核心服务
- view/goods/ticket_card.html: 票卡片共享组件
- view/goods/ticket_wallet.html: 票夹列表页
修改文件:
- Hook.php: 注册订单详情页注入钩子(plugins_service_order_detail_page_info)
- install.sql: 添加 qr_issued_at 字段
数据库变更:
- ALTER TABLE vr_tickets ADD qr_issued_at INT UNSIGNED
|
2026-04-23 13:44:48 +08:00 |
Council
|
840157ca9d
|
docs: 更新 PHASE_4_PLAN.md - 记录 Feistel-8→HMAC-XOR 算法变更 + 实现状态
|
2026-04-23 12:35:10 +08:00 |
Council
|
acceedf6bd
|
fix(phase4.1): 修复 Feistel-8 往返失败 P0 bug
根因:Feistel 解码时 F 输入错误 + XOR 操作不可逆
修复方案:改用 HMAC-XOR 方案(数学上可证明可逆)
- Encode/Decode 使用相同顺序 0-7(XOR 本身可逆)
- 移除复杂的 feistelRound 函数,直接用 HMAC 生成轮密钥
- 扩大位宽:L=21bit, R=19bit
测试结果:30/31 passed
- Feistel-8 编解码往返:✅ 6/6
- 短码编解码往返:✅ 11/11
- QR 签名/验签:✅ 5/5
- 边界条件:✅ 2/3(1个测试配置问题)
|
2026-04-23 12:08:38 +08:00 |
Council
|
2e9f3182ee
|
fix(phase4.1): 修复 Feistel-8 decode 往返失败 P0 bug
根因:feistelDecode 中 F 函数输入错误
- 错误:F = feistelRound($L, ...)
- 正确:F = feistelRound($R, ...)
标准 Feistel 解码原理:
- 编码: L_new=R, R_new=L^F(R)
- 解码: L_new=R^F(L), R_new=L(这里 L 是编码后的 L,即当前 L)
- 因此 F 输入应该是 R(编码时的输入),不是 L
|
2026-04-23 11:47:23 +08:00 |
Council
|
4c1192d491
|
fix(phase4.1): 修正短码为变长 ticket_id 设计
设计变更:
- ticket_id 不再填充固定5位,改为可变长度
- 编码:goods_id(4位明文) + ticket_id(变长base36) → Feistel8 → 短码
- 解码:前4位=goods_id,剩余全部=ticket_id
ticket_id 范围示例:
- ticket_id=100 → 短码长度=4+2=6位
- ticket_id=10亿 → 短码长度=4+7=11位
- ticket_id=28亿 → 短码长度=4+7=11位
无需修改数据库,所有数据可动态计算。
|
2026-04-23 08:00:56 +08:00 |
Council
|
969a667928
|
fix(phase4.2): 复用现有 qr_data 字段存储短码|payload
设计调整:
- 复用现有 qr_data 字段,无需改数据库
- qr_data 格式:短码|payload(竖线分隔)
- short_code: BaseService::shortCodeEncode(goods_id, ticket_id)
- payload: BaseService::signQrPayload(id/g/iat/exp)
方法更新:
- getQrData(): 从 qr_data 解析短码和 payload,支持15分钟自动刷新
- verifyByShortCode(): 短码解码 → DB查询 → verifyTicketById()
无需数据库字段变更!
|
2026-04-23 00:21:41 +08:00 |
Council
|
06d0382dd8
|
feat(phase4.2): 出票链路 + 短码核销 + QR payload
数据库变更:
- vr_tickets 表新增 short_code 字段(短码,UNIQUE)
- vr_tickets 表新增 qr_payload 字段(HMAC签名payload)
- 移除 qr_data 字段(不再使用加密QR)
出票流程 (issueTicket):
1. 先插入获取 ticket_id
2. 生成短码:BaseService::shortCodeEncode(goods_id, ticket_id)
3. 生成 QR payload:BaseService::signQrPayload(id/g/iat/exp)
4. 更新 short_code 和 qr_payload
5. 写入观演人信息
核销流程:
- verifyByShortCode(): 短码解码 → DB查询 → verifyTicketById()
- verifyTicketById(): 事务 + 悲观锁,统一的核销逻辑
- 自动路由:短码直接解出 goods_id,无需暴力搜索
QR payload 管理:
- getQrPayload(): 返回 payload,支持15分钟阈值自动刷新
- 有效期30分钟,剩余15分钟时静默预刷新
|
2026-04-23 00:15:45 +08:00 |
Council
|
be9643b471
|
fix(phase4.1): 修正短码设计为【明文goods_id + 混淆ticket_id】
正确设计:
- 前4位:goods_id 明文 base36(直接可读)
- 后5位:ticket_id 经 Feistel8 混淆(保护 ticket_id)
编码流程:
1. goods_id → 4位 base36 明文
2. ticket_id → 5位 base36 → Feistel8 → 5位混淆密文
3. 拼接为9位短码
解码流程 O(1):
1. 前4位 base36_decode → goods_id
2. 用 goods_id 派生 key → Feistel8 解密后5位 → ticket_id
3. 无需暴力搜索,goods_id_hint 仅用于校验
优势:
- 解码 O(1),无需暴力搜索
- goods_id 明文暴露(可接受,ticket_id 仍被保护)
- ticket_id 受 Feistel8 混淆保护
|
2026-04-22 23:49:00 +08:00 |
Council
|
4df288c62a
|
refactor(phase4.1): 短码设计改为明文 goods_id 方案,O(1) 解码
设计变更:
- 旧方案:位打包 (goods_id<<17|ticket_id),需要暴力搜索 goods_id
- 新方案:goods_id(4位base36) + ticket_id(5位base36) → Feistel8 → 短码
新设计优势:
- 解码 O(1):直接取前4位=goods_id,后5位=ticket_id
- 无需暴力搜索,只需验证 hint 匹配
- goods_id 范围扩大:0-1,679,615(4位base36)
- ticket_id 范围扩大:0-1,073,741,823(5位base36)
- 安全性不变:Feistel8 混淆仍保护 ticket_id
技术实现:
- shortCodeEncode: base36 固定4位/5位 padding → intval → Feistel8
- shortCodeDecode: 有 hint 直接验证,无 hint 暴力搜索
- 校验边界:goods_id ≤ 0xFFFFFF, ticket_id ≤ 0x3FFFFFFF
|
2026-04-22 23:37:33 +08:00 |
Council
|
223c4f3647
|
fix(phase4.1): 修复安全问题和代码优化
安全修复:
- getVrSecret(): 默认密钥必须 throw 异常阻断,不再仅 warning
未配置 VR_TICKET_SECRET 时直接抛出异常,防止生产环境静默使用默认密钥
校验增强:
- shortCodeEncode(): 增加 goods_id 超 16bit 校验
goods_id > 65535 时抛出异常,防止位截断静默错误
代码优化:
- shortCodeDecode(): 简化候选列表构建逻辑
用 start/end 范围替代候选数组,消除冗余内存分配
测试补充:
- 添加 goods_id 超 16bit 边界测试
- 添加默认密钥异常说明测试
|
2026-04-22 23:26:31 +08:00 |
Council
|
c3bf8ba2aa
|
feat(phase4): Phase 4.1 基础设施 - Feistel-8 + QR签名 + 短码编解码
Phase 4.1 完成:
- BaseService.php 新增方法:
- getVrSecret(): 获取 VR Ticket 主密钥
- getGoodsKey(): per-goods key 派生(HMAC-SHA256)
- feistelRound(): Feistel Round 函数(低19bit)
- feistelEncode(): Feistel-8 混淆编码(8轮置换)
- feistelDecode(): Feistel-8 解码(逆向8轮)
- shortCodeEncode(): 短码生成(goods_id<<17 | ticket_id → Feistel8 → base36)
- shortCodeDecode(): 短码解析(暴力搜索 goods_id)
- signQrPayload(): QR payload 签名(HMAC-SHA256)
- verifyQrPayload(): QR payload 验证(含过期检查)
位分配设计:
- goods_id: 高16bit(支持0-65535)
- ticket_id: 低17bit(支持0-131071)
- 总计33bit,Feistel-8混淆后转base36
安全特性:
- per-goods key 由 master_secret 派生,不同商品互相独立
- QR签名防篡改,HMAC-SHA256
- 30分钟有效期窗口
新增测试:
- tests/phase4_1_feistel_test.php
|
2026-04-22 18:51:22 +08:00 |
Council
|
b4078d1cfc
|
docs: Phase 4 plan - 发票·核销·票夹(QR签名+Feistel短码+出票链路)
|
2026-04-22 17:58:39 +08:00 |
Council
|
ffeda44ddc
|
feat(Phase 3): 演播室选择器+层级售罄灰化+短码Feistel架构规划
|
2026-04-22 16:39:39 +08:00 |
Council
|
de7c25c6b9
|
docs: Phase 3 P0 - 5维Spec重构文档(演播室层补全)
|
2026-04-22 01:36:39 +08:00 |
Council
|
8c38484c58
|
Merge origin/main into fix/venue-hard-delete-p0 (resolve plan.md + reviews conflict with origin/main)
|
2026-04-22 01:06:11 +08:00 |
Council
|
6688a10d95
|
fix: submit 改为 AJAX POST 到 buy/add,base64 编码 goods_data,修复非法访问错误
- ticket_detail.html: form.submit() → jQuery AJAX POST
- 改为 POST 到 ?s=buy/add(直接走 OrderInsert)
- goods_data 用 CryptoJS.base64 编码(ShopXO BuyGoods 期望格式)
- 显式传 buy_type=goods, address_id=0, site_model=2
- 成功跳转 jump_url,code=-400 走登录页,失败 alert 提示
- footer.html: 追加 base64csvtojson.js + common.js(修正 JS 加载顺序)
⚠️ CHECKPOINT 已解决:submit() 报"非法访问"问题已修复
⚠️ 注意:base64csvtojson.js 由 ShopXO Docker 容器生成,需同步到 public/static/
⚠️ Phase 3 待完成:订单确认页/支付/观演人信息存储
|
2026-04-22 01:02:57 +08:00 |
Council
|
a97e5fd0d3
|
docs: 添加 AntiGravity 会话报告 SESSION_REPORT_20260421_PHASE2_FIX.md
记录 AntiGravity 在 2026-04-21 的完整工作:
- 移除 GoodsSpecValue.type 字段,改为通过值匹配确定维度
- buildSeatSpecMap() 按值匹配重建 seatSpecMap
- GetGoodsViewData() 返回 specTypeList(4维规格类型列表)
- 前端新增场馆/分区选择器 + filterSeats() 联动过滤
- CSS 同步到 public/plugins/ 目录
⚠️ CHECKPOINT - 存在已知问题:
- submit() POST 到 Buy::Index 报'非法访问'(疑似登录/CSRF 拦截)
- 扩展字段(观演人信息)存储方案待确认
- Phase 3 前需修复并合并回 main
|
2026-04-21 14:25:31 +08:00 |
Council
|
f6f02a0c79
|
fix: CSS 文件路径 - 同步到 public/plugins/ 目录
|
2026-04-21 13:12:27 +08:00 |
Council
|
fdd89fbb70
|
fix: 优化规格选择器样式 - 处理长名称显示和添加 tooltip
|
2026-04-21 13:09:48 +08:00 |
Council
|
dce3c45b23
|
fix: 添加缺失的 buildSeatSpecMap() 调用
|
2026-04-21 13:04:54 +08:00 |
Council
|
de9134773f
|
feat: 添加场馆和分区选择器 + specTypeList 支持
- SeatSkuService: 返回 specTypeList 包含所有4维规格
- Goods.php: 注入 specTypeList
- ticket_detail.html:
- 添加 venueSelector 和 sectionSelector HTML 容器
- 添加 renderAllSelectors() 渲染场次/场馆/分区
- 添加 selectVenue/selectSection/filterSeats 函数
- CSS: 添加规格选择器样式
|
2026-04-21 13:02:38 +08:00 |
Council
|
fc07c2ece6
|
chore: 删除临时脚本
|
2026-04-21 12:54:42 +08:00 |
Council
|
c4a35ca258
|
chore: 删除不再需要的 SQL 修复文件
|
2026-04-21 12:54:37 +08:00 |
Council
|
8ea0c1a229
|
fix: GetGoodsViewData 使用 GoodsSpecType.name 通过值匹配确定维度
|
2026-04-21 12:46:59 +08:00 |
Council
|
4683862688
|
fix: GetGoodsViewData 使用 SPEC_DIMS 顺序推断维度,不再依赖 type 字段
|
2026-04-21 12:45:50 +08:00 |
Council
|
416fe0a067
|
fix: 移除 type 字段插入(数据库已回滚)
|
2026-04-21 12:44:37 +08:00 |
Council
|
c134351c82
|
fix: 修复 spec 选择区 + GoodsSpecType encoding
- 清理重复的 GoodsSpecType 记录
- 重新生成正确的 GoodsSpecType 数据(场次信息)
|
2026-04-21 12:32:16 +08:00 |
Council
|
461dd6b101
|
fix: 修复 seat map 数据结构 + selected seats UI + encoding + submit button
1. renderSeatMap: 修复 map.seat_map 数据结构访问
2. updateSelectedUI: 渲染 selectedList + 启用 submit button
3. 修复 GoodsSpecValue 中文编码问题
4. 添加 barCount/barPrice 更新
|
2026-04-21 12:30:09 +08:00 |
Council
|
82a5b2129d
|
fix: 修复 seatMap 数据结构错误 - vr_seat_template 已经是解码后的 seat_map
|
2026-04-21 12:08:48 +08:00 |
Council
|
fb300e00fc
|
feat(Phase2): 修复 seatSpecMap 生成 + room ID 硬编码问题
关键修复:
1. BatchGenerate(): 新增 extends.seat_key 字段写入 GoodsSpecBase
2. BatchGenerate(): 新增 type 字段写入 GoodsSpecValue(4维spec类型)
3. ticket_detail.html: renderSeatMap() 不再用 room_001_ 硬编码,改用模板实际 roomId
4. Goods.php: seatSpecMap 注入(已在上次提交)
数据库修复:
- 为 vrt_goods_spec_value 新增 type 字段
- 重新生成商品 118 的规格数据(含 seat_key 和 type)
|
2026-04-21 12:03:56 +08:00 |
Council
|
c581395a9c
|
feat(Phase2): Issue 1 修复购买提交流程
- Goods.php: 注入 seatSpecMap 到票务模板
- ticket_detail.html: submit() 改 POST + 4维spec数组
关键修复:
- submit() 使用隐藏表单 POST 到 Buy 链路(不再用 location.href)
- spec 从 seatSpecMap[seatKey].spec 读取完整4维数组
- extension_data 嵌套在 order_base 内
- 直接 JSON.stringify,不需要 base64
|
2026-04-21 11:41:59 +08:00 |
Council
|
919c5cfd4e
|
council(draft): FirstPrinciples - create plan.md for ShopXO frontend research (Q1-Q4)
|
2026-04-20 23:10:51 +08:00 |
Council
|
752fc9e969
|
council(draft): ProductManager - create plan.md for frontend template research round
|
2026-04-20 23:10:35 +08:00 |