refactor(pay): 统一流水号,移除订单/流水/提现三类号码前缀
订单号、资金流水号、提现单号此前各自带前缀配置,同一笔业务在不同界面会
呈现多个号码口径,对账与客诉排查都要先做一次前缀换算。现收敛为单一流水号
分配器,不再按业务类型区分号码形态。
- 删除 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 文件通过。
This commit is contained in:
1 parent
8b999444b2
commit
aad83072a8
30 files changed
+126
-144
No files matched your search
@@ -726,8 +726,8 @@ class ChargeCard {
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡密核销订单号:走统一的订单号分配器,受后台「订单前缀」控制
|
||||
* (Settings::get_order_settings()['prefix'],默认 AKSRT)。
|
||||
* 卡密核销订单号:走统一的流水号分配器,不再使用任何可配置前缀
|
||||
* (见 {@see \AKSRT\Finance\BusinessNumber::allocate()})。
|
||||
*
|
||||
* @return string|\WP_Error
|
||||
*/
|
||||
@@ -743,16 +743,11 @@ class ChargeCard {
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡密核销订单的渠道交易号:与资金流水同源,受后台「流水号前缀」控制
|
||||
* (Settings::get_order_settings()['flow_prefix'],默认 AK)。
|
||||
* 卡密核销订单的渠道交易号:与资金流水号同源,统一走流水号分配器,
|
||||
* 不再消费任何后台前缀配置。
|
||||
*/
|
||||
private function card_pass_transaction_number(): string {
|
||||
$prefix = 'AK';
|
||||
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_order_settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_order_settings();
|
||||
$prefix = \AKSRT\Finance\BusinessNumber::normalize_prefix($settings['flow_prefix'] ?? 'AK', 'AK');
|
||||
}
|
||||
return \AKSRT\Finance\BusinessNumber::candidate($prefix);
|
||||
return \AKSRT\Finance\BusinessNumber::allocate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ trait OrderRepositoryCore {
|
||||
public function create(array $data): string {
|
||||
global $wpdb;
|
||||
|
||||
$order_number = $data['order_number'] ?? $this->generate_order_number();
|
||||
$order_number = $data['order_number'] ?? $this->allocate_order_number();
|
||||
|
||||
// Preserve JSON pay meta (VIP/content/coupon). Only strip tags for plain text notes.
|
||||
$note_raw = (string) ($data['note'] ?? '');
|
||||
@@ -811,39 +811,13 @@ trait OrderRepositoryCore {
|
||||
|
||||
|
||||
/**
|
||||
* Build a unique order number using the admin-configured prefix.
|
||||
* 分配订单号。
|
||||
*
|
||||
* Column + major gateways (WeChat out_trade_no) cap at 32 chars, so the
|
||||
* prefix is trimmed to leave room for {YmdHis}{4 digits} (=18).
|
||||
* 订单号不再是独立编号体系:与资金流水号、提现单号共用统一流水号规则,
|
||||
* 由 {@see \AKSRT\Finance\BusinessNumber::allocate()} 生成,不带任何前缀。
|
||||
*/
|
||||
public function allocate_order_number(): string {
|
||||
return $this->generate_order_number();
|
||||
}
|
||||
|
||||
private function generate_order_number(): string {
|
||||
$prefix = 'AKSRT';
|
||||
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_order_settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_order_settings();
|
||||
$configured = sanitize_text_field((string) ($settings['prefix'] ?? ''));
|
||||
if ($configured !== '') {
|
||||
$prefix = $configured;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep only gateway-safe chars (REST + WeChat/Alipay out_trade_no).
|
||||
$prefix = preg_replace('/[^A-Za-z0-9_-]/', '', $prefix);
|
||||
if (!is_string($prefix) || $prefix === '') {
|
||||
$prefix = 'AKSRT';
|
||||
}
|
||||
|
||||
$body = date('YmdHis') . str_pad((string) random_int(0, 9999), 4, '0', STR_PAD_LEFT);
|
||||
// Reserve 18 for body; leave ≥1 char of prefix so numbers stay human-readable.
|
||||
$max_prefix = max(1, 32 - strlen($body));
|
||||
if (strlen($prefix) > $max_prefix) {
|
||||
$prefix = substr($prefix, 0, $max_prefix);
|
||||
}
|
||||
|
||||
return $prefix . $body;
|
||||
return \AKSRT\Finance\BusinessNumber::allocate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@ class PaymentOrchestrator {
|
||||
public static function pay(GatewayInterface $gateway, array $order, array $opts = []): array {
|
||||
$order_number = sanitize_text_field((string) ($order['order_number'] ?? ''));
|
||||
if ($order_number === '' || !class_exists('AKSRT\\Payment\\OrderRepository')) {
|
||||
return self::fail('aksrt_pay_failed', __('订单号无效。', 'aksrt'));
|
||||
return self::fail('aksrt_pay_failed', __('流水号无效。', 'aksrt'));
|
||||
}
|
||||
|
||||
$method = sanitize_key((string) ($order['payment_method'] ?? ''));
|
||||
|
||||
@@ -232,9 +232,8 @@ class UserOrders {
|
||||
])),
|
||||
];
|
||||
if ($detail) {
|
||||
$view['flow_number'] = $order_number !== '' && $group !== OrderCenterQuery::STATUS_PENDING
|
||||
? \AKSRT\Finance\FinancialFlowQuery::get_instance()->order_payment_number($order_number)
|
||||
: '';
|
||||
// 前台订单详情只暴露统一流水号(order_number);账本流水号为后台口径,
|
||||
// 不下发到前台,避免同一单据出现两个对外号码。
|
||||
$view['paid_at'] = (string) ($order['paid_at'] ?? '');
|
||||
$view['expires_at'] = (string) ($order['expires_at'] ?? '');
|
||||
$view['reconciliation'] = $reconciliation ?: ['state' => 'not_needed', 'required' => false];
|
||||
|
||||
@@ -147,11 +147,9 @@ class Withdrawal {
|
||||
'source' => 'balance',
|
||||
'operation_key' => $operation_key,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: $account;
|
||||
$order_settings = \AKSRT\Settings\Settings::get_order_settings();
|
||||
$prefix = \AKSRT\Finance\BusinessNumber::normalize_prefix($order_settings['withdrawal_prefix'] ?? 'TX', 'TX');
|
||||
$ok = false;
|
||||
for ($attempt = 1; $attempt <= 5; $attempt++) {
|
||||
$withdrawal_number = \AKSRT\Finance\BusinessNumber::candidate($prefix);
|
||||
$withdrawal_number = \AKSRT\Finance\BusinessNumber::allocate();
|
||||
$ok = $wpdb->insert($this->table(), [
|
||||
'withdrawal_number' => $withdrawal_number,
|
||||
'user_id' => $user_id,
|
||||
|
||||
Reference in new issue
Block a user