Files
wordpress-theme-aksrt/inc/classes/admin/class-aksrt-admin-shop-controller-fulfillment-operations.php
T

355 lines
13 KiB
PHP

<?php
/**
* AKSRT admin shop fulfillment operations.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
trait AKSRT_Admin_Shop_Controller_Fulfillment_Operations {
/**
* GET /shop/after-sale — 售后工单列表
*/
public function get_after_sale_list(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_After_Sale')) {
return $this->success(['data' => ['items' => [], 'total' => 0, 'page' => 1, 'max_pages' => 1]]);
}
$page = max(1, (int) $request->get_param('page'));
$per_page = max(1, min(100, (int) $request->get_param('per_page'))) ?: 20;
$status = sanitize_key((string) $request->get_param('status'));
$stage = sanitize_key((string) $request->get_param('stage'));
$order = sanitize_text_field((string) $request->get_param('order_number'));
$type = sanitize_key((string) $request->get_param('type'));
$date_from = sanitize_text_field((string) $request->get_param('date_from'));
$date_to = sanitize_text_field((string) $request->get_param('date_to'));
$result = AKSRT_After_Sale::get_instance()->get_list([
'limit' => $per_page,
'offset' => ($page - 1) * $per_page,
'status' => $status,
'stage' => $stage,
'order_number' => $order,
'type' => $type,
'date_from' => $date_from,
'date_to' => $date_to,
'include_order_context' => true,
]);
$stats = AKSRT_After_Sale::get_instance()->get_stats();
return $this->success(['data' => array_merge($result, [
'stats' => $stats,
'filters' => [
'status' => $status,
'stage' => $stage,
'order_number' => $order,
'type' => $type,
'date_from' => $date_from,
'date_to' => $date_to,
],
'options' => [
'status' => AKSRT_After_Sale::get_status_options(),
'stage' => AKSRT_After_Sale::get_stage_options(),
'type' => AKSRT_After_Sale::get_type_options(),
],
])]);
}
/**
* POST /shop/after-sale — 创建售后工单
*/
public function create_after_sale(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_After_Sale')) {
return $this->error('售后系统未启用', 500);
}
$params = $this->get_json_params($request);
$order_number = sanitize_text_field((string) ($params['order_number'] ?? ''));
$reason = sanitize_textarea_field((string) ($params['reason'] ?? ''));
if ($order_number === '' || $reason === '') {
return $this->error('订单号和原因不能为空');
}
$order = class_exists('AKSRT_Order_Repository')
? AKSRT_Order_Repository::get_instance()->get($order_number)
: null;
if (!$order) {
return $this->error(__('订单不存在', 'aksrt'), 404);
}
$params['user_id'] = (int) ($params['user_id'] ?? ($order['user_id'] ?? 0));
if ($params['user_id'] <= 0) {
return $this->error(__('订单未绑定有效用户', 'aksrt'));
}
$id = AKSRT_After_Sale::get_instance()->create($params);
if ($id > 0) {
return $this->success([
'message' => __('售后工单已创建', 'aksrt'),
'data' => ['id' => $id],
], 201);
}
return $this->error(__('创建失败', 'aksrt'));
}
/**
* POST /shop/after-sale/{id}/update-status — 更新售后工单状态
*/
public function update_after_sale_status(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_After_Sale')) {
return $this->error('售后系统未启用', 500);
}
$id = (int) $request->get_param('id');
$params = $this->get_json_params($request);
$new_status = sanitize_key((string) ($params['status'] ?? ''));
$note = sanitize_textarea_field((string) ($params['operator_note'] ?? ''));
if ($id <= 0 || $new_status === '') {
return $this->error('参数无效');
}
$success = AKSRT_After_Sale::get_instance()->update_status(
$id,
$new_status,
get_current_user_id(),
$note
);
if ($success) {
return $this->success(['message' => __('状态已更新', 'aksrt')]);
}
return $this->error(__('更新失败', 'aksrt'));
}
/**
* GET /shop/shipping — 发货记录列表
*/
public function get_shipping_list(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shipping')) {
return $this->success(['data' => ['items' => [], 'total' => 0, 'page' => 1, 'max_pages' => 1]]);
}
$page = max(1, (int) $request->get_param('page'));
$per_page = max(1, min(100, (int) $request->get_param('per_page'))) ?: 20;
$status = sanitize_key((string) $request->get_param('status'));
$order = sanitize_text_field((string) $request->get_param('order_number'));
$date_from = sanitize_text_field((string) $request->get_param('date_from'));
$date_to = sanitize_text_field((string) $request->get_param('date_to'));
$tracking_no = sanitize_text_field((string) $request->get_param('tracking_no'));
$result = AKSRT_Shipping::get_instance()->get_list([
'limit' => $per_page,
'offset' => ($page - 1) * $per_page,
'status' => $status,
'order_number' => $order,
'date_from' => $date_from,
'date_to' => $date_to,
'tracking_no' => $tracking_no,
'include_order_context' => true,
]);
$stats = AKSRT_Shipping::get_instance()->get_stats();
return $this->success(['data' => array_merge($result, [
'stats' => $stats,
'filters' => [
'status' => $status,
'order_number' => $order,
'date_from' => $date_from,
'date_to' => $date_to,
'tracking_no' => $tracking_no,
],
'options' => [
'status' => AKSRT_Shipping::get_status_options(),
'carrier' => AKSRT_Shipping::get_carrier_options(),
],
])]);
}
/**
* POST /shop/shipping — 创建发货记录
*/
public function create_shipping(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shipping')) {
return $this->error('物流系统未启用', 500);
}
$params = $this->get_json_params($request);
$order_number = sanitize_text_field((string) ($params['order_number'] ?? ''));
$carrier = sanitize_text_field((string) ($params['carrier'] ?? ''));
$tracking_no = sanitize_text_field((string) ($params['tracking_no'] ?? ''));
if ($order_number === '' || $carrier === '' || $tracking_no === '') {
return $this->error('订单号、快递公司和物流单号不能为空');
}
$order = class_exists('AKSRT_Order_Repository')
? AKSRT_Order_Repository::get_instance()->get($order_number)
: null;
if (!$order) {
return $this->error(__('订单不存在', 'aksrt'), 404);
}
$params['operator_id'] = get_current_user_id();
$id = AKSRT_Shipping::get_instance()->create($params);
if ($id > 0) {
return $this->success([
'message' => __('发货记录已创建', 'aksrt'),
'data' => ['id' => $id],
], 201);
}
return $this->error(__('创建失败', 'aksrt'));
}
/**
* POST /shop/shipping/{id}/update-status — 更新发货状态
*/
public function update_shipping_status(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shipping')) {
return $this->error('物流系统未启用', 500);
}
$id = (int) $request->get_param('id');
$params = $this->get_json_params($request);
$new_status = sanitize_key((string) ($params['status'] ?? ''));
$note = sanitize_textarea_field((string) ($params['note'] ?? ''));
if ($id <= 0 || $new_status === '') {
return $this->error('参数无效');
}
$success = AKSRT_Shipping::get_instance()->update_status($id, $new_status, $note, get_current_user_id());
if ($success) {
return $this->success(['message' => __('状态已更新', 'aksrt')]);
}
return $this->error(__('更新失败', 'aksrt'));
}
public function get_after_sale_detail(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_After_Sale')) {
return $this->error('售后系统未启用', 500);
}
$id = (int) $request->get_param('id');
if ($id <= 0) {
return $this->error('参数无效');
}
$record = AKSRT_After_Sale::get_instance()->get_with_order_context($id);
if (!$record) {
return $this->error(__('售后工单不存在', 'aksrt'), 404);
}
return $this->success([
'data' => [
'record' => $record,
'timeline' => $this->build_after_sale_timeline($record),
],
]);
}
public function get_shipping_detail(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shipping')) {
return $this->error('物流系统未启用', 500);
}
$id = (int) $request->get_param('id');
if ($id <= 0) {
return $this->error('参数无效');
}
$record = AKSRT_Shipping::get_instance()->get_with_order_context($id);
if (!$record) {
return $this->error(__('发货记录不存在', 'aksrt'), 404);
}
return $this->success([
'data' => [
'record' => $record,
'timeline' => $this->build_shipping_timeline($record),
],
]);
}
public function batch_update_shipping_status(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shipping')) {
return $this->error('物流系统未启用', 500);
}
$params = $this->get_json_params($request);
$ids = is_array($params['ids'] ?? null) ? $params['ids'] : [];
$status = sanitize_key((string) ($params['status'] ?? ''));
$note = sanitize_textarea_field((string) ($params['note'] ?? ''));
if (empty($ids) || $status === '') {
return $this->error('参数无效');
}
$result = AKSRT_Shipping::get_instance()->batch_update_status($ids, $status, $note, get_current_user_id());
return $this->success([
'message' => __('批量状态更新已执行', 'aksrt'),
'data' => $result,
]);
}
private function build_shipping_timeline(array $record): array {
$timeline = [];
foreach ([
'created_at' => __('创建发货记录', 'aksrt'),
'updated_at' => __('更新物流状态', 'aksrt'),
'delivered_at' => __('确认签收', 'aksrt'),
] as $field => $label) {
if (!empty($record[$field])) {
$timeline[] = [
'time' => (string) $record[$field],
'label' => $label,
];
}
}
usort($timeline, static function ($a, $b) {
return strcmp((string) ($a['time'] ?? ''), (string) ($b['time'] ?? ''));
});
return $timeline;
}
private function build_after_sale_timeline(array $record): array {
$timeline = [];
foreach ([
'created_at' => __('提交售后申请', 'aksrt'),
'updated_at' => __('更新售后状态', 'aksrt'),
'resolved_at' => __('售后处理结束', 'aksrt'),
] as $field => $label) {
if (!empty($record[$field])) {
$timeline[] = [
'time' => (string) $record[$field],
'label' => $label,
];
}
}
usort($timeline, static function ($a, $b) {
return strcmp((string) ($a['time'] ?? ''), (string) ($b['time'] ?? ''));
});
return $timeline;
}
}