Files
wordpress-theme-aksrt/inc/classes/admin/class-aksrt-admin-shop-controller-product-operations.php
T
kate522 7c56afa376 fix(admin): repair email rollback and product taxonomy APIs
Call the existing Email_Rollback::list_pending method instead of the missing
list_recent_changes method that caused /email-rollback/users to fatal. Add
the product taxonomy route expected by ProductEditorDrawer and return
category/tag options for the editor.
2026-07-11 12:20:33 +08:00

413 lines
19 KiB
PHP

<?php
/**
* AKSRT admin shop product operations.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
trait AKSRT_Admin_Shop_Controller_Product_Operations {
public function get_overview(): WP_REST_Response {
$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);
}
$order_stats = ['total' => 0, 'pending' => 0, 'paid' => 0, 'revenue' => 0];
if (class_exists('AKSRT_Order_Repository')) {
$order_stats = AKSRT_Order_Repository::get_instance()->get_stats();
}
$revenue_stats = ['total' => 0, 'pending' => 0, 'settled' => 0];
if (class_exists('AKSRT_Revenue')) {
$revenue_stats = AKSRT_Revenue::get_instance()->get_stats();
}
$gateway_summary = [];
if (class_exists('AKSRT_Payment_Gateway')) {
$gateway_summary = AKSRT_Payment_Gateway::get_instance()->get_status_summary();
}
return $this->success([
'data' => [
'enabled' => true,
'product_count' => $product_count,
'order_count' => $order_stats['total'],
'order_pending' => $order_stats['pending'] ?? 0,
'revenue_total' => $order_stats['revenue'] ?? 0,
'revenue_pending' => $revenue_stats['pending'] ?? 0,
'gateways' => $gateway_summary,
],
]);
}
public function get_product_taxonomy(): WP_REST_Response {
$format_terms = static function (string $taxonomy): array {
if (!taxonomy_exists($taxonomy)) {
return [];
}
$terms = get_terms([
'taxonomy' => $taxonomy,
'hide_empty' => false,
]);
if (is_wp_error($terms)) {
return [];
}
return array_map(static function (WP_Term $term): array {
return [
'id' => $term->term_id,
'name' => $term->name,
];
}, $terms);
};
return $this->success([
'data' => [
'categories' => $format_terms('aksrt_product_cat'),
'tags' => $format_terms('aksrt_product_tag'),
],
]);
}
/**
* GET /shop/products — 商品列表,支持 search、status、page、per_page
*/
public function get_products(WP_REST_Request $request): WP_REST_Response {
$page = max(1, (int) $request->get_param('page'));
$per_page = max(1, min(50, (int) $request->get_param('per_page'))) ?: 20;
$search = sanitize_text_field((string) $request->get_param('search'));
$status = sanitize_key((string) $request->get_param('status'));
$query_args = [
'post_type' => 'aksrt_product',
'posts_per_page' => $per_page,
'paged' => $page,
'orderby' => 'date',
'order' => 'DESC',
];
if ($status !== '') {
$query_args['post_status'] = $status;
} else {
$query_args['post_status'] = ['publish', 'draft', 'pending'];
}
if ($search !== '') {
$query_args['s'] = $search;
}
$query = new WP_Query($query_args);
$products = [];
while ($query->have_posts()) {
$query->the_post();
$id = get_the_ID();
$products[] = [
'id' => $id,
'title' => get_the_title(),
'price' => (float) get_post_meta($id, '_aksrt_price', true),
'image' => get_the_post_thumbnail_url($id, 'medium') ?: '',
'stock' => (int) get_post_meta($id, '_aksrt_stock', true),
'sales' => (int) get_post_meta($id, '_aksrt_sales', true),
'status' => get_post_status(),
'created' => get_the_date('Y-m-d H:i'),
];
}
wp_reset_postdata();
return $this->success([
'data' => [
'items' => $products,
'total' => (int) $query->found_posts,
'total_pages' => (int) $query->max_num_pages,
'page' => $page,
],
]);
}
/**
* GET /shop/products/{id} — 单个商品详情
*/
public function get_product(WP_REST_Request $request): WP_REST_Response {
$id = (int) $request->get_param('id');
$post = get_post($id);
if (!$post || $post->post_type !== 'aksrt_product') {
return $this->error('商品不存在', 404);
}
return $this->success([
'data' => [
'id' => $post->ID,
'title' => $post->post_title,
'content' => $post->post_content,
'excerpt' => $post->post_excerpt,
'price' => (float) get_post_meta($post->ID, '_aksrt_price', true),
'image' => (string) (get_post_meta($post->ID, '_aksrt_product_image', true) ?: get_the_post_thumbnail_url($post->ID, 'medium') ?: ''),
'stock' => (int) get_post_meta($post->ID, '_aksrt_stock', true),
'sales' => (int) get_post_meta($post->ID, '_aksrt_sales', true),
'status' => $post->post_status,
'created' => get_the_date('Y-m-d H:i', $post),
'modified' => get_the_modified_date('Y-m-d H:i', $post),
'categories' => wp_get_post_terms($post->ID, 'aksrt_product_cat', ['fields' => 'names']) ?: [],
'tags' => wp_get_post_terms($post->ID, 'aksrt_product_tag', ['fields' => 'names']) ?: [],
'gallery' => get_post_meta($post->ID, '_aksrt_product_gallery', true) ?: [],
'shipping_mode' => (string) get_post_meta($post->ID, '_aksrt_shipping_mode', true),
'shipping_fee' => (float) get_post_meta($post->ID, '_aksrt_shipping_fee', true),
'after_sale_policy' => (string) get_post_meta($post->ID, '_aksrt_after_sale_policy', true),
'support_refund' => get_post_meta($post->ID, '_aksrt_support_refund', true) !== '0',
],
]);
}
/**
* POST /shop/products — 创建商品
*/
public function create_product(WP_REST_Request $request): WP_REST_Response {
$params = $this->get_json_params($request);
$title = sanitize_text_field((string) ($params['title'] ?? ''));
$content = wp_kses_post((string) ($params['content'] ?? ''));
$excerpt = sanitize_textarea_field((string) ($params['excerpt'] ?? ''));
$price = (float) ($params['price'] ?? 0);
$stock = (int) ($params['stock'] ?? 0);
$status = sanitize_key((string) ($params['status'] ?? 'publish'));
$image = esc_url_raw((string) ($params['image'] ?? ''));
$gallery = is_array($params['gallery'] ?? null) ? array_values(array_filter(array_map('esc_url_raw', $params['gallery']))) : [];
$shipping_mode = sanitize_key((string) ($params['shipping_mode'] ?? 'express'));
$shipping_fee = max(0, (float) ($params['shipping_fee'] ?? 0));
$after_sale_policy = sanitize_textarea_field((string) ($params['after_sale_policy'] ?? ''));
$support_refund = !isset($params['support_refund']) || !empty($params['support_refund']);
$author_id = max(0, (int) ($params['author_id'] ?? 0));
if ($title === '') {
return $this->error('商品名称不能为空');
}
$valid_statuses = ['publish', 'draft', 'pending'];
if (!in_array($status, $valid_statuses, true)) {
$status = 'draft';
}
$post_id = wp_insert_post([
'post_type' => 'aksrt_product',
'post_title' => $title,
'post_content' => $content,
'post_excerpt' => $excerpt,
'post_status' => $status,
'post_author' => $author_id > 0 ? $author_id : get_current_user_id(),
'meta_input' => [
'_aksrt_price' => max(0, $price),
'_aksrt_stock' => max(0, $stock),
'_aksrt_sales' => 0,
'_aksrt_score' => 0,
'_aksrt_views' => 0,
'_aksrt_favorite_count' => 0,
'_aksrt_product_gallery' => $gallery,
'_aksrt_shipping_mode' => $shipping_mode,
'_aksrt_shipping_fee' => $shipping_fee,
'_aksrt_after_sale_policy' => $after_sale_policy,
'_aksrt_support_refund' => $support_refund ? '1' : '0',
'_aksrt_product_video' => is_array($params['product_video'] ?? null) ? array_values(array_filter(array_map('esc_url_raw', $params['product_video']))) : [],
'_aksrt_product_params' => sanitize_textarea_field((string) ($params['product_params'] ?? '')),
'_aksrt_pay_modo' => sanitize_key((string) ($params['pay_modo'] ?? '0')),
'_aksrt_start_price' => max(0, (float) ($params['start_price'] ?? 0)),
'_aksrt_product_rebate' => sanitize_text_field((string) ($params['product_rebate'] ?? '')),
'_aksrt_limit_buy' => max(-1, min(9999, (int) ($params['limit_buy'] ?? -1))),
'_aksrt_limit_buy_note' => sanitize_text_field((string) ($params['limit_buy_note'] ?? '')),
'_aksrt_stock_type' => sanitize_key((string) ($params['stock_type'] ?? 'all')),
'_aksrt_user_required' => sanitize_textarea_field((string) ($params['user_required'] ?? '')),
'_aksrt_shipping_type' => sanitize_key((string) ($params['shipping_type'] ?? $shipping_mode)),
'_aksrt_shipping_delivery_desc' => sanitize_text_field((string) ($params['shipping_delivery_desc'] ?? '')),
'_aksrt_shipping_fee_type' => sanitize_key((string) ($params['shipping_fee_type'] ?? 'free')),
'_aksrt_shipping_fee_free_amount'=> max(0, (float) ($params['shipping_fee_free_amount'] ?? 0)),
'_aksrt_after_sale_matrix' => sanitize_textarea_field((string) ($params['after_sale_matrix'] ?? '')),
'_aksrt_service' => sanitize_textarea_field((string) ($params['service'] ?? '')),
'_aksrt_single_tab' => sanitize_textarea_field((string) ($params['single_tab'] ?? '')),
'_aksrt_content_layout' => sanitize_key((string) ($params['content_layout'] ?? '')),
'_aksrt_content_show_bg' => sanitize_text_field((string) ($params['content_show_bg'] ?? '')),
'_aksrt_delivery_note' => sanitize_text_field((string) ($params['delivery_note'] ?? '')),
'_aksrt_marketing_headline' => sanitize_text_field((string) ($params['marketing_headline'] ?? '')),
'_aksrt_shipping_tip' => sanitize_text_field((string) ($params['shipping_tip'] ?? '')),
'_aksrt_service_tip' => sanitize_text_field((string) ($params['service_tip'] ?? '')),
],
], true);
if (is_wp_error($post_id)) {
return $this->error($post_id->get_error_message());
}
if (!empty($params['categories']) && is_array($params['categories'])) {
wp_set_object_terms($post_id, $params['categories'], 'aksrt_product_cat');
}
if (!empty($params['tags']) && is_array($params['tags'])) {
wp_set_object_terms($post_id, $params['tags'], 'aksrt_product_tag');
}
if ($image !== '') {
update_post_meta($post_id, '_aksrt_product_image', $image);
}
return $this->success([
'message' => __('商品已创建', 'aksrt'),
'data' => ['id' => $post_id],
], 201);
}
/**
* POST /shop/products/{id} — 更新商品(含上下架)
*/
public function update_product(WP_REST_Request $request): WP_REST_Response {
$id = (int) $request->get_param('id');
$post = get_post($id);
if (!$post || $post->post_type !== 'aksrt_product') {
return $this->error('商品不存在', 404);
}
$params = $this->get_json_params($request);
$update = ['ID' => $id];
if (isset($params['title'])) {
$update['post_title'] = sanitize_text_field($params['title']);
}
if (isset($params['content'])) {
$update['post_content'] = wp_kses_post($params['content']);
}
if (isset($params['excerpt'])) {
$update['post_excerpt'] = sanitize_textarea_field($params['excerpt']);
}
if (isset($params['status'])) {
$new_status = sanitize_key($params['status']);
$valid_statuses = ['publish', 'draft', 'pending', 'trash'];
if (in_array($new_status, $valid_statuses, true)) {
$update['post_status'] = $new_status;
}
}
$meta = [];
if (isset($params['price'])) {
$meta['_aksrt_price'] = max(0, (float) $params['price']);
}
if (isset($params['stock'])) {
$meta['_aksrt_stock'] = max(0, (int) $params['stock']);
}
if (isset($params['gallery']) && is_array($params['gallery'])) {
$meta['_aksrt_product_gallery'] = array_values(array_filter(array_map('esc_url_raw', $params['gallery'])));
}
if (isset($params['shipping_mode'])) {
$meta['_aksrt_shipping_mode'] = sanitize_key((string) $params['shipping_mode']);
}
if (isset($params['shipping_fee'])) {
$meta['_aksrt_shipping_fee'] = max(0, (float) $params['shipping_fee']);
}
if (isset($params['after_sale_policy'])) {
$meta['_aksrt_after_sale_policy'] = sanitize_textarea_field((string) $params['after_sale_policy']);
}
if (isset($params['support_refund'])) {
$meta['_aksrt_support_refund'] = !empty($params['support_refund']) ? '1' : '0';
}
if (isset($params['product_video']) && is_array($params['product_video'])) {
$meta['_aksrt_product_video'] = array_values(array_filter(array_map('esc_url_raw', $params['product_video'])));
}
if (isset($params['product_params'])) {
$meta['_aksrt_product_params'] = sanitize_textarea_field((string) $params['product_params']);
}
if (isset($params['pay_modo'])) {
$meta['_aksrt_pay_modo'] = sanitize_key((string) $params['pay_modo']);
}
if (isset($params['start_price'])) {
$meta['_aksrt_start_price'] = max(0, (float) $params['start_price']);
}
if (isset($params['product_rebate'])) {
$meta['_aksrt_product_rebate'] = sanitize_text_field((string) $params['product_rebate']);
}
if (isset($params['limit_buy'])) {
$meta['_aksrt_limit_buy'] = max(-1, min(9999, (int) $params['limit_buy']));
}
if (isset($params['limit_buy_note'])) {
$meta['_aksrt_limit_buy_note'] = sanitize_text_field((string) $params['limit_buy_note']);
}
if (isset($params['stock_type'])) {
$meta['_aksrt_stock_type'] = sanitize_key((string) $params['stock_type']);
}
if (isset($params['stock_opts']) && is_array($params['stock_opts'])) {
$meta['_aksrt_stock_opts'] = wp_json_encode($params['stock_opts']);
}
if (isset($params['user_required'])) {
$meta['_aksrt_user_required'] = sanitize_textarea_field((string) $params['user_required']);
}
if (isset($params['shipping_type'])) {
$meta['_aksrt_shipping_type'] = sanitize_key((string) $params['shipping_type']);
}
if (isset($params['shipping_delivery_desc'])) {
$meta['_aksrt_shipping_delivery_desc'] = sanitize_text_field((string) $params['shipping_delivery_desc']);
}
if (isset($params['auto_delivery']) && is_array($params['auto_delivery'])) {
$meta['_aksrt_auto_delivery'] = wp_json_encode($params['auto_delivery']);
}
if (isset($params['shipping_fee_type'])) {
$meta['_aksrt_shipping_fee_type'] = sanitize_key((string) $params['shipping_fee_type']);
}
if (isset($params['shipping_fee_free_amount'])) {
$meta['_aksrt_shipping_fee_free_amount'] = max(0, (float) $params['shipping_fee_free_amount']);
}
if (isset($params['after_sale_matrix'])) {
$meta['_aksrt_after_sale_matrix'] = sanitize_textarea_field((string) $params['after_sale_matrix']);
}
if (isset($params['service'])) {
$meta['_aksrt_service'] = sanitize_textarea_field((string) $params['service']);
}
if (isset($params['single_tab'])) {
$meta['_aksrt_single_tab'] = sanitize_textarea_field((string) $params['single_tab']);
}
if (isset($params['content_layout'])) {
$meta['_aksrt_content_layout'] = sanitize_key((string) $params['content_layout']);
}
if (isset($params['content_show_bg'])) {
$meta['_aksrt_content_show_bg'] = sanitize_text_field((string) $params['content_show_bg']);
}
if (isset($params['delivery_note'])) {
$meta['_aksrt_delivery_note'] = sanitize_text_field((string) $params['delivery_note']);
}
if (isset($params['marketing_headline'])) {
$meta['_aksrt_marketing_headline'] = sanitize_text_field((string) $params['marketing_headline']);
}
if (isset($params['shipping_tip'])) {
$meta['_aksrt_shipping_tip'] = sanitize_text_field((string) $params['shipping_tip']);
}
if (isset($params['service_tip'])) {
$meta['_aksrt_service_tip'] = sanitize_text_field((string) $params['service_tip']);
}
if (!empty($meta)) {
$update['meta_input'] = $meta;
}
$result = wp_update_post($update, true);
if (is_wp_error($result)) {
return $this->error($result->get_error_message());
}
if (isset($params['categories']) && is_array($params['categories'])) {
wp_set_object_terms($id, $params['categories'], 'aksrt_product_cat');
}
if (isset($params['tags']) && is_array($params['tags'])) {
wp_set_object_terms($id, $params['tags'], 'aksrt_product_tag');
}
if (isset($params['image'])) {
update_post_meta($id, '_aksrt_product_image', esc_url_raw((string) $params['image']));
}
return $this->success([
'message' => __('商品已更新', 'aksrt'),
]);
}
}