订单号、资金流水号、提现单号此前各自带前缀配置,同一笔业务在不同界面会
呈现多个号码口径,对账与客诉排查都要先做一次前缀换算。现收敛为单一流水号
分配器,不再按业务类型区分号码形态。
- 删除 payment_order_prefix / payment_flow_prefix /
payment_withdrawal_prefix 三项配置项及其生成、校验链路
- BusinessNumber::allocate() 成为唯一入口,输出 {YmdHis}{6 位随机},
共 20 位无前缀,满足 VARCHAR(32) 与网关 out_trade_no ≤ 32 的限制
- 订单、资金流水、提现、卡密渠道交易号四处生成点全部改走该分配器
- 后台支付中心表头「订单号 / 提现单号」统一为「流水号」
- 前台用户中心与订单卡片文案同步统一;前台订单详情合并为单行流水号,
账本流水号不再下发到前台,避免同一单据出现两个对外号码
- 同步更新 financial-flow / pay-card / card-pass-order-numbers 契约测试
与 BusinessNumberTest 断言
按用户要求不做双路逻辑与数据迁移,直接替换为统一方案,历史行不做转换。
验证:node --test tests/**/*.test.cjs 362/364 通过,新增 37 项契约;失败的
2 项在改动前的干净 HEAD 基线上同样失败(基线 327 项 / 失败 2 项),分别命中
遗留的 fa fa- 标记与 FA 版本号断言,与本次改动无关。phpunit 44 项 / 103
断言全部通过;php -l 全部改动的 PHP 文件通过。
107 lines
6.2 KiB
JavaScript
107 lines
6.2 KiB
JavaScript
const test = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
|
|
const root = path.resolve(__dirname, '../..')
|
|
const read = file => fs.readFileSync(path.join(root, file), 'utf8')
|
|
|
|
test('financial flow and withdrawal numbers use the new-only schema', () => {
|
|
const schema = read('src/Core/DBMigratorPart2.php')
|
|
assert.match(schema, /CREATE TABLE \{\$prefix\}aksrt_financial_flow \([\s\S]*?UNIQUE KEY uniq_flow_number \(flow_number\)/)
|
|
assert.match(schema, /UNIQUE KEY uniq_flow_operation \(operation_key\)/)
|
|
assert.match(schema, /CREATE TABLE \{\$prefix\}aksrt_financial_flow_entry \([\s\S]*?UNIQUE KEY uniq_flow_line \(flow_id, line_no\)/)
|
|
assert.match(schema, /CREATE TABLE \{\$prefix\}aksrt_withdrawal \([\s\S]*?withdrawal_number VARCHAR\(32\) NOT NULL[\s\S]*?UNIQUE KEY uniq_withdrawal_number \(withdrawal_number\)/)
|
|
})
|
|
|
|
test('order, flow and withdrawal numbers share one prefix-free allocator', () => {
|
|
const jsSchema = read('assets/admin/src/schema/sections/payment/gateway.js')
|
|
const phpSchema = read('src/Admin/Schema/SchemaPayment.php')
|
|
const provider = read('src/Admin/SchemaProvider.php')
|
|
const runtime = read('src/Settings/PaymentRuntime.php')
|
|
const number = read('src/Finance/BusinessNumber.php')
|
|
|
|
// 三个前缀配置项及其校验/归一化链路必须彻底消失。
|
|
for (const source of [jsSchema, phpSchema]) {
|
|
assert.doesNotMatch(source, /payment_order_prefix|payment_flow_prefix|payment_withdrawal_prefix/)
|
|
assert.doesNotMatch(source, /business_prefix/)
|
|
}
|
|
assert.doesNotMatch(provider, /business_prefix|normalize_prefix/)
|
|
assert.doesNotMatch(runtime, /payment_order_prefix|payment_flow_prefix|payment_withdrawal_prefix/)
|
|
assert.doesNotMatch(runtime, /['"](prefix|flow_prefix|withdrawal_prefix)['"]\s*=>/)
|
|
assert.doesNotMatch(number, /normalize_prefix|MAX_PREFIX_LENGTH|candidate\(/)
|
|
|
|
// 唯一入口:无前缀的统一流水号分配器。
|
|
assert.match(number, /public static function allocate\(\?\\?DateTimeInterface/)
|
|
assert.match(number, /YmdHis/)
|
|
|
|
// 订单号、资金流水号、提现单号、卡密渠道交易号全部收敛到该分配器。
|
|
assert.match(read('src/Payment/OrderRepositoryCore.php'), /BusinessNumber::allocate\(\)/)
|
|
assert.match(read('src/Finance/FinancialFlowService.php'), /BusinessNumber::allocate\(\)/)
|
|
assert.match(read('src/Payment/Withdrawal.php'), /BusinessNumber::allocate\(\)/)
|
|
assert.match(read('src/Payment/ChargeCard.php'), /BusinessNumber::allocate\(\)/)
|
|
})
|
|
|
|
test('ledger and business state write their flow before commit', () => {
|
|
const ledger = read('src/Ledger/LedgerService.php')
|
|
const order = read('src/Payment/OrderRepositoryCore.php')
|
|
const withdrawal = read('src/Payment/Withdrawal.php')
|
|
assert.ok(ledger.indexOf('record_ledger(') < ledger.indexOf("query('COMMIT')"))
|
|
assert.match(order, /record_order_payment\(\$paid_order, true\)/)
|
|
assert.match(order, /record_order_refund\(\$refunded_order, true\)/)
|
|
assert.match(withdrawal, /record_withdrawal_payout\([\s\S]*?true[\s\S]*?query\('COMMIT'\)/)
|
|
})
|
|
|
|
test('native and front-end surfaces expose immutable business numbers', () => {
|
|
assert.match(read('src/Admin/Native/PayMenus.php'), /PayFlows::PAGE_SLUG/)
|
|
assert.match(read('src/Admin/Native/PayFlows.php'), /流水明细/)
|
|
// 后台同时提供业务流水号与账本流水号两个口径
|
|
assert.match(read('src/Admin/Native/PayOrders.php'), /账本流水号/)
|
|
assert.match(read('src/Admin/Native/PayWithdrawals.php'), /esc_html__\('流水号'/)
|
|
assert.doesNotMatch(read('src/Admin/Native/PayWithdrawals.php'), /提现单号/)
|
|
// 前台订单详情只对外暴露统一流水号:账本流水号既不下发也不展示
|
|
assert.match(read('assets/js/order-center.js'), /\["流水号", order\.order_number\]/)
|
|
assert.doesNotMatch(read('assets/js/order-center.js'), /flow_number|账本流水号/)
|
|
assert.doesNotMatch(read('src/Payment/UserOrders.php'), /\$view\['flow_number'\]/)
|
|
assert.match(read('inc/helpers/user-center-renderers/credits-wallet-ledger.php'), /withdrawal_number/)
|
|
})
|
|
|
|
test('balance history renders withdrawal numbers from the committed flow', () => {
|
|
const query = read('src/Ledger/LedgerQuery.php')
|
|
const profile = read('inc/helpers/user-center-renderers/credits-wallet-ledger.php')
|
|
const wallet = read('inc/helpers/user-center-renderers/credits-wallet.php') + read('inc/helpers/user-center-renderers/credits-wallet-ledger.php')
|
|
assert.match(query, /LEFT JOIN \{\$flow_table\} f ON f\.ledger_transaction_id = t\.id/)
|
|
assert.match(query, /'withdrawal_number'\s*=>\s*\(string\)/)
|
|
for (const source of [profile, wallet]) {
|
|
assert.match(source, /\['withdraw_hold', 'withdraw_refund'\]/)
|
|
assert.match(source, /\$row\['withdrawal_number'\]/)
|
|
}
|
|
})
|
|
|
|
test('user withdrawal history uses one paginated ten-item endpoint', () => {
|
|
const controller = read('src/Payment/CommissionLedger.php')
|
|
const renderer = read('inc/helpers/user-center-renderers/credits-wallet.php')
|
|
const script = read('assets/js/credits.js')
|
|
const styles = read('assets/css/routes/user-center/credits.css')
|
|
|
|
assert.match(controller, /\$withdrawal_args\['per_page'\]\['default'\]\s*=\s*10/)
|
|
assert.match(controller, /\$withdrawal_args\['per_page'\]\['maximum'\]\s*=\s*10/)
|
|
assert.match(controller, /'total'\s*=>\s*\$total/)
|
|
assert.match(controller, /'max_pages'\s*=>\s*\$max_pages/)
|
|
assert.match(controller, /'user_id'\s*=>\s*\$user_id/)
|
|
assert.doesNotMatch(renderer, /get_user_withdrawals\(/)
|
|
assert.doesNotMatch(renderer, /withdraw_history/)
|
|
assert.match(renderer, /data-aksrt-withdraw-history-list/)
|
|
assert.match(script, /\/earnings\/withdrawals\?page=/)
|
|
assert.match(script, /&per_page=10/)
|
|
assert.match(script, /data-aksrt-withdraw-history-page/)
|
|
assert.match(script, /实际到账:/)
|
|
assert.match(script, /元)/)
|
|
assert.match(script, /appendText\(row, text\(item && item\.created_at\)/)
|
|
assert.doesNotMatch(script, /item\.note/)
|
|
assert.doesNotMatch(styles, /uc-withdraw-history__note/)
|
|
assert.match(styles, /\.uc-withdraw-history-modal__dialog\s*\{[\s\S]*?max-width: min\(860px/)
|
|
assert.match(styles, /\.uc-withdraw-history__item\s*\{[\s\S]*?display: flex/)
|
|
assert.match(styles, /\.uc-withdraw-history-modal__dialog\s*\{[\s\S]*?overflow: hidden/)
|
|
})
|