refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
1 parent
8f349b40fb
commit
aaf458af1c
404 files changed
+13602
-7705
No files matched your search
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Shop;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class Shop {
|
||||
use Part1;
|
||||
use Part2;
|
||||
use Part3;
|
||||
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
private ?CartService $cart_svc = null;
|
||||
private ?CheckoutService $checkout_svc = null;
|
||||
private ?AddressService $address_svc = null;
|
||||
private ?ProductService $product_svc = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function cart_service(): CartService {
|
||||
if (!$this->cart_svc) {
|
||||
$this->cart_svc = new CartService($this);
|
||||
}
|
||||
return $this->cart_svc;
|
||||
}
|
||||
|
||||
private function checkout_service(): CheckoutService {
|
||||
if (!$this->checkout_svc) {
|
||||
$this->checkout_svc = new CheckoutService($this);
|
||||
}
|
||||
return $this->checkout_svc;
|
||||
}
|
||||
|
||||
private function address_service(): AddressService {
|
||||
if (!$this->address_svc) {
|
||||
$this->address_svc = new AddressService($this);
|
||||
}
|
||||
return $this->address_svc;
|
||||
}
|
||||
|
||||
private function product_service(): ProductService {
|
||||
if ($this->product_svc === null) {
|
||||
$this->product_svc = new ProductService($this);
|
||||
}
|
||||
return $this->product_svc;
|
||||
}
|
||||
|
||||
public function get_config(): array {
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
return \AKSRT\Settings\Settings::get_shop_settings();
|
||||
}
|
||||
|
||||
return ['enabled' => false];
|
||||
}
|
||||
|
||||
public function get_products(array $args = []): array {
|
||||
$limit = max(1, (int) ($args['limit'] ?? 20));
|
||||
$offset = max(0, (int) ($args['offset'] ?? 0));
|
||||
$search = sanitize_text_field((string) ($args['search'] ?? ''));
|
||||
$status = sanitize_key((string) ($args['status'] ?? ''));
|
||||
|
||||
$query_args = [
|
||||
'post_type' => 'aksrt_product',
|
||||
'posts_per_page' => $limit,
|
||||
'offset' => $offset,
|
||||
'post_status' => $status !== '' ? $status : ['publish', 'draft', 'pending'],
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
];
|
||||
|
||||
if ($search !== '') {
|
||||
$query_args['s'] = $search;
|
||||
}
|
||||
|
||||
$query = new \WP_Query($query_args);
|
||||
$products = [];
|
||||
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$products[] = $this->build_product_card_view(get_the_ID());
|
||||
}
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
public function get_product_detail_view(int $product_id): array {
|
||||
$product = get_post($product_id);
|
||||
if (!$product || $product->post_type !== 'aksrt_product') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$detail = $this->build_product_card_view($product_id);
|
||||
$detail['content'] = (string) $product->post_content;
|
||||
$detail['excerpt'] = (string) $product->post_excerpt;
|
||||
$detail['permalink'] = get_permalink($product_id) ?: '';
|
||||
$detail['status'] = (string) get_post_status($product_id);
|
||||
$detail['gallery'] = $this->product_service()->get_product_gallery($product_id);
|
||||
$detail['categories'] = wp_get_post_terms($product_id, 'aksrt_product_cat', ['fields' => 'all']) ?: [];
|
||||
$detail['tags'] = wp_get_post_terms($product_id, 'aksrt_product_tag', ['fields' => 'all']) ?: [];
|
||||
$detail['shipping'] = $this->product_service()->get_product_shipping_profile($product_id);
|
||||
$detail['after_sale'] = $this->product_service()->get_product_after_sale_profile($product_id);
|
||||
$detail['marketing'] = $this->product_service()->get_product_marketing_profile($product_id);
|
||||
$detail['checkout_hint'] = $this->get_checkout_hint($detail);
|
||||
$detail['purchase_enabled'] = ($detail['is_unlimited'] || $detail['stock'] > 0) && $detail['status'] === 'publish';
|
||||
$detail['default_quantity'] = 1;
|
||||
$detail['options'] = $this->get_product_options($product_id);
|
||||
$detail['has_options'] = $this->has_options($product_id);
|
||||
$detail['price_range'] = $this->get_price_range($product_id);
|
||||
$detail['pay_modo'] = sanitize_key((string) get_post_meta($product_id, '_aksrt_pay_modo', true)) ?: '0';
|
||||
$detail['start_price'] = (float) get_post_meta($product_id, '_aksrt_start_price', true);
|
||||
$detail['shipping_type'] = sanitize_key((string) get_post_meta($product_id, '_aksrt_shipping_type', true)) ?: 'express';
|
||||
$detail['auto_delivery'] = $this->get_auto_delivery_config($product_id);
|
||||
$detail['limit_buy'] = $this->get_limit_buy_config($product_id);
|
||||
$detail['services'] = $this->get_product_services($product_id);
|
||||
$detail['params'] = $this->get_product_params($product_id);
|
||||
|
||||
return $detail;
|
||||
}
|
||||
|
||||
public function get_product_edit_view(int $user_id, int $product_id = 0): array {
|
||||
$view = [
|
||||
'allowed' => true,
|
||||
'is_edit' => false,
|
||||
'product' => [
|
||||
'id' => 0,
|
||||
'title' => '',
|
||||
'price' => 0,
|
||||
'stock' => 0,
|
||||
'excerpt' => '',
|
||||
'content' => '',
|
||||
'image' => '',
|
||||
'gallery' => [],
|
||||
'categories' => [],
|
||||
'tags' => [],
|
||||
'shipping_mode' => 'express',
|
||||
'shipping_fee' => 0,
|
||||
'after_sale_policy' => '',
|
||||
'support_refund' => true,
|
||||
'status' => 'draft',
|
||||
'options' => [],
|
||||
'pay_modo' => '0',
|
||||
'start_price' => 0,
|
||||
'product_video' => [],
|
||||
'product_params' => '',
|
||||
],
|
||||
'taxonomy' => [
|
||||
'categories' => $this->product_service()->get_term_options('aksrt_product_cat'),
|
||||
'tags' => $this->product_service()->get_term_options('aksrt_product_tag'),
|
||||
],
|
||||
'shipping_modes' => $this->get_shipping_method_options(),
|
||||
'status_options' => [
|
||||
['value' => 'draft', 'label' => __('草稿', 'aksrt')],
|
||||
['value' => 'publish', 'label' => __('发布', 'aksrt')],
|
||||
['value' => 'pending', 'label' => __('待审核', 'aksrt')],
|
||||
],
|
||||
];
|
||||
|
||||
if ($product_id <= 0) {
|
||||
return $view;
|
||||
}
|
||||
|
||||
$product = get_post($product_id);
|
||||
if (!$product || $product->post_type !== 'aksrt_product') {
|
||||
return $view;
|
||||
}
|
||||
|
||||
$is_owner = (int) $product->post_author === $user_id || current_user_can('manage_options');
|
||||
if (!$is_owner) {
|
||||
$view['allowed'] = false;
|
||||
return $view;
|
||||
}
|
||||
|
||||
$view['is_edit'] = true;
|
||||
$videos = get_post_meta($product_id, '_aksrt_product_video', true);
|
||||
if (!is_array($videos)) {
|
||||
$videos = [];
|
||||
}
|
||||
$view['product'] = [
|
||||
'id' => $product_id,
|
||||
'title' => (string) $product->post_title,
|
||||
'price' => (float) get_post_meta($product_id, '_aksrt_price', true),
|
||||
'stock' => (int) get_post_meta($product_id, '_aksrt_stock', true),
|
||||
'excerpt' => (string) $product->post_excerpt,
|
||||
'content' => (string) $product->post_content,
|
||||
'image' => $this->resolve_main_image($product_id),
|
||||
'gallery' => $this->product_service()->get_product_gallery($product_id),
|
||||
'categories' => wp_get_post_terms($product_id, 'aksrt_product_cat', ['fields' => 'ids']) ?: [],
|
||||
'tags' => wp_get_post_terms($product_id, 'aksrt_product_tag', ['fields' => 'names']) ?: [],
|
||||
'shipping_mode' => sanitize_key((string) get_post_meta($product_id, '_aksrt_shipping_mode', true)) ?: 'express',
|
||||
'shipping_fee' => (float) get_post_meta($product_id, '_aksrt_shipping_fee', true),
|
||||
'after_sale_policy' => (string) get_post_meta($product_id, '_aksrt_after_sale_policy', true),
|
||||
'support_refund' => get_post_meta($product_id, '_aksrt_support_refund', true) !== '0',
|
||||
'status' => (string) get_post_status($product_id),
|
||||
'options' => method_exists($this, 'get_product_options') ? $this->get_product_options($product_id) : [],
|
||||
'pay_modo' => sanitize_key((string) get_post_meta($product_id, '_aksrt_pay_modo', true)) ?: '0',
|
||||
'start_price' => (float) get_post_meta($product_id, '_aksrt_start_price', true),
|
||||
'product_video' => array_values(array_filter(array_map('esc_url_raw', $videos))),
|
||||
'product_params' => (string) get_post_meta($product_id, '_aksrt_product_params', true),
|
||||
];
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
public function get_cart_page_view(int $user_id): array {
|
||||
$pending_orders = $this->get_order_center_view($user_id, [
|
||||
'page' => 1,
|
||||
'per_page' => 5,
|
||||
'status' => \AKSRT\Payment\Order::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
return [
|
||||
'payment_methods' => $this->get_payment_method_options(),
|
||||
'shipping_methods' => $this->get_shipping_method_options(),
|
||||
'address_book' => $this->get_user_address_book($user_id),
|
||||
'coupon_enabled' => class_exists('\AKSRT\Payment\Coupon'),
|
||||
'currency_symbol' => '¥',
|
||||
'pending_orders' => $pending_orders['items'] ?? [],
|
||||
'cart_items' => $this->get_cart($user_id),
|
||||
'cart_count' => $this->get_cart_count($user_id),
|
||||
];
|
||||
}
|
||||
|
||||
public function get_cart(int $user_id): array {
|
||||
return $this->cart_service()->get_cart($user_id);
|
||||
}
|
||||
|
||||
public function get_cart_count(int $user_id): int {
|
||||
return $this->cart_service()->get_cart_count($user_id);
|
||||
}
|
||||
|
||||
public function add_to_cart(int $user_id, int $product_id, string $options_string = '', int $count = 1): array {
|
||||
return $this->cart_service()->add_to_cart($user_id, $product_id, $options_string, $count);
|
||||
}
|
||||
|
||||
public function reduce_from_cart(int $user_id, int $product_id, string $options_string = '', int $count = 1): array {
|
||||
return $this->cart_service()->reduce_from_cart($user_id, $product_id, $options_string, $count);
|
||||
}
|
||||
|
||||
public function remove_from_cart(int $user_id, int $product_id, string $options_string = ''): array {
|
||||
return $this->cart_service()->remove_from_cart($user_id, $product_id, $options_string);
|
||||
}
|
||||
|
||||
public function remove_multi_from_cart(int $user_id, array $items): array {
|
||||
return $this->cart_service()->remove_multi_from_cart($user_id, $items);
|
||||
}
|
||||
|
||||
public function empty_cart(int $user_id): array {
|
||||
return $this->cart_service()->empty_cart($user_id);
|
||||
}
|
||||
|
||||
public function validate_cart_item(int $product_id, string $options_string = '') {
|
||||
return $this->cart_service()->validate_cart_item($product_id, $options_string);
|
||||
}
|
||||
|
||||
public function get_order_center_view(int $user_id, array $args = []): array {
|
||||
if ($user_id <= 0 || !class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
return ['items' => [], 'total' => 0, 'total_pages' => 1, 'page' => 1];
|
||||
}
|
||||
|
||||
$page = max(1, (int) ($args['page'] ?? 1));
|
||||
$per_page = max(1, min(50, (int) ($args['per_page'] ?? 20)));
|
||||
$status = sanitize_key((string) ($args['status'] ?? ''));
|
||||
|
||||
$repo_args = [
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page - 1) * $per_page,
|
||||
];
|
||||
|
||||
if ($status !== '') {
|
||||
$repo_args['status'] = $status;
|
||||
}
|
||||
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
$orders = $repo->get_user_orders($user_id, $repo_args);
|
||||
$total = $repo->count_user_orders($user_id, $repo_args);
|
||||
|
||||
$items = [];
|
||||
foreach ($orders as $order) {
|
||||
$items[] = $this->decorate_order_view($order);
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'total_pages' => max(1, (int) ceil($total / $per_page)),
|
||||
'page' => $page,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_order_detail_view(int $user_id, string $order_number): ?array {
|
||||
if ($user_id <= 0 || $order_number === '' || !class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$order = \AKSRT\Payment\OrderRepository::get_instance()->get_user_order($user_id, $order_number);
|
||||
if (!$order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->decorate_order_view($order, true);
|
||||
}
|
||||
|
||||
public function preview_checkout(int $user_id, array $payload): array {
|
||||
return $this->checkout_service()->preview_checkout($user_id, $payload);
|
||||
}
|
||||
|
||||
public function create_checkout_order(int $user_id, array $payload): array {
|
||||
return $this->checkout_service()->create_checkout_order($user_id, $payload);
|
||||
}
|
||||
|
||||
public function pay_order(int $user_id, string $order_number, string $payment_method): array {
|
||||
return $this->checkout_service()->pay_order($user_id, $order_number, $payment_method);
|
||||
}
|
||||
|
||||
public function get_user_favorites(int $user_id, int $page = 1, int $per_page = 20): array {
|
||||
if ($user_id <= 0) {
|
||||
return ['items' => [], 'total' => 0];
|
||||
}
|
||||
$fav_ids = get_user_meta($user_id, 'aksrt_product_favorites', true);
|
||||
if (!is_array($fav_ids) || empty($fav_ids)) {
|
||||
return ['items' => [], 'total' => 0];
|
||||
}
|
||||
$total = count($fav_ids);
|
||||
$paged = array_slice(array_reverse($fav_ids), ($page - 1) * $per_page, $per_page);
|
||||
$items = [];
|
||||
foreach ($paged as $pid) {
|
||||
$card = $this->build_product_card_view((int) $pid);
|
||||
if (!empty($card)) {
|
||||
$items[] = $card;
|
||||
}
|
||||
}
|
||||
return ['items' => $items, 'total' => $total];
|
||||
}
|
||||
|
||||
public function toggle_favorite(int $user_id, int $product_id): array {
|
||||
if ($user_id <= 0 || $product_id <= 0) {
|
||||
return ['success' => false];
|
||||
}
|
||||
$fav_ids = get_user_meta($user_id, 'aksrt_product_favorites', true);
|
||||
if (!is_array($fav_ids)) {
|
||||
$fav_ids = [];
|
||||
}
|
||||
$key = array_search($product_id, $fav_ids, true);
|
||||
if ($key !== false) {
|
||||
unset($fav_ids[$key]);
|
||||
$fav_ids = array_values($fav_ids);
|
||||
$is_fav = false;
|
||||
} else {
|
||||
$fav_ids[] = $product_id;
|
||||
$is_fav = true;
|
||||
}
|
||||
update_user_meta($user_id, 'aksrt_product_favorites', $fav_ids);
|
||||
$count = (int) get_post_meta($product_id, '_aksrt_favorite_count', true);
|
||||
update_post_meta($product_id, '_aksrt_favorite_count', max(0, $count + ($is_fav ? 1 : -1)));
|
||||
if ($is_fav) {
|
||||
do_action('aksrt_product_favorited', $user_id, $product_id);
|
||||
} else {
|
||||
do_action('aksrt_product_unfavorited', $user_id, $product_id);
|
||||
}
|
||||
return ['success' => true, 'is_favorite' => $is_fav, 'count' => count($fav_ids)];
|
||||
}
|
||||
|
||||
public function is_favorite(int $user_id, int $product_id): bool {
|
||||
if ($user_id <= 0) return false;
|
||||
$fav_ids = get_user_meta($user_id, 'aksrt_product_favorites', true);
|
||||
return is_array($fav_ids) && in_array($product_id, $fav_ids, true);
|
||||
}
|
||||
|
||||
public function re_purchase(int $user_id, string $order_number): array {
|
||||
$items = $this->get_order_items($order_number);
|
||||
if (empty($items)) {
|
||||
return ['success' => false, 'message' => __('订单无商品记录', 'aksrt')];
|
||||
}
|
||||
$added = 0;
|
||||
foreach ($items as $item) {
|
||||
$pid = (int) $item['product_id'];
|
||||
$qty = max(1, (int) $item['quantity']);
|
||||
$opt_key = (string) $item['options_key'];
|
||||
$result = $this->add_to_cart($user_id, $pid, $opt_key, $qty);
|
||||
if (!empty($result['success'])) {
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
return ['success' => $added > 0, 'added' => $added, 'cart_count' => $this->get_cart_count($user_id)];
|
||||
}
|
||||
|
||||
public function cancel_order(int $user_id, string $order_number): array {
|
||||
if ($user_id <= 0 || $order_number === '' || !class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
return ['success' => false, 'message' => __('订单不存在。', 'aksrt')];
|
||||
}
|
||||
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
$order = $repo->get_user_order($user_id, $order_number);
|
||||
if (!$order) {
|
||||
return ['success' => false, 'message' => __('订单不存在。', 'aksrt')];
|
||||
}
|
||||
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
if (!in_array($status, [\AKSRT\Payment\Order::STATUS_PENDING, \AKSRT\Payment\Order::STATUS_CREATED, \AKSRT\Payment\Order::STATUS_EXPIRED], true)) {
|
||||
return ['success' => false, 'message' => __('当前订单不可取消。', 'aksrt')];
|
||||
}
|
||||
|
||||
$repo->update_status($order_number, \AKSRT\Payment\Order::STATUS_CANCELLED, [
|
||||
'force' => true,
|
||||
'note' => __('用户在前台取消订单。', 'aksrt'),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'order' => $this->get_order_detail_view($user_id, $order_number),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user