Releases: NovaWorks/ZCard
Release list
v1.16.0 — 后台新增「数据迁移」控制台
v1.16.0
🚚 后台新增「数据迁移」控制台:1.x → 2.0 迁移的源端入口
ZCard 2.0(Go 重写版,仓库 NovaWorks/zcard-next)已具备
一键全量数据迁移能力(zcard migrate-from-v1 子命令,P0-P6 全阶段交付)。本版本为
1.x 侧补上运维入口:后台「系统设置 → 数据迁移」页面,覆盖迁移前自检、命令生成、
密钥包导出三件事——站长不需要登录服务器翻配置文件即可完成迁移准备。
1. 新页面:系统设置 → 数据迁移
- 源端自检(只读,不改任何数据):
APP_KEY/ 卡密加密钥匙可用性检测(含后台配置与.env双来源解析);- 卡密抽样:随机抽取 10 条做密文/明文形态识别与解密验证(密钥错配当场暴露);
- 数据库版本探测:金额列停留在「元」小数形态(v1.9 之前的旧库)会直接标红,
提示先升级 1.x 再迁移; - 数据规模概览(用户/商品/卡密/订单/支付/流水行数)与在途订单提醒
(待付款订单/待确认支付计数——切换窗口前排空依据); - 软删除数据提示(迁移会带上软删数据,站长知情)。
- 命令生成:按本站配置自动生成 2.0 侧执行命令(dry-run 演练 / 正式迁移 /
主站先行--phase 0-5/ 迁移后校验--verify-only),一键复制;
附五步切换流程指引(准备 2.0 → 密钥包 → 演练 → 切换窗口 → 回滚预案)。 - 密钥包下载:导出
zcard-v1-migrate.env(含APP_KEY、卡密钥匙、时区、数据库连接),
复制到 2.0 服务器供--old-env使用;下载需二次确认,页面明示 600 权限与迁移后删除。
2. 新增 API(admin 角色守卫 + 审计)
| 端点 | 用途 |
|---|---|
GET /api/admin/migration/preflight |
源端自检(密钥/卡密抽样/版本探测/规模/在途单) |
GET /api/admin/migration/command |
2.0 侧命令与切换清单 |
GET /api/admin/migration/keys |
密钥包导出(含库凭据,仅管理员) |
3. 配套交付
docs/重构/generate-v1-crypto-fixtures.php:用真实 Laravel 加密器生成 golden vector
测试样本(已用于钉死 2.0 侧解密兼容性),供后续回归;docs/重构/数据迁移工具开发计划.md:逐表字段映射规格与七阶段交付日志
(settings 首批映射、状态映射总表、加密口径附录等)。
4. 迁移能力速览(2.0 侧已交付,供规划参考)
- 覆盖:设置/货币/会员等级、用户(密码原样可用)与管理员、商品目录、卡密
(换算法重加密+防重)、订单五表(老单号可继续查询)、支付/充值/优惠券、
余额流水与对账差异清单、分站分批补迁、供货商、媒体文件、访问/审计日志; - 幂等可重跑(中断续跑、重跑即增量);
--dry-run预检、--verify-only事后校验; - 100 万卡密实测 6 分 41 秒零失败;全程只读 1.x 数据库。
升级说明
- 常规升级:后台「系统设置 → 在线更新」或
git pull && composer install;
本版本无数据库结构变更、无配置迁移; - 前台/后台前端产物已随包更新(
public/admin),无需 Node 环境。
v1.15.0 缺货商品禁止下单 — 上游商品付款前拒单 + 前台售罄态
v1.15.0
🛡️ 缺货商品禁止下单:上游商品付款前拒单 + 前台售罄态
1. 根因:上游货源商品下单完全不校验库存
此前只有本地自动发卡(auto_card)商品在下单事务内锁卡校验库存,上游货源(upstream)
商品下单时不看库存——顾客可以对零库存商品直接付款,付款后才去上游拿货、拿不到
则发不出货,造成「付款成功却收不到卡密」的客诉与退款。
本次修复(app/Support/OrderService::createOrder()):
- 上游商品在下单入口依据同步缓存
stock_cache付款前拒单:缓存为 0 或小于
购买数量时直接抛InsufficientStockException(HTTP 422「库存不足」),
订单不落库、不进入支付流程; stock_cache = null(上游库存未知)与-1(上游不限量)仍放行,由付款后的
上游拿货流程兜底,不改变既有语义;- 固定内容(fixed)/人工发货(manual)商品本就不限量,不受影响;
- 自动发卡商品的既有锁卡防超卖校验保持不变。
2. 前台商品详情页:缺货禁购(storefront)
- 库存为 0 时商品主图盖「已售罄」半透明遮罩;
- 「加入购物车」按钮隐藏,「立即购买」置灰不可点(脚本层同样拦截,
防止绕过 UI 直接触发); - 详情页仍可正常浏览(商品说明/评价/SEO 不受影响)。
3. 前台商品列表:售罄态标识(storefront)
- 网格卡片缺货时封面盖「已售罄」角标;
- 列表模式的「购买」按钮置灰为「已售罄」,不可点击。
提示:后台「设置 → 显示缺货商品」开关(默认开)可控制缺货商品是否出现在
商城列表;关闭后缺货商品直接不展示,与本次禁购逻辑互为双保险。
测试
- 新增
tests/Feature/UpstreamStockOrderGuardTest.php(5 用例:零库存拒单、
购买数超缓存拒单、缓存内正常下单、库存未知放行、上游不限量放行); - 全量
php artisan test:458 passed,1562 assertions;Laravel Pint 通过; - storefront 前端产物已重建(
public/storefront)。
升级说明
- 本版本不包含数据库迁移;
- 已含前端变更,生产部署需同步
public/storefront(仓库已带编译产物); - 更新后请执行
php artisan queue:restart; - 缺货拦截依据的是库存同步缓存,请确认货源的「定时同步库存」任务正常运行
(缓存越新拒单越准;同步间隔内上游售罄仍由付款后拿货失败兜底)。
v1.14.9 兼容出海版 ACG-Faka 并修复充值/验证码/定价输入
v1.14.9
🐛 修复:出海同款版(acg-3.1.1Max)货源对接 + 三处后台/前台体验问题
1. 货源同步:兼容出海同款版 ACG-Faka(修复 chuhaitg.com 等站点同步失败)
部分上游(如 chuhaitg.com,运行 acg-3.1.1Max-chuhai「出海同款版」)与标准 ACG-Faka 协议存在差异,
导致库存同步报 UPSTREAM INVALID RESPONSE、商品同步整体失败、影响发货:
- 出海版没有
/shared/commodity/stock路由,库存接口为/shared/commodity/inventory
(参数sharedCode、返回data.count); - 商品详情接口只认
sharedCode(标准版为code); - 对不存在的路由返回 HTTP 200 的 JS + Base64 包装 404 页(非 404 状态码),
v1.14.8 的「明文/固定片段 404 检测」无法识别。
本次修复(app/Supply/Drivers/AcgFakaDriver.php):
- 库存查询改为三候选探测(每个驱动实例只探测一次,按协议演进排序):
stock(3.1.3+,code/stock)→item(旧版,code/stock)→inventory(出海版与标准新版均有,
sharedCode/count); - 404 页识别增强:除明文外,解码响应内 Base64 片段后再找「404 Not Found」,
覆盖 JSatob混淆包装页(正则不再依赖词边界,避免 hex 前缀粘连导致漏判); - 商品详情参数名自动探测(code/sharedCode),后台「勾选商品重新获取」的
getProduct兜底路径同样受益; - 保留 v1.14.8 的安全边界:真实业务错误(密钥错误/商品不存在/已停售)立即失败,
不再继续探测,防止误锁 inventory 模式后把手动发货商品记为零库存;
只有「不能为空/缺少参数」类参数名不符才降级下一候选; - WAF/Cloudflare 普通 HTML、限流、5xx 的重试与诊断语义不变。
2. 后台用户充值:改为「元」输入
用户管理编辑抽屉的余额输入原以「分」为单位(输入 1000 仅到账 10 元)。现改为
元为单位、两位小数(与账单管理调账口径一致),加载时分→元、提交时元→分,
并更新提示文案(中/英)。
3. 登录验证码:修复开启后不显示、且永远校验失败
两层叠加问题:
- 图片不显示:登录/注册/找回密码页的验证码
watch未加immediate,
当设置从 localStorage 缓存同步恢复时回调永不触发;找回密码页还把返回 JSON 的
接口 URL 直接当图片src(必然裂图); - 校验必失败:
/api/captcha/{scene}为无状态 API 模式(答案存缓存),
而登录/注册/找回密码校验走的却是 Session 模式captcha_check,永远不通过。
修复(前台 storefront、后台 sysadmin、后端三处统一为无状态 key 模式):
- 三页
watch增加immediate: true,找回密码页改为解析 JSON 取src; - 前后端登录提交均携带
captcha_key,AuthController(login/register/sendResetCode)
校验时配对 key; /api/captcha/{scene}响应新增enabled字段,后台登录页一次请求即可拿到
开关状态 + 图片 + key(不再依赖 Session 模式的/captcha/config);- 未升级的旧客户端仍回退 Session 校验,行为兼容。
4. 货源商品预览:定价策略输入框被 loading 遮罩锁死
预览抽屉把 v-loading 遮罩套在整个内容区,而上游商品预览请求超时放宽到了 120 秒——
上游慢或失败(如出海版站点)期间,定价策略的加价输入框被遮罩盖住完全无法编辑。
现将遮罩收窄到「分类映射 + 商品列表」数据区,定价策略/工具栏随时可操作;
加载中不再误显示「暂无商品」。
测试
- 新增
tests/Feature/AcgFakaChuhaiCompatTest.php(3 用例:inventory 回退全目录、
商品详情参数名重试、业务错误优先于路由错误); AdminLoginCaptchaTest补充无状态 key 登录全链路用例;TradeCaptchaTest适配enabled字段;- 全量
php artisan test:453 passed,1553 assertions;Laravel Pint 通过; - sysadmin / storefront 前端产物已重建(
public/admin、public/storefront)。
升级说明
- 本版本不包含数据库迁移;
- 已含前端变更,生产部署需同步
public/admin与public/storefront(仓库已带编译产物); - 更新后请执行
php artisan queue:restart,使常驻 worker 加载新的库存探测逻辑; - 出海版货源更新后请在后台点一次「同步商品」验证库存补查正常。
v1.14.8
v1.14.8
🐛 修复:兼容旧版 ACG-Faka 库存接口
本版本修复旧版 ACG-Faka 未提供 /shared/commodity/stock 时,商品同步因 HTTP 200 HTML 404 页面而全部失败的问题。
库存接口兼容
- 继续使用新版
/shared/commodity/stock获取实时库存; - 仅在确认库存路由不存在时,切换到旧版已有的
/shared/commodity/item商品详情接口读取stock; - 批量同步首次确认旧版路由后,后续商品直接使用兼容接口,避免对不存在的库存路由重复请求;
- WAF、Cloudflare、限流和临时网关 HTML 不触发兼容降级,保留原有重试与错误诊断;
- 不使用旧版
/shared/commodity/inventory作为回退,避免人工发货商品被错误识别为零库存。
测试
- ACG-Faka 库存与协议定向测试:21 passed,98 assertions;
- 全量
php artisan test:449 passed,1529 assertions; - Laravel Pint 与
git diff --check通过。
升级说明
- 本版本不包含数据库迁移;
- 本版本不包含前端变更,无需重建前端产物;
- 更新后请执行
php artisan queue:restart,使常驻队列 worker 加载新的库存兼容逻辑。
v1.14.7
v1.14.7
🐛 修复:完整对齐 ACG-Faka 供货协议
本版本修复 ACG-Faka 商品同步和上游拿货链路中仍未按对接手册处理的问题。
协议与请求
- 正式货源驱动和商品调试请求的默认超时时间统一调整为 60 秒,并允许在 5–60 秒范围内配置;
- 按手册修正 MD5 签名参数,
app_key只在本地参与签名,不再随请求发送; - 禁止上游请求自动跟随重定向,避免凭据或签名被转发到非预期地址。
商品同步与下单
- 支持同步 ACG-Faka 的
race、多维 SKU、最小/最大购买数量、联系方式类型和动态下单控件; - 在本地 SKU 中保存上游规格选择,下单及查库存时还原为 ACG-Faka 所需的
race/sku参数; - 商城结算页支持选择框、单选、复选、文本域等动态控件,并在后端校验必填项、选项和正则规则;
- 动态控件商品改为单品结算,避免购物车批量下单时控件值绑定错误。
成本修复
- 商品目录成本改用手册定义的当前会员价
user_price,不再误用上游站长成本factory_price; - 上游交易成功后,使用接口返回的实际扣款金额回写订单成本,确保利润统计准确。
测试
- 全量
php artisan test:447 passed,1507 assertions; - 管理后台
vue-tsc --noEmit与生产构建通过; - 商城生产构建通过;
- Laravel Pint 与
git diff --check通过。
升级说明
- 本版本包含数据库迁移,请在更新后执行
php artisan migrate --force; - 已重建并提交管理后台及商城前端产物;
- 请重启常驻队列 worker,使新的上游商品 DTO、请求超时和下单参数逻辑立即生效。
v1.14.6 延长 ACG-Faka 商品调试请求超时
v1.14.6
🐛 修复:延长 ACG-Faka 商品调试请求超时时间
现象:管理员通过货源商品调试接口直接请求 ACG-Faka
/shared/commodity/items 时,大型商品目录或上游响应较慢时可能在 30 秒处超时。
修复:
- 将该表单 POST 请求的超时时间从 30 秒延长到 60 秒;
- 保持其他上游驱动及支付、回调请求的超时策略不变;
- 新增回归测试,直接断言传给 HTTP 客户端的
timeout为 60。
测试
- 货源管理专项测试:7 passed,24 assertions;
- 全量
php artisan test:439 passed,1467 assertions; - Laravel Pint 与
git diff --check通过。
升级说明
- 本版本不包含数据库迁移;
- 不需要重建管理后台前端产物;
- 不需要重启队列 worker。
v1.14.5 渠道编辑站点地址回填修复
v1.14.5
🐛 修复:渠道编辑时站点地址无法回填
现象:在渠道管理中编辑已有货源时,必填的“站点地址”显示为空,管理员每次保存前都需要
重新填写;列表页仍能正常显示该地址。
根因:货源接口同时返回顶层 base_url 和动态凭证中的 credentials.base_url。后端误将
动态凭证中的站点地址当作密钥脱敏,前端又把脱敏占位值识别为敏感字段并清空,导致编辑表单
未使用列表中已有的真实地址。
修复:
- 将公开的
base_url明确列为非敏感配置字段,继续对 API 密钥等真实凭证进行脱敏; - 编辑表单统一以顶层
base_url作为站点地址的回填真理源,兼容旧接口和历史数据; - 新增接口回归测试,确保站点地址可回填,同时 API 密钥仍保持脱敏;
- 重新构建并提交管理后台生产产物。
测试
- 货源管理专项测试:6 passed,22 assertions;
- 全量
php artisan test:438 passed,1465 assertions; - 管理后台 TypeScript 检查与生产构建通过;
- Laravel Pint、构建产物引用检查与
git diff --check通过。
升级说明
- 本版本不包含数据库迁移;
- 不需要重启队列 worker;
- 更新后刷新管理后台页面,即可在编辑渠道时看到已保存的站点地址。
v1.14.4 重复心跳误取消同步任务修复(issue #43)
v1.14.4
🐛 修复:重复心跳被误判为同步任务取消(issue #43)
现象:大型货源同步在处理到第 10 个商品后可能无故变成 cancelled,但
cancel_requested_at 为空,也没有管理员取消审计。该问题在 MariaDB/MySQL 的 changed-rows
语义下可稳定触发。
根因:同步 Job 在每 10 件商品边界会连续写入相同阶段与进度。若两次心跳发生在同一秒,
第二次 UPDATE 没有实际改变字段,数据库会返回 affected rows 0。旧状态机把 0 直接当成
status != running,随后错误进入取消终态。
修复:
- 继续使用
WHERE status = running的条件更新,保留取消、超时与迟到 worker 之间的并发保护; - affected rows 为
0时重新读取任务状态:仍为running即视为心跳成功; - 只有状态为
cancelling时才完成取消,其他终态仍会阻止 worker 继续执行; - 新增 MariaDB changed-rows 语义回归测试,并覆盖第 10 件后的重复心跳与完整任务成功。
测试
- 心跳专项回归:2 passed,4 assertions;
- 同步任务测试:23 passed,99 assertions;
- 全量
php artisan test:437 passed,1461 assertions; - Laravel Pint 与
git diff --check通过。
升级说明
- 本版本不包含数据库迁移;
- 升级后必须执行
php artisan queue:restart(或重启 Supervisor/systemd worker),让常驻 worker
加载 v1.14.4 的心跳状态机; - 已被误标为
cancelled的历史任务不会自动恢复,可在升级并重启 worker 后重新发起同步。
v1.14.3
v1.14.3
🐛 修复:大型 ACG-Faka 目录库存补查触发限流后整批失败(issue #40)
现象:ACG-Faka 的商品列表不会返回手动发货商品的真实库存。旧版本会把所有缺失库存商品
按固定 10 并发连续请求 /shared/commodity/stock,大型目录会产生明显请求突发;任何一个请求
遇到 429、5xx、连接异常或 Cloudflare/WAF 返回的伪 200 HTML,都会立即终止整个同步。
修复:
- 库存补查默认并发从固定 10 调整为 3,可在「货源管理 → 定时任务」配置 1–10;
- 增加独立的库存批次间隔,默认 200ms,可配置 0–10000ms,不再复用只对商品分页生效的
request_delay; - 429、5xx、连接异常和非 JSON 网关页面仅重试失败商品,最多 3 次,按 250ms、500ms
指数退避;成功商品不重复请求; - 401/403/404 和上游业务错误不盲目重试,库存无法确认时继续安全失败,禁止把未知库存当成无限库存;
- 非 JSON/HTTP 异常诊断新增脱敏的 HTTP 状态、Content-Type、CF-Ray、安全 Location、正文长度、
SHA-256、商品 code 和重试次数,不记录请求头、凭据、Location 查询串或正文全文; - 根据商品数量、并发数和批次间隔计算主动限速预算;预计等待超过 600 秒时在发出库存请求前
给出可操作错误,避免任务必然撞上 900 秒 Job 超时; - 大目录库存回写由逐商品扫描完整数组改为一次索引映射,复杂度从 O(n²) 降为 O(n)。
管理端
货源「定时任务」弹窗新增:
- 库存补查并发:默认 3,范围 1–10;
- 库存批次间隔:默认 200ms,范围 0–10000ms。
这两个配置会同时作用于手动和定时触发的 ACG-Faka 采集/价格同步。生产编译产物已随版本提交,
服务器无需安装 Node.js 或重新构建前端。
测试
- ACG-Faka 库存专项:7 passed,30 assertions;
- ACG-Faka 协议、同步任务、计划任务组合:47 passed,174 assertions;
- 全量
php artisan test:435 passed,1457 assertions; - Laravel Pint 与
git diff --check通过; - sysadmin
vue-tsc --noEmit与生产构建通过; - 编译产物引用及
stock_concurrency/stock_request_delay_ms内容检查通过。
升级说明
- 本版本不包含数据库迁移;
- 升级后必须执行
php artisan queue:restart(或重启 Supervisor/systemd worker),让常驻 worker
加载 v1.14.3 的新重试与限速逻辑; - 若低并发单请求仍持续收到伪 200 HTML/内嵌 404,说明上游 Cloudflare/WAF/伪静态路由仍需修复;
本版本会在 3 次有界重试后保留完整脱敏诊断,但不会绕过上游安全策略。
v1.14.2 队列探针误报与取消审计修复(issue #42)
v1.14.2
🐛 修复:大型货源同步时队列状态误报(issue #42)
现象:生产环境只有一个 queue:work worker 时,大型货源同步会长时间占用该 worker。
后台队列探针与同步任务同在默认队列,探针无法插队执行;20 秒后旧逻辑就显示
「队列未开启:同步任务不会执行」,即使同步任务仍在正常推进。
根因:队列状态接口只读取 QueueHeartbeatJob 的缓存心跳,没有使用
supply_sync_tasks.heartbeat_at 这条已经存在、且能直接证明 worker 正在处理同步任务的运行信号。
修复:队列健康状态改为双信号判断:
- 探针在 20 秒内执行时返回
healthy; - 探针过期、但运行中任务的心跳仍在任务看门狗阈值内时返回
busy; - 两种心跳都过期时才返回
unavailable。
后台会把 busy 显示为「worker 正在执行同步任务,探针正在排队,任务心跳正常」,
不会再诱导管理员取消仍在正常运行的任务。本修复兼容现有单 worker 部署,
不要求额外配置 health 队列或第二个 worker。
🔎 新增:同步任务取消审计
取消任务现在会持久化以下信息:
- 首次发起取消的管理员 ID 与当时的显示名称;
- 请求来源 IP;
- 触发入口(
admin/system); - 可选取消原因和原有的取消时间。
后台取消按钮增加二次确认和可选原因输入,任务详情会展示完整取消审计。
审计字段只在首次从 queued/running 转入取消状态时原子写入,重复点击不会覆盖首位操作者和原因。
测试
SupplySyncTaskTest新增并扩展 4 类回归:- 探针被长任务阻塞、任务心跳新鲜时返回
busy; - 陈旧任务心跳不能掩盖不可用 worker;
- 取消审计完整写入且重复请求保持幂等;
- 运行中任务在每 10 件商品的检查点响应取消。
- 探针被长任务阻塞、任务心跳新鲜时返回
- 聚焦测试:21 passed,95 assertions。
- 全量
php artisan test:430 passed,1432 assertions。 - Laravel Pint 检查通过。
- sysadmin
vue-tsc --noEmit与生产构建通过,编译产物已更新到public/admin。
升级说明
- 本版本包含数据库迁移
2026_08_19_000000_add_cancel_audit_to_supply_sync_tasks_table;升级时必须执行迁移。 - 发布包含新版后台编译产物,服务器无需安装 Node.js 或重新构建前端。
- 升级后应执行
php artisan queue:restart(或重启 Supervisor/systemd worker),
使常驻 worker 加载 v1.14.2 代码;后台会继续校验 Web 与 worker 版本是否一致。