Move ~94 root class shells and root *-traits into domain directories (core, auth, user, credits, content, payment/gateways, forum, shop, sms, ai, messages, extensions, widgets, settings, admin). Update bootstrap require paths, co-locate traits with owners, fix forum/user relative requires, drop duplicate shop controller requires, and wire home-modules load. Path-only; class names and init order unchanged.
148 lines
5.4 KiB
PHP
148 lines
5.4 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Order facade.
|
|
*
|
|
* Keeps the public contract stable while delegating storage to
|
|
* `AKSRT_Order_Repository`.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Order {
|
|
|
|
public const STATUS_CREATED = AKSRT_Domain_Model::ORDER_STATUS_CREATED;
|
|
public const STATUS_PENDING = AKSRT_Domain_Model::ORDER_STATUS_PENDING;
|
|
public const STATUS_PAID = AKSRT_Domain_Model::ORDER_STATUS_PAID;
|
|
public const STATUS_DELIVERED = AKSRT_Domain_Model::ORDER_STATUS_DELIVERED;
|
|
public const STATUS_COMPLETED = AKSRT_Domain_Model::ORDER_STATUS_COMPLETED;
|
|
public const STATUS_CANCELLED = AKSRT_Domain_Model::ORDER_STATUS_CANCELLED;
|
|
public const STATUS_EXPIRED = AKSRT_Domain_Model::ORDER_STATUS_EXPIRED;
|
|
public const STATUS_REFUNDING = AKSRT_Domain_Model::ORDER_STATUS_REFUNDING;
|
|
public const STATUS_REFUNDED = AKSRT_Domain_Model::ORDER_STATUS_REFUNDED;
|
|
public const STATUS_PENDING_REVIEW = AKSRT_Domain_Model::ORDER_STATUS_PENDING_REVIEW;
|
|
|
|
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_valid_statuses(): array {
|
|
return AKSRT_Domain_Model::order_status_labels();
|
|
}
|
|
|
|
public static function can_transition(string $from, string $to): bool {
|
|
return AKSRT_Domain_Model::can_transition_order_status($from, $to);
|
|
}
|
|
|
|
public function init(): void {
|
|
add_action('wp_ajax_aksrt_order_list', [$this, 'ajax_list']);
|
|
add_action('wp_ajax_aksrt_order_update_status', [$this, 'ajax_update_status']);
|
|
add_action('wp_ajax_aksrt_order_stats', [$this, 'ajax_stats']);
|
|
}
|
|
|
|
public function create(int $user_id, string $product_type, int $product_id, float $amount, string $currency = 'CNY'): string {
|
|
// Guest content orders may use user_id=0 (Zibll pay_no_logged_in).
|
|
if ($user_id < 0 || $product_id < 0 || $amount < 0) {
|
|
return '';
|
|
}
|
|
|
|
return $this->repo()->create([
|
|
'user_id' => $user_id,
|
|
'product_type' => sanitize_key($product_type),
|
|
'product_id' => $product_id,
|
|
'amount' => $amount,
|
|
'currency' => sanitize_key($currency),
|
|
'status' => self::STATUS_PENDING,
|
|
]);
|
|
}
|
|
|
|
public function get(string $order_number): ?array {
|
|
return $this->repo()->get($order_number);
|
|
}
|
|
|
|
public function get_user_orders(int $user_id, array $args = []): array {
|
|
return $this->repo()->get_user_orders($user_id, $args);
|
|
}
|
|
|
|
public function update_status(string $order_number, string $new_status, array $extra = []): bool {
|
|
return $this->repo()->update_status($order_number, $new_status, $extra);
|
|
}
|
|
|
|
public function get_stats(int $user_id = 0): array {
|
|
return $this->repo()->get_stats($user_id);
|
|
}
|
|
|
|
public function has_paid_access(int $user_id, string $product_type, int $product_id): bool {
|
|
return $this->repo()->has_paid_access($user_id, $product_type, $product_id);
|
|
}
|
|
|
|
public function get_valid_vip_order(int $user_id): ?array {
|
|
return $this->repo()->get_valid_vip_order($user_id);
|
|
}
|
|
|
|
public function ajax_list(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
|
|
}
|
|
|
|
$status = sanitize_key((string) ($_GET['status'] ?? ''));
|
|
$search = sanitize_text_field((string) ($_GET['search'] ?? ''));
|
|
$limit = max(1, min(100, (int) ($_GET['limit'] ?? 20)));
|
|
$offset = max(0, (int) ($_GET['offset'] ?? 0));
|
|
|
|
$orders = $this->repo()->get_orders_admin([
|
|
'status' => $status,
|
|
'limit' => $limit,
|
|
'offset' => $offset,
|
|
], $search);
|
|
|
|
wp_send_json_success([
|
|
'orders' => $orders,
|
|
'stats' => $this->get_stats(),
|
|
'total' => $this->repo()->get_orders_count(['status' => $status], $search),
|
|
]);
|
|
}
|
|
|
|
public function ajax_update_status(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
|
|
}
|
|
|
|
$order_number = sanitize_text_field((string) ($_POST['order_number'] ?? ''));
|
|
$new_status = sanitize_key((string) ($_POST['status'] ?? ''));
|
|
$note = sanitize_textarea_field((string) ($_POST['note'] ?? ''));
|
|
|
|
if ($order_number === '' || $new_status === '') {
|
|
wp_send_json_error(['message' => __('参数无效', 'aksrt')]);
|
|
}
|
|
|
|
if ($this->update_status($order_number, $new_status, ['note' => $note, 'force' => true])) {
|
|
wp_send_json_success(['message' => __('订单状态已更新', 'aksrt')]);
|
|
}
|
|
|
|
wp_send_json_error(['message' => __('更新失败', 'aksrt')]);
|
|
}
|
|
|
|
public function ajax_stats(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
|
|
}
|
|
|
|
wp_send_json_success($this->get_stats());
|
|
}
|
|
|
|
private function repo(): AKSRT_Order_Repository {
|
|
return AKSRT_Order_Repository::get_instance();
|
|
}
|
|
}
|