导入 AKSRT 主题开发源码
This commit is contained in:
1 parent
9681dd3d48
commit
1268a55dd0
595 files changed
+127705
No files matched your search
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Shop_Part_1 {
|
||||
|
||||
public function create_after_sale_request(int $user_id, array $payload): array {
|
||||
if ($user_id <= 0 || !class_exists('AKSRT_After_Sale')) {
|
||||
return ['success' => false, 'message' => __('售后系统不可用。', 'aksrt')];
|
||||
}
|
||||
|
||||
$order_number = sanitize_text_field((string) ($payload['order_number'] ?? ''));
|
||||
$reason = sanitize_textarea_field((string) ($payload['reason'] ?? ''));
|
||||
$type = sanitize_key((string) ($payload['type'] ?? AKSRT_After_Sale::TYPE_REFUND));
|
||||
|
||||
if ($order_number === '' || $reason === '') {
|
||||
return ['success' => false, 'message' => __('请填写售后原因。', 'aksrt')];
|
||||
}
|
||||
|
||||
$order = $this->get_order_detail_view($user_id, $order_number);
|
||||
if (!$order) {
|
||||
return ['success' => false, 'message' => __('订单不存在。', 'aksrt')];
|
||||
}
|
||||
|
||||
$ticket_id = AKSRT_After_Sale::get_instance()->create([
|
||||
'order_number' => $order_number,
|
||||
'user_id' => $user_id,
|
||||
'type' => $type,
|
||||
'reason' => $reason,
|
||||
'amount' => (float) ($order['amount'] ?? 0),
|
||||
'note' => sanitize_textarea_field((string) ($payload['note'] ?? '')),
|
||||
]);
|
||||
|
||||
if ($ticket_id <= 0) {
|
||||
return ['success' => false, 'message' => __('售后申请提交失败。', 'aksrt')];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'ticket_id' => $ticket_id,
|
||||
'order' => $this->get_order_detail_view($user_id, $order_number),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function get_product_archive_view(array $args = []): array {
|
||||
$taxonomy = sanitize_key((string) ($args['taxonomy'] ?? ''));
|
||||
$term_id = (int) ($args['term_id'] ?? 0);
|
||||
$paged = max(1, (int) ($args['paged'] ?? 1));
|
||||
$per_page = 12;
|
||||
|
||||
if (!in_array($taxonomy, ['aksrt_product_cat', 'aksrt_product_tag'], true)) {
|
||||
return ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
}
|
||||
|
||||
$term = get_term($term_id, $taxonomy);
|
||||
if (is_wp_error($term) || !$term) {
|
||||
return ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
}
|
||||
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'aksrt_product',
|
||||
'posts_per_page' => $per_page,
|
||||
'paged' => $paged,
|
||||
'post_status' => 'publish',
|
||||
'tax_query' => [[
|
||||
'taxonomy' => $taxonomy,
|
||||
'field' => 'term_id',
|
||||
'terms' => $term_id,
|
||||
]],
|
||||
]);
|
||||
|
||||
$products = [];
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$products[] = $this->build_product_card_view(get_the_ID());
|
||||
}
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
return [
|
||||
'products' => $products,
|
||||
'term' => [
|
||||
'id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'description' => $term->description,
|
||||
'count' => $term->count,
|
||||
],
|
||||
'total' => (int) $query->found_posts,
|
||||
'max_pages' => (int) $query->max_num_pages,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_product_categories(): array {
|
||||
$terms = get_terms([
|
||||
'taxonomy' => 'aksrt_product_cat',
|
||||
'hide_empty' => true,
|
||||
'orderby' => 'count',
|
||||
'order' => 'DESC',
|
||||
]);
|
||||
|
||||
if (is_wp_error($terms)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map(static function ($t) {
|
||||
return [
|
||||
'id' => $t->term_id,
|
||||
'name' => $t->name,
|
||||
'count' => $t->count,
|
||||
'url' => get_term_link($t),
|
||||
];
|
||||
}, $terms);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Merchant / Store Mode (§6.10)
|
||||
* ============================================================ */
|
||||
|
||||
public function get_store_profile(int $author_id): array {
|
||||
if ($author_id <= 0) return [];
|
||||
|
||||
$user = get_userdata($author_id);
|
||||
if (!$user) return [];
|
||||
|
||||
$product_count = $this->count_author_products($author_id);
|
||||
$shop_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_shop_settings() : [];
|
||||
$author_show = !isset($shop_settings['general']['shop_author_show']) || !empty($shop_settings['general']['shop_author_show']);
|
||||
|
||||
if (!$author_show) return [];
|
||||
|
||||
$contact = $this->get_store_contact($author_id);
|
||||
|
||||
return [
|
||||
'author_id' => $author_id,
|
||||
'name' => (string) $user->display_name,
|
||||
'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($author_id) : get_avatar_url($author_id),
|
||||
'description' => (string) get_the_author_meta('description', $author_id),
|
||||
'url' => get_author_posts_url($author_id),
|
||||
'product_count' => $product_count,
|
||||
'contact' => $contact,
|
||||
'addresses' => $this->get_user_address_book($author_id),
|
||||
];
|
||||
}
|
||||
|
||||
public function get_store_products(int $author_id, int $page = 1, int $per_page = 12): array {
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'aksrt_product',
|
||||
'author' => $author_id,
|
||||
'posts_per_page' => $per_page,
|
||||
'paged' => $page,
|
||||
'post_status' => 'publish',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
]);
|
||||
|
||||
$products = [];
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$products[] = $this->build_product_card_view(get_the_ID());
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
return [
|
||||
'items' => $products,
|
||||
'total' => (int) $query->found_posts,
|
||||
'max_pages' => (int) $query->max_num_pages,
|
||||
];
|
||||
}
|
||||
|
||||
public function count_author_products(int $author_id): int {
|
||||
global $wpdb;
|
||||
return (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'aksrt_product' AND post_status = 'publish' AND post_author = %d",
|
||||
$author_id
|
||||
));
|
||||
}
|
||||
|
||||
private function get_store_contact(int $author_id): array {
|
||||
$raw = get_user_meta($author_id, 'aksrt_shop_contact', true);
|
||||
if (!is_array($raw)) return [];
|
||||
|
||||
$contacts = [];
|
||||
foreach ($raw as $c) {
|
||||
$name = sanitize_text_field((string) ($c['name'] ?? ''));
|
||||
if ($name === '') continue;
|
||||
$contacts[] = [
|
||||
'name' => $name,
|
||||
'icon' => sanitize_text_field((string) ($c['icon'] ?? '')),
|
||||
'link' => esc_url_raw((string) ($c['link'] ?? '')),
|
||||
'qr' => esc_url_raw((string) ($c['qr'] ?? '')),
|
||||
];
|
||||
}
|
||||
return $contacts;
|
||||
}
|
||||
|
||||
public function get_stats(): array {
|
||||
$product_count = 0;
|
||||
$post_type_obj = get_post_type_object('aksrt_product');
|
||||
if ($post_type_obj) {
|
||||
$counts = wp_count_posts('aksrt_product');
|
||||
$product_count = (int) ($counts->publish ?? 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'product_count' => $product_count,
|
||||
'order_count' => class_exists('AKSRT_Order_Repository') ? AKSRT_Order_Repository::get_instance()->get_stats()['total'] : 0,
|
||||
'revenue_total' => class_exists('AKSRT_Order_Repository') ? AKSRT_Order_Repository::get_instance()->get_stats()['revenue'] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function resolve_main_image(int $product_id): string {
|
||||
return $this->product_service()->resolve_main_image($product_id);
|
||||
}
|
||||
|
||||
public function build_product_card_view(int $product_id): array {
|
||||
return $this->product_service()->build_product_card_view($product_id);
|
||||
}
|
||||
|
||||
private function decorate_order_view(array $order, bool $with_detail = false): array {
|
||||
$meta = $order['meta_payload'] ?? [];
|
||||
$order_number = (string) ($order['order_number'] ?? '');
|
||||
|
||||
$order_items = $this->get_order_items($order_number);
|
||||
$has_structured_items = !empty($order_items);
|
||||
|
||||
$product = $has_structured_items
|
||||
? $this->build_product_card_view((int) ($order_items[0]['product_id'] ?? 0))
|
||||
: $this->build_product_card_view((int) ($order['product_id'] ?? 0));
|
||||
|
||||
$shipping_records = class_exists('AKSRT_Shipping') ? AKSRT_Shipping::get_instance()->get_by_order($order_number) : [];
|
||||
$after_sale_records = class_exists('AKSRT_After_Sale') ? AKSRT_After_Sale::get_instance()->get_list([
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'order_number' => $order_number,
|
||||
]) : ['items' => []];
|
||||
|
||||
$status = (string) ($order['status'] ?? AKSRT_Order::STATUS_PENDING);
|
||||
$subtotal = (float) ($meta['items'][0]['line_total'] ?? $order['amount']);
|
||||
$shipping_fee = (float) ($meta['shipping_fee'] ?? 0);
|
||||
$discount = (float) ($meta['discount'] ?? 0);
|
||||
$expires_at = (string) ($order['expires_at'] ?? '');
|
||||
$payment_value = (string) ($meta['payment_method']['value'] ?? $order['payment_method'] ?? 'balance');
|
||||
|
||||
$view = [
|
||||
'id' => (int) ($order['id'] ?? 0),
|
||||
'order_number' => (string) ($order['order_number'] ?? ''),
|
||||
'amount' => (float) ($order['amount'] ?? 0),
|
||||
'status' => $status,
|
||||
'status_label' => AKSRT_Order::get_valid_statuses()[$status] ?? $status,
|
||||
'product_name' => $product['title'] ?? __('商品订单', 'aksrt'),
|
||||
'product' => $product,
|
||||
'created_at' => (string) ($order['created_at'] ?? ''),
|
||||
'paid_at' => (string) ($order['paid_at'] ?? ''),
|
||||
'expires_at' => $expires_at,
|
||||
'currency_symbol' => '¥',
|
||||
'subtotal' => $subtotal,
|
||||
'shipping_fee' => $shipping_fee,
|
||||
'discount' => $discount,
|
||||
'address' => $meta['address'] ?? [],
|
||||
'payment_method' => $meta['payment_method'] ?? ['value' => $order['payment_method'] ?? '', 'label' => __('站内余额', 'aksrt')],
|
||||
'coupon' => $meta['coupon'] ?? [],
|
||||
'items' => $has_structured_items ? $order_items : ($meta['items'] ?? []),
|
||||
'shipping_method' => $meta['shipping_method'] ?? [],
|
||||
'customer_note' => $meta['customer_note'] ?? '',
|
||||
'pay_url' => '#',
|
||||
'actions' => [
|
||||
'can_pay' => in_array($status, [AKSRT_Order::STATUS_PENDING, AKSRT_Order::STATUS_EXPIRED], true) && $payment_value !== 'manual_review',
|
||||
'can_cancel' => in_array($status, [AKSRT_Order::STATUS_PENDING, AKSRT_Order::STATUS_CREATED, AKSRT_Order::STATUS_EXPIRED], true),
|
||||
'can_after_sale' => in_array($status, [AKSRT_Order::STATUS_PAID, AKSRT_Order::STATUS_DELIVERED, AKSRT_Order::STATUS_COMPLETED], true),
|
||||
'can_receipt' => $status === AKSRT_Order::STATUS_DELIVERED,
|
||||
],
|
||||
'time_left' => $this->get_order_countdown($expires_at, $status),
|
||||
];
|
||||
|
||||
if ($with_detail) {
|
||||
$view['shipping_records'] = $shipping_records;
|
||||
$view['after_sale_records'] = $after_sale_records['items'] ?? [];
|
||||
$view['timeline'] = $this->build_order_timeline($order, $shipping_records, $after_sale_records['items'] ?? []);
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function build_order_timeline(array $order, array $shipping_records, array $after_sale_records): array {
|
||||
$timeline = [];
|
||||
$date_fields = [
|
||||
'created_at' => __('订单创建', 'aksrt'),
|
||||
'paid_at' => __('完成支付', 'aksrt'),
|
||||
'delivered_at' => __('订单发货', 'aksrt'),
|
||||
'completed_at' => __('交易完成', 'aksrt'),
|
||||
'cancelled_at' => __('订单取消', 'aksrt'),
|
||||
'refunded_at' => __('完成退款', 'aksrt'),
|
||||
];
|
||||
|
||||
foreach ($date_fields as $key => $label) {
|
||||
if (!empty($order[$key])) {
|
||||
$timeline[] = [
|
||||
'time' => (string) $order[$key],
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($shipping_records as $record) {
|
||||
$timeline[] = [
|
||||
'time' => (string) ($record['created_at'] ?? ''),
|
||||
'label' => sprintf(__('物流更新:%s', 'aksrt'), $record['tracking_no'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($after_sale_records as $record) {
|
||||
$timeline[] = [
|
||||
'time' => (string) ($record['created_at'] ?? ''),
|
||||
'label' => sprintf(__('售后申请:%s', 'aksrt'), $record['reason'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
usort($timeline, static function ($a, $b) {
|
||||
return strcmp($a['time'] ?? '', $b['time'] ?? '');
|
||||
});
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
private function get_order_countdown(string $expires_at, string $status): int {
|
||||
if ($expires_at === '' || !in_array($status, [AKSRT_Order::STATUS_PENDING, AKSRT_Order::STATUS_EXPIRED], true)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$left = strtotime($expires_at) - time();
|
||||
return max(0, $left);
|
||||
}
|
||||
|
||||
public function get_limit_buy_config(int $product_id): array {
|
||||
return $this->product_service()->get_limit_buy_config($product_id);
|
||||
}
|
||||
|
||||
public function get_product_services(int $product_id): array {
|
||||
return $this->product_service()->get_product_services($product_id);
|
||||
}
|
||||
|
||||
public function get_product_params(int $product_id): array {
|
||||
return $this->product_service()->get_product_params($product_id);
|
||||
}
|
||||
|
||||
public function get_address_book(int $user_id): array {
|
||||
return $this->address_service()->get_address_book($user_id);
|
||||
}
|
||||
|
||||
public function add_address(int $user_id, array $data): array {
|
||||
return $this->address_service()->add_address($user_id, $data);
|
||||
}
|
||||
|
||||
public function update_address(int $user_id, int $address_id, array $data): array {
|
||||
return $this->address_service()->update_address($user_id, $address_id, $data);
|
||||
}
|
||||
|
||||
public function delete_address(int $user_id, int $address_id): array {
|
||||
return $this->address_service()->delete_address($user_id, $address_id);
|
||||
}
|
||||
|
||||
public function set_default_address(int $user_id, int $address_id): array {
|
||||
return $this->address_service()->set_default_address($user_id, $address_id);
|
||||
}
|
||||
|
||||
public function get_payment_method_options(): array {
|
||||
return $this->product_service()->get_payment_method_options();
|
||||
}
|
||||
|
||||
public function get_shipping_method_options(): array {
|
||||
return $this->product_service()->get_shipping_method_options();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Product Variants / SKU
|
||||
* ============================================================ */
|
||||
|
||||
public function get_variants(int $product_id): array {
|
||||
return $this->product_service()->get_variants($product_id);
|
||||
}
|
||||
|
||||
public function get_variant(int $variant_id): ?array {
|
||||
return $this->product_service()->get_variant($variant_id);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user