feat(payment): rebuild order details and asset records

This commit is contained in:
kate522 committed 2026-10-05 16:42:25 +08:00
1 parent 476e2f961d
commit 7b55cfb190
57 files changed
+2080 -1294

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ class DBMigrator {
use DBMigratorVip;
/** Current schema version; changing it re-runs dbDelta. */
const SCHEMA_VERSION = '2.1.0';
const SCHEMA_VERSION = '2.5.0';
const OPT_KEY = 'aksrt_db_schema_version';
+25
View File
@@ -204,6 +204,29 @@ trait DBMigratorPart2 {
) ENGINE=InnoDB {$charset_collate};";
dbDelta($sql_order);
// ---------- order after-sales ----------
$sql_order_aftersale = "CREATE TABLE {$prefix}aksrt_order_aftersale (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_number VARCHAR(32) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
aftersale_type VARCHAR(24) NOT NULL DEFAULT 'refund',
reason TEXT NOT NULL,
refund_amount VARCHAR(32) NOT NULL DEFAULT '0',
status VARCHAR(20) NOT NULL DEFAULT 'pending',
admin_note TEXT DEFAULT NULL,
gateway_request_id VARCHAR(128) DEFAULT NULL,
gateway_refund_id VARCHAR(128) DEFAULT NULL,
gateway_result LONGTEXT DEFAULT NULL,
created_at DATETIME NOT NULL,
reviewed_at DATETIME DEFAULT NULL,
completed_at DATETIME DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_order_status (order_number, status),
KEY idx_user_created (user_id, created_at),
KEY idx_status_created (status, created_at)
) ENGINE=InnoDB {$charset_collate};";
dbDelta($sql_order_aftersale);
// ---------- 9. 作者收益台账 ----------
$sql_revenue = "CREATE TABLE {$prefix}aksrt_revenue (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -246,12 +269,14 @@ trait DBMigratorPart2 {
method VARCHAR(32) NOT NULL DEFAULT '',
account_info TEXT DEFAULT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
operation_key VARCHAR(191) DEFAULT NULL,
ledger_transaction_id BIGINT UNSIGNED DEFAULT NULL,
created_at DATETIME NOT NULL,
processed_at DATETIME DEFAULT NULL,
note TEXT DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uniq_withdrawal_number (withdrawal_number),
UNIQUE KEY uniq_withdrawal_operation (operation_key),
UNIQUE KEY uniq_ledger_transaction (ledger_transaction_id),
KEY idx_user (user_id),
KEY idx_user_created (user_id, created_at),
+1
View File
@@ -25,6 +25,7 @@ trait DBMigratorPart3 {
'dl_token' => 'aksrt_dl_token',
'notification' => 'aksrt_notification',
'order' => 'aksrt_order',
'order_aftersale' => 'aksrt_order_aftersale',
'order_item' => 'aksrt_order_item',
'revenue' => 'aksrt_revenue',
'withdrawal' => 'aksrt_withdrawal',
+49 -9
View File
@@ -40,6 +40,10 @@ class DomainModel {
];
public const ORDER_PRODUCT_RESOURCE = 'resource';
public const ORDER_PRODUCT_READING = 'reading';
public const ORDER_PRODUCT_DOWNLOAD = 'download';
public const ORDER_PRODUCT_IMAGE = 'image';
public const ORDER_PRODUCT_VIDEO = 'video';
public const ORDER_PRODUCT_VIP = 'vip';
public const ORDER_PRODUCT_RECHARGE = 'recharge';
public const ORDER_PRODUCT_POINTS = 'points';
@@ -51,13 +55,53 @@ class DomainModel {
*/
public static function order_product_type_labels(): array {
return [
self::ORDER_PRODUCT_RESOURCE => __('付费内容', 'aksrt'),
self::ORDER_PRODUCT_VIP => __('VIP 会员', 'aksrt'),
self::ORDER_PRODUCT_READING => __('付费阅读', 'aksrt'),
self::ORDER_PRODUCT_DOWNLOAD => __('付费下载', 'aksrt'),
self::ORDER_PRODUCT_IMAGE => __('付费图片', 'aksrt'),
self::ORDER_PRODUCT_VIDEO => __('付费视频', 'aksrt'),
self::ORDER_PRODUCT_VIP => __('购买会员', 'aksrt'),
self::ORDER_PRODUCT_RECHARGE => __('余额充值', 'aksrt'),
self::ORDER_PRODUCT_POINTS => __('积分购买', 'aksrt'),
self::ORDER_PRODUCT_POINTS => __('购买积分', 'aksrt'),
];
}
public static function is_resource_product_type(string $type): bool {
return in_array(sanitize_key($type), [
self::ORDER_PRODUCT_RESOURCE,
self::ORDER_PRODUCT_READING,
self::ORDER_PRODUCT_DOWNLOAD,
self::ORDER_PRODUCT_IMAGE,
self::ORDER_PRODUCT_VIDEO,
], true);
}
/** Resolve new and historical paid-content order types from stored order meta. */
public static function resolve_order_product_type(string $stored_type, array $meta = []): string {
$stored_type = sanitize_key($stored_type);
if ($stored_type !== self::ORDER_PRODUCT_RESOURCE) {
return isset(self::order_product_type_labels()[$stored_type]) ? $stored_type : $stored_type;
}
$pay_type = sanitize_key((string) ($meta['pay_type'] ?? $meta['content_pay_type'] ?? ''));
return match ($pay_type) {
self::PAY_TYPE_READING => self::ORDER_PRODUCT_READING,
self::PAY_TYPE_DOWNLOAD => self::ORDER_PRODUCT_DOWNLOAD,
self::PAY_TYPE_GALLERY => self::ORDER_PRODUCT_IMAGE,
self::PAY_TYPE_VIDEO => self::ORDER_PRODUCT_VIDEO,
default => self::ORDER_PRODUCT_READING,
};
}
/** @return string[] */
public static function resource_order_product_types(): array {
return [
self::ORDER_PRODUCT_RESOURCE,
self::ORDER_PRODUCT_READING,
self::ORDER_PRODUCT_DOWNLOAD,
self::ORDER_PRODUCT_IMAGE,
self::ORDER_PRODUCT_VIDEO,
];
}
public const PAY_TYPE_NO = 'no';
public const PAY_TYPE_READING = 'reading';
public const PAY_TYPE_DOWNLOAD = 'download';
@@ -67,7 +111,6 @@ class DomainModel {
public const ORDER_STATUS_PENDING = 'pending';
public const ORDER_STATUS_PAID = 'paid';
public const ORDER_STATUS_REFUNDING = 'refunding';
public const ORDER_STATUS_REFUNDED = 'refunded';
public const ORDER_STATUS_CLOSED = 'closed';
@@ -454,7 +497,6 @@ class DomainModel {
return [
self::ORDER_STATUS_PENDING => __('待支付', 'aksrt'),
self::ORDER_STATUS_PAID => __('已支付', 'aksrt'),
self::ORDER_STATUS_REFUNDING => __('退款中', 'aksrt'),
self::ORDER_STATUS_REFUNDED => __('已退款', 'aksrt'),
self::ORDER_STATUS_CLOSED => __('已关闭', 'aksrt'),
];
@@ -470,11 +512,7 @@ class DomainModel {
self::ORDER_STATUS_CLOSED,
],
self::ORDER_STATUS_PAID => [
self::ORDER_STATUS_REFUNDING,
],
self::ORDER_STATUS_REFUNDING => [
self::ORDER_STATUS_REFUNDED,
self::ORDER_STATUS_PAID,
],
self::ORDER_STATUS_REFUNDED => [],
self::ORDER_STATUS_CLOSED => [],
@@ -500,6 +538,8 @@ class DomainModel {
public static function paid_order_statuses(): array {
return [
self::ORDER_STATUS_PAID,
// Read-only compatibility: normalize_row exposes legacy in-flight refunds as paid.
'refunding',
];
}