feat(orders): replace order stat cards with a 售后中 filter

/user/orders 顶部的「已退款」「售后待处理」统计卡移除(统计行改为
全部 / 待支付 / 已支付 / 已关闭 四格,栅格同步由 3 列调为 4 列),
筛选的订单状态新增「售后中」,命中带有「待审核 / 处理中」工单的订单。

售后中与订单自身状态正交(售后中的订单可能仍是 paid,也可能已
refunded),因此不过滤 status 列,而是由 OrderCenterQuery 通过
repository_args 的 after_sale_open 下传,在 get_user_order_query_parts
中作为独立谓词叠加工单子查询,与类型、订单号条件自然组合。

工单流转口径复用 AfterSaleService 的 STATUS_PENDING / STATUS_PROCESSING,
与 find_active() 的退款闸门同一套判定;已完结(completed)与已撤销
(cancelled)不算售后中。

同时清理随之失效的死代码:UserOrders::get_user_order_stats()、
AfterSaleService::count_user_open()、aksrt_get_order_status_labels(),
以及两处 summary 查询与空视图中的 refunded / after_sale_pending 统计键。
不写双路与数据迁移,旧键直接替换。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-10-25 18:58:06 +08:00
1 parent 81b60eb427
commit 761c84d70f
8 files changed
+29 -39

No files matched your search

+1 -1
View File
@@ -31,7 +31,7 @@
.aksrt-orders-page__stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(4, 1fr);
border: 1px solid var(--orders-line);
border-radius: 14px;
background: var(--surface-1, #fff);
+1 -3
View File
@@ -366,9 +366,7 @@
root.innerHTML = '<div class="aksrt-orders-page__stat"><span>全部</span><strong>' + Number(stats.total || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>待支付</span><strong class="is-pending">' + Number(stats.pending || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>已支付</span><strong class="is-paid">' + Number(stats.paid || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>已退款</span><strong>' + Number(stats.refunded || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>已关闭</span><strong>' + Number(stats.closed || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>售后待处理</span><strong>' + Number(stats.after_sale_pending || 0) + "</strong></div>";
'<div class="aksrt-orders-page__stat"><span>已关闭</span><strong>' + Number(stats.closed || 0) + "</strong></div>";
var pendingCount = state.root.querySelector('[data-aksrt-order-filter="status"][data-aksrt-order-filter-value="pending"] .aksrt-orders-page__filter-count');
if (pendingCount) {
pendingCount.textContent = String(Number(stats.pending || 0));
-8
View File
@@ -9,14 +9,6 @@
defined('ABSPATH') || exit;
function aksrt_get_order_status_labels(): array {
if (class_exists('\AKSRT\Core\DomainModel')) {
return \AKSRT\Core\DomainModel::order_status_labels();
}
return [];
}
function aksrt_get_orders_page_view(int $user_id, int $paged = 1, int $per_page = 20, string $status = 'all', string $product_type = '', string $order_number = ''): array {
$empty = [
'items' => [],
@@ -39,6 +39,7 @@ $status_options = [
'all' => __('全部', 'aksrt'),
'pending' => __('待支付', 'aksrt'),
'paid' => __('已支付', 'aksrt'),
'after_sale' => __('售后中', 'aksrt'),
'refunded' => __('已退款', 'aksrt'),
'closed' => __('已关闭', 'aksrt'),
];
@@ -57,8 +58,7 @@ $status_options = [
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('全部', 'aksrt'); ?></span><strong><?php echo esc_html((string) (int) ($stats['total'] ?? 0)); ?></strong></div>
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('待支付', 'aksrt'); ?></span><strong class="is-pending"><?php echo esc_html((string) (int) ($stats['pending'] ?? 0)); ?></strong></div>
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('已支付', 'aksrt'); ?></span><strong class="is-paid"><?php echo esc_html((string) (int) ($stats['paid'] ?? 0)); ?></strong></div>
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('已退款', 'aksrt'); ?></span><strong><?php echo esc_html((string) (int) ($stats['refunded'] ?? 0)); ?></strong></div>
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('售后待处理', 'aksrt'); ?></span><strong><?php echo esc_html((string) (int) ($stats['after_sale_pending'] ?? 0)); ?></strong></div>
<div class="aksrt-orders-page__stat"><span><?php esc_html_e('已关闭', 'aksrt'); ?></span><strong><?php echo esc_html((string) (int) ($stats['closed'] ?? 0)); ?></strong></div>
</div>
</header>
-6
View File
@@ -300,12 +300,6 @@ final class AfterSaleService {
return (int) ($params ? $wpdb->get_var($wpdb->prepare($sql, $params)) : $wpdb->get_var($sql));
}
public function count_user_open(int $user_id): int {
if ($user_id <= 0) return 0;
global $wpdb;
return (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$this->table()} WHERE user_id = %d AND status IN ('pending','processing')", $user_id));
}
/** A refund status transition is valid only for its own active application. */
public function can_finalize_order_refund(int $after_sale_id, string $order_number): bool {
if ($after_sale_id <= 0 || $order_number === '') return false;
+9 -1
View File
@@ -16,6 +16,7 @@ final class OrderCenterQuery {
public const STATUS_ALL = 'all';
public const STATUS_PENDING = 'pending';
public const STATUS_PAID = 'paid';
public const STATUS_AFTER_SALE = 'after_sale';
public const STATUS_REFUNDED = 'refunded';
public const STATUS_CLOSED = 'closed';
@@ -23,6 +24,7 @@ final class OrderCenterQuery {
self::STATUS_ALL,
self::STATUS_PENDING,
self::STATUS_PAID,
self::STATUS_AFTER_SALE,
self::STATUS_REFUNDED,
self::STATUS_CLOSED,
];
@@ -92,7 +94,7 @@ final class OrderCenterQuery {
/** @return string[] */
public function database_statuses(): array {
if ($this->status() === self::STATUS_ALL) {
if ($this->status() === self::STATUS_ALL || $this->status() === self::STATUS_AFTER_SALE) {
return array_merge(array_keys(DomainModel::order_status_labels()), ['refunding']);
}
if ($this->status() === self::STATUS_PAID) {
@@ -101,6 +103,11 @@ final class OrderCenterQuery {
return [$this->status()];
}
/** 售后中:订单带有「待审核 / 处理中」工单,与订单自身状态无关。 */
public function filters_open_after_sale(): bool {
return $this->status() === self::STATUS_AFTER_SALE;
}
/** @return array<string, scalar> */
public function url_values(): array {
return array_filter([
@@ -115,6 +122,7 @@ final class OrderCenterQuery {
public function repository_args(): array {
return [
'status_in' => $this->database_statuses(),
'after_sale_open' => $this->filters_open_after_sale(),
'product_type' => $this->product_type(),
'order_number_prefix' => $this->order_number(),
'limit' => $this->per_page(),
+15 -5
View File
@@ -264,7 +264,7 @@ trait OrderRepositoryCore {
if ($user_id <= 0) {
return [
'total' => 0,
'stats' => ['total' => 0, 'pending' => 0, 'paid' => 0, 'refunded' => 0, 'closed' => 0, 'after_sale_pending' => 0],
'stats' => ['total' => 0, 'pending' => 0, 'paid' => 0, 'closed' => 0],
];
}
@@ -279,13 +279,12 @@ trait OrderRepositoryCore {
SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS pending,
SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS paid,
SUM(CASE WHEN status = 'refunding' THEN 1 ELSE 0 END) AS paid_legacy,
SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS refunded,
SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS closed
FROM {$this->table()}
WHERE user_id = %d AND status IN ({$known_placeholders})";
$params = array_merge(
$parts['params'],
[Order::STATUS_PENDING, Order::STATUS_PAID, Order::STATUS_REFUNDED, Order::STATUS_CLOSED],
[Order::STATUS_PENDING, Order::STATUS_PAID, Order::STATUS_CLOSED],
[$user_id],
$known_statuses
);
@@ -297,9 +296,7 @@ trait OrderRepositoryCore {
'total' => (int) ($row['known_total'] ?? 0),
'pending' => (int) ($row['pending'] ?? 0),
'paid' => (int) ($row['paid'] ?? 0) + (int) ($row['paid_legacy'] ?? 0),
'refunded' => (int) ($row['refunded'] ?? 0),
'closed' => (int) ($row['closed'] ?? 0),
'after_sale_pending' => class_exists(AfterSaleService::class) ? AfterSaleService::get_instance()->count_user_open($user_id) : 0,
],
];
}
@@ -765,6 +762,19 @@ trait OrderRepositoryCore {
}
}
// 售后中:只认「待审核 / 处理中」两张在流转状态,与订单自身状态正交,
// 因此作为独立谓词叠加上去(工单已完结或已撤销不算售后中)。
if (!empty($args['after_sale_open']) && class_exists(AfterSaleService::class)) {
$open = [
AfterSaleService::STATUS_PENDING,
AfterSaleService::STATUS_PROCESSING,
];
$where[] = 'order_number IN (SELECT order_number FROM '
. \AKSRT\Core\DBMigrator::table('order_aftersale')
. ' WHERE status IN (' . implode(', ', array_fill(0, count($open), '%s')) . '))';
array_push($params, ...$open);
}
$product_type = sanitize_key((string) ($args['product_type'] ?? ''));
if ($product_type !== '') {
if ($product_type === \AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE) {
+1 -13
View File
@@ -103,18 +103,6 @@ class UserOrders {
return $this->get_order_detail_view($user_id, $order_number, true);
}
/** @return array<string, int> */
public function get_user_order_stats(int $user_id): array {
if ($user_id <= 0 || !class_exists(OrderRepository::class)) {
return ['total' => 0, 'pending' => 0, 'paid' => 0];
}
$summary = OrderRepository::get_instance()->get_user_order_center_summary($user_id);
return is_array($summary['stats'] ?? null)
? $summary['stats']
: ['total' => 0, 'pending' => 0, 'paid' => 0];
}
/** @return array<string, string> */
public function product_type_filter_options(): array {
if ($this->type_labels_cache === []) {
@@ -328,7 +316,7 @@ class UserOrders {
'total_pages' => 1,
'page' => 1,
'query' => $query->url_values(),
'stats' => ['total' => 0, 'pending' => 0, 'paid' => 0, 'refunded' => 0, 'closed' => 0, 'after_sale_pending' => 0],
'stats' => ['total' => 0, 'pending' => 0, 'paid' => 0, 'closed' => 0],
'invalidation_tags' => ['orders'],
'product_types' => $this->product_type_filter_options(),
];