perf(modal): unify cancellable frontend modal flows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 02:04:55 +08:00
co-authored by Claude Fable 5
parent 25ce1d8f43
commit 5375ad6429
36 changed files with 2623 additions and 1806 deletions
+99 -63
View File
@@ -100,46 +100,14 @@ trait OrderRepositoryCore {
public function get_user_orders(int $user_id, array $args = []): array {
global $wpdb;
$where = [];
$params = [];
if ($user_id > 0) {
$where[] = 'user_id = %d';
$params[] = $user_id;
}
$statuses = array_values(array_filter(array_map('sanitize_key', (array) ($args['status_in'] ?? []))));
if ($statuses !== []) {
$where[] = 'status IN (' . implode(', ', array_fill(0, count($statuses), '%s')) . ')';
array_push($params, ...$statuses);
} else {
$status = sanitize_key((string) ($args['status'] ?? ''));
if ($status !== '') {
$where[] = 'status = %s';
$params[] = $status;
}
}
$product_type = sanitize_key((string) ($args['product_type'] ?? ''));
if ($product_type !== '') {
$where[] = 'product_type = %s';
$params[] = $product_type;
}
$order_number_prefix = strtoupper(preg_replace('/[^A-Za-z0-9_-]/', '', (string) ($args['order_number_prefix'] ?? '')) ?? '');
if ($order_number_prefix !== '') {
$where[] = 'order_number LIKE %s';
$params[] = $wpdb->esc_like($order_number_prefix) . '%';
}
$limit = max(1, min(100, (int) ($args['limit'] ?? 20)));
$parts = $this->get_user_order_query_parts($user_id, $args);
$limit = max(1, min(100, (int) ($args['limit'] ?? 20)));
$offset = max(0, (int) ($args['offset'] ?? 0));
$where_sql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT * FROM {$this->table()} {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";
$params = $parts['params'];
$params[] = $limit;
$params[] = $offset;
$sql = "SELECT * FROM {$this->table()} {$parts['where']} ORDER BY created_at DESC LIMIT %d OFFSET %d";
$rows = $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A);
return array_map([$this, 'normalize_row'], (array) $rows);
@@ -175,37 +143,60 @@ trait OrderRepositoryCore {
return 0;
}
$where = ['user_id = %d'];
$params = [$user_id];
$parts = $this->get_user_order_query_parts($user_id, $args);
return (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$this->table()} {$parts['where']}",
$parts['params']
));
}
$statuses = array_values(array_filter(array_map('sanitize_key', (array) ($args['status_in'] ?? []))));
if ($statuses !== []) {
$where[] = 'status IN (' . implode(', ', array_fill(0, count($statuses), '%s')) . ')';
array_push($params, ...$statuses);
} else {
$status = sanitize_key((string) ($args['status'] ?? ''));
if ($status !== '') {
$where[] = 'status = %s';
$params[] = $status;
}
/**
* Get the filtered list total and the user's order-center counters in one query.
*
* The filtered total mirrors the list query while the counters intentionally keep
* their existing user-wide semantics, so changing a filter does not change the
* summary cards. The subquery avoids four independent COUNT statements.
*
* @return array{total:int,stats:array{total:int,pending:int,completed:int}}
*/
public function get_user_order_center_summary(int $user_id, array $args = []): array {
global $wpdb;
if ($user_id <= 0) {
return [
'total' => 0,
'stats' => ['total' => 0, 'pending' => 0, 'completed' => 0],
];
}
$product_type = sanitize_key((string) ($args['product_type'] ?? ''));
if ($product_type !== '') {
$where[] = 'product_type = %s';
$params[] = $product_type;
}
$parts = $this->get_user_order_query_parts($user_id, $args);
$pending_statuses = [Order::STATUS_CREATED, Order::STATUS_PENDING];
$paid_status = Order::STATUS_PAID;
$known_statuses = OrderCenterQuery::from_array([])->database_statuses();
$known_placeholders = implode(', ', array_fill(0, count($known_statuses), '%s'));
$sql = "SELECT
(SELECT COUNT(*) FROM {$this->table()} {$parts['where']}) AS filtered_total,
COUNT(*) AS known_total,
SUM(CASE WHEN status IN (%s, %s) THEN 1 ELSE 0 END) AS pending,
SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS completed
FROM {$this->table()}
WHERE user_id = %d AND status IN ({$known_placeholders})";
$params = array_merge(
$parts['params'],
$pending_statuses,
[$paid_status, $user_id],
$known_statuses
);
$row = $wpdb->get_row($wpdb->prepare($sql, $params), ARRAY_A);
$order_number_prefix = strtoupper(preg_replace('/[^A-Za-z0-9_-]/', '', (string) ($args['order_number_prefix'] ?? '')) ?? '');
if ($order_number_prefix !== '') {
$where[] = 'order_number LIKE %s';
$params[] = $wpdb->esc_like($order_number_prefix) . '%';
}
$where_sql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT COUNT(*) FROM {$this->table()} {$where_sql}";
return (int) $wpdb->get_var($wpdb->prepare($sql, $params));
return [
'total' => (int) ($row['filtered_total'] ?? 0),
'stats' => [
'total' => (int) ($row['known_total'] ?? 0),
'pending' => (int) ($row['pending'] ?? 0),
'completed' => (int) ($row['completed'] ?? 0),
],
];
}
@@ -515,6 +506,51 @@ trait OrderRepositoryCore {
* Internal helpers
* ----------------------------------------------------------------*/
/**
* Build the shared list/count predicate without changing caller semantics.
*
* @return array{where:string,params:array<int,mixed>}
*/
private function get_user_order_query_parts(int $user_id, array $args = []): array {
global $wpdb;
$where = [];
$params = [];
if ($user_id > 0) {
$where[] = 'user_id = %d';
$params[] = $user_id;
}
$statuses = array_values(array_filter(array_map('sanitize_key', (array) ($args['status_in'] ?? []))));
if ($statuses !== []) {
$where[] = 'status IN (' . implode(', ', array_fill(0, count($statuses), '%s')) . ')';
array_push($params, ...$statuses);
} else {
$status = sanitize_key((string) ($args['status'] ?? ''));
if ($status !== '') {
$where[] = 'status = %s';
$params[] = $status;
}
}
$product_type = sanitize_key((string) ($args['product_type'] ?? ''));
if ($product_type !== '') {
$where[] = 'product_type = %s';
$params[] = $product_type;
}
$order_number_prefix = strtoupper(preg_replace('/[^A-Za-z0-9_-]/', '', (string) ($args['order_number_prefix'] ?? '')) ?? '');
if ($order_number_prefix !== '') {
$where[] = 'order_number LIKE %s';
$params[] = $wpdb->esc_like($order_number_prefix) . '%';
}
return [
'where' => $where ? 'WHERE ' . implode(' AND ', $where) : '',
'params' => $params,
];
}
private function normalize_row(array $row): array {
$row['id'] = (int) $row['id'];
$row['user_id'] = (int) $row['user_id'];