Skip to content

Releases: NovaWorks/ZCard

v1.16.0 — 后台新增「数据迁移」控制台

Choose a tag to compare

@SunGreatSage SunGreatSage released this 07 Sep 07:08

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 缺货商品禁止下单 — 上游商品付款前拒单 + 前台售罄态

Choose a tag to compare

@SunGreatSage SunGreatSage released this 31 Aug 18:04

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 test458 passed,1562 assertions;Laravel Pint 通过;
  • storefront 前端产物已重建(public/storefront)。

升级说明

  • 本版本不包含数据库迁移;
  • 已含前端变更,生产部署需同步 public/storefront(仓库已带编译产物);
  • 更新后请执行 php artisan queue:restart
  • 缺货拦截依据的是库存同步缓存,请确认货源的「定时同步库存」任务正常运行
    (缓存越新拒单越准;同步间隔内上游售罄仍由付款后拿货失败兜底)。

v1.14.9 兼容出海版 ACG-Faka 并修复充值/验证码/定价输入

Choose a tag to compare

@SunGreatSage SunGreatSage released this 25 Aug 16:09

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」,
    覆盖 JS atob 混淆包装页(正则不再依赖词边界,避免 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_keyAuthController(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 test453 passed,1553 assertions;Laravel Pint 通过;
  • sysadmin / storefront 前端产物已重建(public/adminpublic/storefront)。

升级说明

  • 本版本不包含数据库迁移;
  • 已含前端变更,生产部署需同步 public/adminpublic/storefront(仓库已带编译产物);
  • 更新后请执行 php artisan queue:restart,使常驻 worker 加载新的库存探测逻辑;
  • 出海版货源更新后请在后台点一次「同步商品」验证库存补查正常。

v1.14.8

Choose a tag to compare

@SunGreatSage SunGreatSage released this 21 Aug 05:29

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 test449 passed,1529 assertions
  • Laravel Pint 与 git diff --check 通过。

升级说明

  • 本版本不包含数据库迁移;
  • 本版本不包含前端变更,无需重建前端产物;
  • 更新后请执行 php artisan queue:restart,使常驻队列 worker 加载新的库存兼容逻辑。

v1.14.7

Choose a tag to compare

@SunGreatSage SunGreatSage released this 20 Aug 18:31

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 test447 passed,1507 assertions
  • 管理后台 vue-tsc --noEmit 与生产构建通过;
  • 商城生产构建通过;
  • Laravel Pint 与 git diff --check 通过。

升级说明

  • 本版本包含数据库迁移,请在更新后执行 php artisan migrate --force
  • 已重建并提交管理后台及商城前端产物;
  • 请重启常驻队列 worker,使新的上游商品 DTO、请求超时和下单参数逻辑立即生效。

v1.14.6 延长 ACG-Faka 商品调试请求超时

Choose a tag to compare

@SunGreatSage SunGreatSage released this 20 Aug 17:25

v1.14.6

🐛 修复:延长 ACG-Faka 商品调试请求超时时间

现象:管理员通过货源商品调试接口直接请求 ACG-Faka
/shared/commodity/items 时,大型商品目录或上游响应较慢时可能在 30 秒处超时。

修复

  • 将该表单 POST 请求的超时时间从 30 秒延长到 60 秒;
  • 保持其他上游驱动及支付、回调请求的超时策略不变;
  • 新增回归测试,直接断言传给 HTTP 客户端的 timeout 为 60。

测试

  • 货源管理专项测试:7 passed,24 assertions
  • 全量 php artisan test439 passed,1467 assertions
  • Laravel Pint 与 git diff --check 通过。

升级说明

  • 本版本不包含数据库迁移;
  • 不需要重建管理后台前端产物;
  • 不需要重启队列 worker。

v1.14.5 渠道编辑站点地址回填修复

Choose a tag to compare

@SunGreatSage SunGreatSage released this 20 Aug 07:37

v1.14.5

🐛 修复:渠道编辑时站点地址无法回填

现象:在渠道管理中编辑已有货源时,必填的“站点地址”显示为空,管理员每次保存前都需要
重新填写;列表页仍能正常显示该地址。

根因:货源接口同时返回顶层 base_url 和动态凭证中的 credentials.base_url。后端误将
动态凭证中的站点地址当作密钥脱敏,前端又把脱敏占位值识别为敏感字段并清空,导致编辑表单
未使用列表中已有的真实地址。

修复

  • 将公开的 base_url 明确列为非敏感配置字段,继续对 API 密钥等真实凭证进行脱敏;
  • 编辑表单统一以顶层 base_url 作为站点地址的回填真理源,兼容旧接口和历史数据;
  • 新增接口回归测试,确保站点地址可回填,同时 API 密钥仍保持脱敏;
  • 重新构建并提交管理后台生产产物。

测试

  • 货源管理专项测试:6 passed,22 assertions
  • 全量 php artisan test438 passed,1465 assertions
  • 管理后台 TypeScript 检查与生产构建通过;
  • Laravel Pint、构建产物引用检查与 git diff --check 通过。

升级说明

  • 本版本不包含数据库迁移;
  • 不需要重启队列 worker;
  • 更新后刷新管理后台页面,即可在编辑渠道时看到已保存的站点地址。

v1.14.4 重复心跳误取消同步任务修复(issue #43)

Choose a tag to compare

@SunGreatSage SunGreatSage released this 19 Aug 09:44

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 test437 passed,1461 assertions
  • Laravel Pint 与 git diff --check 通过。

升级说明

  • 本版本不包含数据库迁移;
  • 升级后必须执行 php artisan queue:restart(或重启 Supervisor/systemd worker),让常驻 worker
    加载 v1.14.4 的心跳状态机;
  • 已被误标为 cancelled 的历史任务不会自动恢复,可在升级并重启 worker 后重新发起同步。

v1.14.3

Choose a tag to compare

@SunGreatSage SunGreatSage released this 19 Aug 06:13

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 test435 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)

Choose a tag to compare

@SunGreatSage SunGreatSage released this 19 Aug 05:35

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 test430 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 版本是否一致。