feat(orders): order detail modal, list actions, use configured prefix

Wire admin payment_order_prefix into order number generation (with 32-char
cap for gateways), make 查看 open a full order-detail modal with
status-aware footer actions, keep type badge top-left only, and right-align
unpaid list actions with countdown on the left.
This commit is contained in:
2026-07-27 12:39:21 +08:00
parent 5e1e2e2634
commit a1e35a4499
5 changed files with 492 additions and 46 deletions
+40 -4
View File
@@ -41,7 +41,7 @@ trait OrderRepositoryCore {
$note = sanitize_textarea_field($note_raw);
}
$wpdb->insert($this->table(), [
$row = [
'order_number' => $order_number,
'user_id' => (int) ($data['user_id'] ?? 0),
'product_type' => sanitize_key((string) ($data['product_type'] ?? '')),
@@ -53,9 +53,17 @@ trait OrderRepositoryCore {
'payment_transaction_id' => sanitize_text_field((string) ($data['payment_transaction_id'] ?? '')),
'created_at' => $data['created_at'] ?? current_time('mysql'),
'paid_at' => $data['paid_at'] ?? null,
'expires_at' => $data['expires_at'] ?? null,
'note' => $note,
], ['%s', '%d', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']);
];
$formats = ['%s', '%d', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%s'];
// Honour configured expiry when callers pass expires_at (shop checkout etc.).
if (array_key_exists('expires_at', $data) && $data['expires_at'] !== null && $data['expires_at'] !== '') {
$row['expires_at'] = (string) $data['expires_at'];
$formats[] = '%s';
}
$wpdb->insert($this->table(), $row, $formats);
if ($wpdb->insert_id) {
return $order_number;
@@ -506,8 +514,36 @@ 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).
*/
private function generate_order_number(): string {
return 'AKS' . date('YmdHis') . str_pad((string) random_int(0, 9999), 4, '0', STR_PAD_LEFT);
$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;
}
/**