feat: C 端套餐沉默期展示与自助延期
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 23m46s

- 套餐历史展示沉默期状态、截止日期、累计延期与调整审批中,可延期时显示延期入口
- 新增延期页:资格、档位与自定义天数试算、支付方式、下单(request_id 幂等)
- 支付宝走现有支付页;微信为链接时在微信外直接跳转,否则展示二维码与复制链接
- 新增我的延期订单列表与详情、沉默期记录,沉默期错误码转客户提示
This commit is contained in:
邹进
2026-11-07 13:19:33 +09:00
parent b2f802e6f8
commit 31ff18d66a
15 changed files with 1500 additions and 22 deletions

65
tests/silence.test.mjs Normal file
View File

@@ -0,0 +1,65 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
createRequestIdKeeper,
getSilenceErrorMessage,
getSilencePaymentOptions,
getSilenceReasonText,
getSilenceStatusClass,
isPaymentLink,
isSilenceFeatureDisabled,
isSilenceOrderFinal,
validateExtensionDays
} from '../utils/silence.js';
test('延期支付方式只取 wechat、alipay 与资产允许方式的交集,支付宝优先', () => {
assert.deepEqual(getSilencePaymentOptions(['wechat', 'wallet', 'alipay']).map((item) => item.value), ['alipay', 'wechat']);
assert.deepEqual(getSilencePaymentOptions(['wallet']), []);
assert.deepEqual(getSilencePaymentOptions(undefined), []);
});
test('延期天数校验:整数且在 min~max 内', () => {
assert.equal(validateExtensionDays('30', 30, 180), '');
assert.equal(validateExtensionDays(180, 30, 180), '');
assert.equal(validateExtensionDays('29', 30, 180), '延期天数需在 30~180 天之间');
assert.equal(validateExtensionDays('181', 30, 180), '延期天数需在 30~180 天之间');
assert.equal(validateExtensionDays('12.5', 30, 180), '请输入整数天数');
assert.equal(validateExtensionDays('', 30, 180), '请输入整数天数');
});
test('request_id:同一天数与支付方式复用,任一变化或重置后生成新的', () => {
let seq = 0;
const keeper = createRequestIdKeeper(() => `id-${++seq}`);
assert.equal(keeper.get(30, 'alipay'), 'id-1');
assert.equal(keeper.get(30, 'alipay'), 'id-1');
assert.equal(keeper.get(60, 'alipay'), 'id-2');
assert.equal(keeper.get(60, 'wechat'), 'id-3');
keeper.reset();
assert.equal(keeper.get(60, 'wechat'), 'id-4');
});
test('沉默期错误码转客户提示', () => {
assert.equal(getSilenceErrorMessage({ code: 1266, data: { min_days: 30, max_days: 180 } }), '延期天数需在 30~180 天之间');
assert.equal(getSilenceErrorMessage({ code: 1265, data: { min_months: 1, max_months: 6 } }), '超过可延期期限,可延期 1~6 个月');
assert.equal(getSilenceErrorMessage({ code: 1263, data: { reason: 'not_expired' } }), '套餐沉默期尚未过期,暂不需要延期');
assert.equal(getSilenceErrorMessage({ code: 1267 }), '免费延期次数已用完');
assert.equal(getSilenceErrorMessage({ code: 1271 }), '已有未完成的延期订单,请先完成支付或等待其关闭');
assert.equal(getSilenceErrorMessage({ code: 1274 }), '延期功能暂未开放');
assert.equal(getSilenceErrorMessage({ code: 1001, msg: '参数错误' }), '参数错误');
assert.equal(getSilenceErrorMessage({ paymentResultUnknown: true }), '网络异常,请稍后在"我的延期订单"中查看结果');
assert.equal(getSilenceErrorMessage({}, '兜底'), '兜底');
assert.equal(isSilenceFeatureDisabled({ code: 1274 }), true);
assert.equal(isSilenceFeatureDisabled({ code: 1266 }), false);
});
test('资格原因、状态样式、订单终态与支付链接判断', () => {
assert.equal(getSilenceReasonText('extension_disabled'), '该套餐不支持付费延期,请联系客服');
assert.equal(getSilenceReasonText('unknown'), '当前暂不能办理延期,请联系客服');
assert.equal(getSilenceStatusClass('expired'), 'silence-expired');
assert.equal(getSilenceStatusClass('other'), '');
assert.equal(isSilenceOrderFinal('completed'), true);
assert.equal(isSilenceOrderFinal('pending_payment'), false);
assert.equal(isPaymentLink('https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?x=1'), true);
assert.equal(isPaymentLink('weixin://wxpay/bizpayurl?pr=abc'), false);
assert.equal(isPaymentLink(''), false);
});