fix(payment): 卡密兑换开关收敛为唯一总开关

- 问题:积分卡密兑换报「积分卡密兑换未开启」,因 asset_points_card_enabled
  与总开关并存为双重闸门;会员卡同样存在隐藏的第二开关 pay_vip_pass_charge_s。
- 修复:ChargeCard 类型闸门统一只消费后台「余额充值 → 卡密兑换」总开关;
  积分卡仅额外要求积分系统可用(系统级依赖,非独立开关)。
- 死代码清理:PaymentRuntime 删除 benefit.pass_charge/pass_charge_desc 映射;
  后台 schema 删除「卡密兑换会员」独立开关组与「启用积分卡密兑换」开关(双源同步);
  vip options 收敛为仅 only_password 展示配置。
- 验证:php -l 4 文件无错;契约测试 59/59 通过;容器内探针实测
  credits 类型闸门放行;SPA 已重建并同步运行副本。
This commit is contained in:
2026-09-17 19:18:54 +08:00
parent 3481620ed0
commit 64ed47165d
7 changed files with 50 additions and 69 deletions
@@ -32,6 +32,7 @@ const schemaPhp = read('src', 'Admin', 'Schema', 'SchemaPayment.php');
const schemaJs = read('assets', 'admin', 'src', 'schema', 'sections', 'payment', 'recharge.js');
const usersSchemaPhp = read('src', 'Admin', 'Schema', 'SchemaUsers.php');
const usersSchemaJs = read('assets', 'admin', 'src', 'schema', 'sections', 'users', 'credits.js');
const vipSchemaJs = read('assets', 'admin', 'src', 'schema', 'sections', 'payment', 'vip.js');
const creditsRenderer = read('inc', 'helpers', 'user-center-renderers', 'credits-balance.php');
const creditsCss = read('assets', 'css', 'routes', 'user-center', 'credits.css');
@@ -63,10 +64,11 @@ test('前台唯一兑换入口:页内联块与死文件已删除,JS 不再
);
assert.doesNotMatch(creditsRenderer, /data-aksrt-card-pass|data-aksrt-card-redeem|card_pass_credits/);
assert.doesNotMatch(creditsJs, /hasAttribute\('required'\)/);
// 积分卡的说明/单密码配置只服务于已删内联块,后台 schema 不应再有。
assert.doesNotMatch(usersSchemaPhp, /asset_points_card_description|asset_points_card_password_only/);
assert.doesNotMatch(usersSchemaJs, /asset_points_card_description|asset_points_card_password_only/);
assert.doesNotMatch(chargeCard, /asset_points_card_description|asset_points_card_password_only/);
// 会员/积分卡密无独立开关与说明/单密码配置残留。
assert.doesNotMatch(usersSchemaPhp, /asset_points_card_/);
assert.doesNotMatch(usersSchemaJs, /asset_points_card_/);
assert.doesNotMatch(chargeCard, /asset_points_card_/);
assert.doesNotMatch(chargeCard, /'pass_charge_desc'/);
// 失去宿主的内联块样式不回潮(弹窗仍在用 __input/__btn/__tip 子元素样式)。
assert.doesNotMatch(creditsCss, /^\.uc-card-pass\s*\{/m);
assert.doesNotMatch(creditsCss, /uc-card-pass__title|uc-card-pass__desc|\.uc-assets-col > \.uc-card-pass/);
@@ -92,6 +94,21 @@ test('后端:核销订单金额恒为 0,options 路由与 labels 死代码
assert.match(chargeCard, /'credits' => \['enabled' => /);
});
test('唯一类型闸门:总开关统一约束三类卡密,独立开关不回潮', () => {
// assert_type_enabled 只查总开关(+ 积分系统级依赖),不再有按类型的独立开关。
assert.match(chargeCard, /卡密兑换功能未开启/);
assert.match(chargeCard, /积分系统未开启/);
assert.doesNotMatch(chargeCard, /余额卡密兑换未开启|会员卡密兑换未开启|积分卡密兑换未开启/);
// 会员/积分卡密独立开关与说明为死代码。
assert.doesNotMatch(chargeCard, /pay_vip_pass_charge_s|pay_vip_pass_charge_desc|pass_charge'\s*=>/);
for (const [name, src] of [['SchemaPayment.php', schemaPhp], ['recharge.js', schemaJs], ['vip.js', vipSchemaJs]]) {
assert.doesNotMatch(src, /pay_vip_pass_charge_s|pay_vip_pass_charge_desc|启用卡密兑换会员功能/, name);
assert.doesNotMatch(src, /启用积分卡密兑换/, name);
}
assert.match(vipSchemaJs, /title: '会员卡密'/);
assert.match(vipSchemaJs, /pay_vip_pass_charge_only_password/);
});
test('订单中心:卡密核销订单实付归零并按 note 生成订单名称', () => {
assert.match(userOrders, /if \(\$payment_method === 'card_pass'\)\s*\{\s*\$amount = \$is_points \? '0' : '0\.00';/);
assert.match(userOrders, /if \(!empty\(\$meta\['card_pass'\]\)\)\s*\{[\s\S]*?resolve_card_pass_title\(\$meta\)/);