新增全局流水号

This commit is contained in:
2026-08-25 05:33:01 +08:00
parent 90fd59b647
commit 07c4e969c3
33 changed files with 1176 additions and 73 deletions
+19
View File
@@ -0,0 +1,19 @@
<?php
use AKSRT\Finance\BusinessNumber;
use PHPUnit\Framework\TestCase;
final class BusinessNumberTest extends TestCase {
public function test_prefix_is_uppercased_filtered_and_limited(): void {
self::assertSame('AK', BusinessNumber::normalize_prefix('', 'AK'));
self::assertSame('TX_TEST-9', BusinessNumber::normalize_prefix(' tx_测test-9 ', 'TX'));
self::assertSame('ABCDEFGHIJKL', BusinessNumber::normalize_prefix('abcdefghijklmnop', 'AK'));
self::assertSame('TX', BusinessNumber::normalize_prefix('中文', 'TX'));
}
public function test_candidate_uses_supplied_site_time_and_six_digit_random_suffix(): void {
$time = new DateTimeImmutable('2026-08-25 13:14:15', new DateTimeZone('Asia/Shanghai'));
self::assertSame('AK20260825131415000007', BusinessNumber::candidate('ak', $time, 7));
self::assertMatchesRegularExpression('/^TX\d{14}\d{6}$/', BusinessNumber::candidate('TX', $time, 999999));
}
}
+3 -1
View File
@@ -8,11 +8,13 @@ final class LedgerResultTest extends TestCase {
public function test_balance_reads_integer_minor_units(): void {
$result = new LedgerResult(17, true, [], [
4 => ['points' => 25, 'balance' => 1234],
]);
], 'AK20260825123456000001');
self::assertSame(25, $result->balance(4, AssetType::POINTS));
self::assertSame(1234, $result->balance(4, AssetType::BALANCE));
self::assertSame(0, $result->balance(99, AssetType::BALANCE));
self::assertSame(17, $result->to_array()['transaction_id']);
self::assertTrue($result->to_array()['replayed']);
self::assertSame('AK20260825123456000001', $result->flow_number);
self::assertSame('AK20260825123456000001', $result->to_array()['flow_number']);
}
}
+11
View File
@@ -12,6 +12,16 @@ if (!function_exists('sanitize_key')) {
return strtolower((string) preg_replace('/[^a-z0-9_\-]/', '', $key));
}
}
if (!function_exists('sanitize_text_field')) {
function sanitize_text_field($value): string {
return trim(strip_tags((string) $value));
}
}
if (!function_exists('wp_timezone')) {
function wp_timezone(): DateTimeZone {
return new DateTimeZone('UTC');
}
}
if (!function_exists('wp_json_encode')) {
function wp_json_encode($value, int $flags = 0) {
return json_encode($value, $flags);
@@ -59,3 +69,4 @@ require_once dirname(__DIR__, 2) . '/src/Ledger/AssetAmount.php';
require_once dirname(__DIR__, 2) . '/src/Ledger/LedgerCommand.php';
require_once dirname(__DIR__, 2) . '/src/Ledger/LedgerResult.php';
require_once dirname(__DIR__, 2) . '/src/Ledger/OperationKey.php';
require_once dirname(__DIR__, 2) . '/src/Finance/BusinessNumber.php';
@@ -0,0 +1,43 @@
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 settings expose strict flow and withdrawal prefixes', () => {
const schema = read('assets/admin/src/schema/sections/payment/order.js')
const provider = read('src/Admin/SchemaProvider.php')
assert.match(schema, /key:\s*'payment_flow_prefix'[^{\n]*sanitize_type:\s*'business_prefix'[^{\n]*default:\s*'AK'/)
assert.match(schema, /key:\s*'payment_withdrawal_prefix'[^{\n]*sanitize_type:\s*'business_prefix'[^{\n]*default:\s*'TX'/)
assert.match(provider, /business_prefix[\s\S]*?BusinessNumber::normalize_prefix/)
})
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'), /提现单号/)
assert.match(read('src/Payment/UserOrders.php'), /\$view\['flow_number'\]/)
assert.match(read('assets/js/order-center.js'), /\["流水号", order\.flow_number \|\| "—"\]/)
assert.match(read('inc/helpers/user-center-renderers/credits-wallet.php'), /withdrawal_number/)
})