This commit is contained in:
2026-09-19 17:02:11 +08:00
parent 4eb17ece4f
commit 514abf6eda
33 changed files with 525 additions and 223 deletions
@@ -8,13 +8,13 @@ const routesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf
const permissionsSource = readFileSync(new URL('../../tenant-management/contracts.ts', import.meta.url), 'utf8')
test('uses only authenticated client finance account endpoints without customer scope', () => {
for (const path of ['/client/finance/v1/account', '/client/finance/v1/account/ledger', '/client/finance/v1/recharge-requests', '/client/finance/v1/recharge-channels']) {
for (const path of ['/client/finance/v1/account', '/client/finance/v1/account/ledger', '/client/finance/v1/recharge-requests', '/client/finance/v1/recharge-channel']) {
assert.match(apiSource, new RegExp(path.replaceAll('/', '\\/')))
}
assert.match(apiSource, /headers: \{ 'Idempotency-Key': idempotencyKey \}/)
assert.match(apiSource, /formData\.append\('amount', amount\)/)
assert.match(apiSource, /formData\.append\('paymentMethod', paymentMethod\)/)
assert.match(apiSource, /formData\.append\('file', file, file\.name\)/)
assert.doesNotMatch(apiSource, /RechargePaymentMethod|paymentMethod|WECHAT|ALIPAY/)
assert.doesNotMatch(apiSource, /customerId|tenantId|mock|fallback/i)
})
@@ -23,12 +23,14 @@ test('activates the account route and maps read and recharge permissions indepen
assert.match(permissionsSource, /'merchant:finance:account:view': \['merchant:finance:account:view'\]/)
assert.match(permissionsSource, /'merchant:finance:recharge:create': \['merchant:finance:recharge:create'\]/)
assert.match(viewSource, /auth\.hasPermission\('merchant:finance:recharge:create'\)/)
assert.match(viewSource, /<el-button v-if="canRecharge"/)
assert.match(viewSource, /class="balance-title"[\s\S]*?<el-button v-if="canRecharge" size="small" type="primary"/)
assert.match(viewSource, /\.balance-summary \{[^}]*grid-template-columns: repeat\(6,/)
assert.doesNotMatch(viewSource, /class="account-actions"|class="balance-grid"/)
assert.doesNotMatch(viewSource, /class="account-heading"/)
})
test('renders server balances, ledger and recharge states without internal identifiers', () => {
for (const field of ['actualBalance', 'frozenBalance', 'availableBalance', 'creditLimit', 'creditUsed', 'availableCredit']) assert.match(viewSource, new RegExp(`account\\?\\.${field}`))
for (const field of ['creditUsedAfter', 'availableCreditAfter']) assert.match(viewSource, new RegExp(`row\\.${field}`))
assert.match(viewSource, /实际可用资金与可用授信合计/)
assert.match(viewSource, /CREDIT_LIMIT: '授信调整'/)
assert.match(viewSource, /row-key="businessNo"/)
@@ -39,9 +41,12 @@ test('renders server balances, ledger and recharge states without internal ident
assert.match(viewSource, /v-loading="pageLoading"/)
assert.match(viewSource, /function formatAccountAmount[\s\S]*value == null \? '—' : formatAmount\(value\)/)
assert.match(viewSource, /formatAccountAmount\(account\?\.actualBalance\)/)
assert.match(viewSource, /:default-current-page="ledgerPage"/)
assert.match(viewSource, /:default-current-page="rechargePage"/)
assert.doesNotMatch(viewSource, /<el-pagination (?:v-model:current-page|:current-page)=/)
assert.match(viewSource, /<DataTable v-model:page="ledgerPage" v-model:page-size="ledgerPageSize"/)
assert.match(viewSource, /<DataTable v-model:page="rechargePage" v-model:page-size="rechargePageSize"/)
assert.match(viewSource, /const ledgerColumns: DataTableColumn\[]/)
assert.match(viewSource, /const rechargeColumns: DataTableColumn\[]/)
assert.doesNotMatch(viewSource, /label: '已用授信'|label: '可用授信'/)
assert.doesNotMatch(viewSource, /<el-table|<el-pagination/)
assert.match(apiSource, /function normalizePageResult<[\s\S]*total: Number\(result\.total\)/)
assert.match(apiSource, /page: Number\(result\.page\)/)
assert.doesNotMatch(viewSource, /requestId|ledgerId|customerId|tenantId/)
@@ -50,12 +55,14 @@ test('renders server balances, ledger and recharge states without internal ident
test('matches the frozen recharge workflow and preserves an idempotency key for failed retries', () => {
assert.match(viewSource, /title="账户充值" width="760px"/)
for (const amount of ['50.00', '100.00', '200.00', '500.00']) assert.match(viewSource, new RegExp(amount.replace('.', '\\.')))
assert.match(viewSource, /\['WECHAT', 'ALIPAY'\]/)
assert.match(viewSource, /selectedChannel\.value\?\.enabled && selectedChannel\.value\.qrCodeUrl/)
assert.match(viewSource, /rechargeChannel\.value\?\.enabled && rechargeChannel\.value\.qrCodeUrl/)
assert.match(viewSource, /alt="平台收款二维码"/)
assert.match(viewSource, /请扫描平台收款二维码,付款后上传截图。/)
assert.match(viewSource, /accept="\.jpg,\.jpeg,\.png,image\/jpeg,image\/png"/)
assert.match(viewSource, /if \(!pendingIdempotencyKey\)[\s\S]*globalThis\.crypto\?\.randomUUID\?\.\(\)/)
assert.match(viewSource, /pendingIdempotencyKey = ''[\s\S]*rechargeVisible\.value = false/)
assert.match(viewSource, /:disabled="!canSubmit"/)
assert.match(viewSource, /await loadAccountData\(\)/)
assert.doesNotMatch(viewSource, /payment SDK|支付成功|模拟成功/)
assert.doesNotMatch(viewSource, /微信支付|支付宝|payment-methods|paymentMethod/)
})