feat(orders): redesign user order center

This commit is contained in:
2026-08-03 12:54:58 +08:00
parent 41506eeb3d
commit 26d307056e
10 changed files with 928 additions and 3652 deletions
+111 -112
View File
@@ -1,9 +1,6 @@
<?php
/**
* Public REST for order-center actions (pay / cancel / detail).
*
* Registered outside the admin SPA bootstrap so front-end order center
* always has routes even if an admin controller fails to load.
* Public REST for the knowledge-payment order center.
*
* @package AKSRT
*/
@@ -14,124 +11,126 @@ defined('ABSPATH') || exit;
class OrderCenterRest {
private static $instance = null;
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('rest_api_init', [$this, 'register_routes'], 20);
}
public function init(): void {
add_action('rest_api_init', [$this, 'register_routes'], 20);
}
public function register_routes(): void {
$ns = 'aksrt/v1';
// Allow common order_number charset (AKS… + optional separators).
$on = '(?P<order_number>[A-Za-z0-9_-]+)';
public function register_routes(): void {
$namespace = 'aksrt/v1';
$order_number = '(?P<order_number>[A-Za-z0-9_-]+)';
$query_args = [
'order_status' => ['sanitize_callback' => 'sanitize_key'],
'order_type' => ['sanitize_callback' => 'sanitize_key'],
'order_number' => ['sanitize_callback' => 'sanitize_text_field'],
'order_page' => ['sanitize_callback' => 'absint'],
];
register_rest_route($ns, '/shop/frontend/orders/' . $on . '/pay', [
'methods' => 'POST',
'callback' => [$this, 'pay'],
'permission_callback' => [$this, 'require_logged_in'],
]);
register_rest_route($namespace, '/orders/view', [
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_view'],
'permission_callback' => [$this, 'require_logged_in'],
'args' => $query_args,
]);
$route_args = [
'order_number' => [
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => static function ($value): bool {
return is_string($value) && (bool) preg_match('/^[A-Za-z0-9_-]+$/', $value);
},
],
];
register_rest_route($namespace, '/orders/' . $order_number . '/pay', [
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [$this, 'pay'],
'permission_callback' => [$this, 'require_logged_in'],
'args' => $route_args,
]);
register_rest_route($namespace, '/orders/' . $order_number . '/cancel', [
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [$this, 'cancel'],
'permission_callback' => [$this, 'require_logged_in'],
'args' => $route_args,
]);
register_rest_route($namespace, '/orders/' . $order_number, [
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_order'],
'permission_callback' => [$this, 'require_logged_in'],
'args' => $route_args,
]);
}
register_rest_route($ns, '/shop/frontend/orders/' . $on . '/cancel', [
'methods' => 'POST',
'callback' => [$this, 'cancel'],
'permission_callback' => [$this, 'require_logged_in'],
]);
/** @return true|\WP_Error */
public function require_logged_in() {
if (!is_user_logged_in()) {
return new \WP_Error('aksrt_rest_forbidden', __('请先登录', 'aksrt'), ['status' => 401]);
}
return true;
}
register_rest_route($ns, '/shop/frontend/orders/' . $on, [
[
'methods' => 'GET',
'callback' => [$this, 'get_order'],
'permission_callback' => [$this, 'require_logged_in'],
],
[
// order-center.js historically POSTed for refresh — accept both.
'methods' => 'POST',
'callback' => [$this, 'get_order'],
'permission_callback' => [$this, 'require_logged_in'],
],
]);
}
public function get_view(\WP_REST_Request $request): \WP_REST_Response {
if (!class_exists(UserOrders::class)) {
return $this->error(__('订单服务不可用', 'aksrt'), 500);
}
$view = UserOrders::get_instance()->get_order_center_view(get_current_user_id(), $this->query_args($request));
return $this->success(['data' => $view]);
}
/**
* @return true|\WP_Error
*/
public function require_logged_in() {
if (!is_user_logged_in()) {
return new \WP_Error(
'aksrt_rest_forbidden',
__('请先登录', 'aksrt'),
['status' => 401]
);
}
return true;
}
public function pay(\WP_REST_Request $request): \WP_REST_Response {
$params = $request->get_json_params();
$params = is_array($params) ? $params : [];
$result = UserOrders::get_instance()->pay_order(
get_current_user_id(),
sanitize_text_field((string) $request->get_param('order_number')),
sanitize_key((string) ($params['payment_method'] ?? ''))
);
return !empty($result['success'])
? $this->success(['data' => $result['data'] ?? []])
: $this->error((string) ($result['message'] ?? __('支付失败', 'aksrt')), 400);
}
public function pay(\WP_REST_Request $request): \WP_REST_Response {
$order_number = sanitize_text_field((string) $request->get_param('order_number'));
$params = $request->get_json_params();
if (!is_array($params)) {
$params = [];
}
$payment_method = sanitize_key((string) ($params['payment_method'] ?? ''));
public function cancel(\WP_REST_Request $request): \WP_REST_Response {
$result = UserOrders::get_instance()->cancel_order(
get_current_user_id(),
sanitize_text_field((string) $request->get_param('order_number'))
);
return !empty($result['success'])
? $this->success(['data' => $result['data'] ?? []])
: $this->error((string) ($result['message'] ?? __('关闭订单失败', 'aksrt')), 400);
}
if (!class_exists(UserOrders::class)) {
return $this->error(__('订单服务不可用', 'aksrt'), 500);
}
$result = UserOrders::get_instance()->pay_order(get_current_user_id(), $order_number, $payment_method);
public function get_order(\WP_REST_Request $request): \WP_REST_Response {
$order = UserOrders::get_instance()->get_order_detail_view(
get_current_user_id(),
sanitize_text_field((string) $request->get_param('order_number'))
);
return $order ? $this->success(['data' => $order]) : $this->error(__('订单不存在', 'aksrt'), 404);
}
if (empty($result['success'])) {
return $this->error((string) ($result['message'] ?? __('支付失败', 'aksrt')), 400);
}
/** @return array<string, mixed> */
private function query_args(\WP_REST_Request $request): array {
return [
'status' => $request->get_param('order_status'),
'type' => $request->get_param('order_type'),
'order_number' => $request->get_param('order_number'),
'page' => $request->get_param('order_page'),
];
}
return $this->success(['data' => $result['data'] ?? []]);
}
private function success(array $data = [], int $status = 200): \WP_REST_Response {
return new \WP_REST_Response(array_merge(['success' => true], $data), $status);
}
public function cancel(\WP_REST_Request $request): \WP_REST_Response {
$order_number = sanitize_text_field((string) $request->get_param('order_number'));
if (!class_exists(UserOrders::class)) {
return $this->error(__('订单服务不可用', 'aksrt'), 500);
}
$result = UserOrders::get_instance()->cancel_order(get_current_user_id(), $order_number);
if (empty($result['success'])) {
return $this->error((string) ($result['message'] ?? __('取消订单失败', 'aksrt')), 400);
}
return $this->success(['data' => $result['data'] ?? []]);
}
public function get_order(\WP_REST_Request $request): \WP_REST_Response {
$order_number = sanitize_text_field((string) $request->get_param('order_number'));
$order = null;
if (!class_exists(UserOrders::class)) {
return $this->error(__('订单服务不可用', 'aksrt'), 500);
}
$order = UserOrders::get_instance()->get_order_detail_view(get_current_user_id(), $order_number);
if (!$order) {
return $this->error(__('订单不存在', 'aksrt'), 404);
}
return $this->success(['data' => $order]);
}
private function success(array $data = [], int $status = 200): \WP_REST_Response {
return new \WP_REST_Response(array_merge(['success' => true], $data), $status);
}
private function error(string $message, int $status = 400): \WP_REST_Response {
return new \WP_REST_Response([
'success' => false,
'message' => $message,
], $status);
}
private function error(string $message, int $status = 400): \WP_REST_Response {
return new \WP_REST_Response(['success' => false, 'message' => $message], $status);
}
}