feat(ticket): replace the after-sale flow with a general ticket system
The old 售后 flow was an order-refund appendage: tickets were hard-bound to an order number, users could only file them from 「我的订单」against a paid order, and the admin action bottomed out at 「标记订单已退款」. It could not carry account questions or feature requests that have nothing to do with an order. Replace it with a general ticket system modelled on 魔方财务云's idcsmart_ticket: title + department + description are required, linking an order is optional (user's own paid orders only). Add departments, custom statuses, preset replies and internal admin notes as four configuration surfaces. Conversation history moves off the ticket row's JSON log into dedicated ticket_reply rows, which unlocks quoted replies. Front end gets a 工单中心 panel under 我的服务; back end moves from 支付中心 → 售后服务 to users.php → 工单管理. Per CLAUDE.md's rewrite-over-compatibility rule: drop the old table, build new ones, delete the old code. No dual paths, no data migration. The upstream plugin's upstream/downstream delivery and internal-ticket conversion are deliberately left out — both are multi-provider platform concerns with no meaning on a single WordPress site. Data: order_aftersale becomes six aksrt_ticket* tables (ticket, ticket_reply, ticket_status, ticket_type, ticket_prereply, ticket_note). SCHEMA_VERSION 2.6.0 -> 3.0.0. Status and department seeds are idempotent; the default status 「待接单」is flagged as non-editable. Domain: new src/Ticket/ (13 files, all under 460 lines). Attachments reuse UserController::rest_upload_comment_image with a ticket-specific permission callback, mirroring the existing /report/upload-image route rather than growing a second upload path. Notifications go through AKSRT\Messages\Message::send() and deep-link to the right surface; email/SMS stay out of scope since the theme's mail and SMS stacks are separate implementations. Refund gate opens up: marking an order refunded no longer requires an in-flight ticket, only that the order is paid. Also generalise a hardcoded special case in tools/audit-admin-contracts.php — schema domain inference now reads the parent directory instead of matching "PostList" by name, so new leaf schema files need no per-name exception. Verification: php tools/audit-admin-contracts.php exit 0, errors=0 node --test (widget-system + admin) 664 pass / 0 fail php -l over all new/changed files clean node --check over all JS clean npm run build + build:public clean Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
761c84d70f
commit
dbb7de41e9
81 files changed
+5812
-4179
No files matched your search
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
/**
|
||||
* 工单 REST — 管理侧(后台工单页 AJAX)。
|
||||
*
|
||||
* 命名空间 aksrt/v1/admin;全部路由要求 manage_options。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Ticket;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class TicketRestAdmin {
|
||||
|
||||
use TicketConfigRest;
|
||||
|
||||
public function register_routes(): void {
|
||||
$namespace = 'aksrt/v1/admin';
|
||||
$id = '(?P<id>[0-9]+)';
|
||||
|
||||
register_rest_route($namespace, '/tickets', [
|
||||
[
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'list_tickets'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => $this->list_args(),
|
||||
],
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'create_ticket'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'user_id' => ['required' => true, 'sanitize_callback' => 'absint'],
|
||||
'title' => ['required' => true, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'ticket_type_id' => ['required' => true, 'sanitize_callback' => 'absint'],
|
||||
'content' => ['required' => true, 'sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'order_number' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'attachment' => ['required' => false, 'type' => 'array'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/statistic', [
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'statistic'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/statuses', [
|
||||
[
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'list_statuses'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
],
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'create_status'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'name' => ['required' => true, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'color' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'is_closed' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/statuses/' . $id, [
|
||||
'methods' => \WP_REST_Server::EDITABLE,
|
||||
'callback' => [$this, 'update_status'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'name' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'color' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'is_closed' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/statuses/' . $id . '/delete', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'delete_status'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/types', [
|
||||
[
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'list_types'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
],
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'create_type'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'name' => ['required' => true, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'description' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'admin_ids' => ['required' => false, 'type' => 'array'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/types/' . $id, [
|
||||
'methods' => \WP_REST_Server::EDITABLE,
|
||||
'callback' => [$this, 'update_type'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'name' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'description' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'admin_ids' => ['required' => false, 'type' => 'array'],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/types/' . $id . '/delete', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'delete_type'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/prereplies', [
|
||||
[
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'list_prereplies'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
],
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'create_prereply'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => ['content' => ['required' => true, 'sanitize_callback' => 'sanitize_textarea_field']],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/prereplies/' . $id, [
|
||||
'methods' => \WP_REST_Server::EDITABLE,
|
||||
'callback' => [$this, 'update_prereply'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => ['content' => ['required' => false, 'sanitize_callback' => 'sanitize_textarea_field']],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/prereplies/' . $id . '/delete', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'delete_prereply'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/prereplies/order', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'reorder_prereplies'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => ['ids' => ['required' => true, 'type' => 'array']],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id, [
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'get_ticket'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/reply', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'reply'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'content' => ['required' => true, 'sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'attachment' => ['required' => false, 'type' => 'array'],
|
||||
'quote_reply_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/status', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'set_status'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => [
|
||||
'status' => ['required' => true, 'sanitize_callback' => 'sanitize_key'],
|
||||
'ticket_type_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
'admin_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/receive', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'receive'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/resolve', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'resolve'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/close', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'close'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/' . $id . '/notes', [
|
||||
[
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'list_notes'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
],
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'create_note'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => ['content' => ['required' => true, 'sanitize_callback' => 'sanitize_textarea_field']],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/notes/' . $id, [
|
||||
'methods' => \WP_REST_Server::EDITABLE,
|
||||
'callback' => [$this, 'update_note'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
'args' => ['content' => ['required' => false, 'sanitize_callback' => 'sanitize_textarea_field']],
|
||||
]);
|
||||
|
||||
register_rest_route($namespace, '/tickets/notes/' . $id . '/delete', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'delete_note'],
|
||||
'permission_callback' => [$this, 'require_admin'],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function list_args(): array {
|
||||
return [
|
||||
'keywords' => ['required' => false, 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'status' => ['required' => false, 'sanitize_callback' => [$this, 'sanitize_status_list']],
|
||||
'ticket_type_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
'user_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
'admin_id' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
'page' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
'per_page' => ['required' => false, 'sanitize_callback' => 'absint'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function sanitize_status_list($value): array {
|
||||
$values = is_array($value) ? $value : explode(',', (string) $value);
|
||||
$out = [];
|
||||
foreach ((array) $values as $item) {
|
||||
$key = sanitize_key((string) $item);
|
||||
if ($key !== '' && TicketStatusService::exists($key)) {
|
||||
$out[] = $key;
|
||||
}
|
||||
}
|
||||
return array_values(array_unique($out));
|
||||
}
|
||||
|
||||
public function require_admin() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return new \WP_Error('aksrt_ticket_forbidden', __('权限不足', 'aksrt'), ['status' => 403]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* 工单
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
public function list_tickets(\WP_REST_Request $request) {
|
||||
$page = max(1, (int) $request->get_param('page'));
|
||||
$per_page = max(1, min(100, (int) $request->get_param('per_page') ?: 20));
|
||||
$result = TicketRepository::get_instance()->paginate($this->read_filters($request, $per_page, $page), true);
|
||||
return rest_ensure_response([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'items' => $result['items'],
|
||||
'total' => $result['total'],
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => max(1, (int) ceil($result['total'] / $per_page)),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function statistic(\WP_REST_Request $request) {
|
||||
$filters = [
|
||||
'ticket_type_id' => absint($request->get_param('ticket_type_id')),
|
||||
];
|
||||
return rest_ensure_response([
|
||||
'success' => true,
|
||||
'data' => TicketRepository::get_instance()->statistic($filters, true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_ticket(\WP_REST_Request $request) {
|
||||
$repo = TicketRepository::get_instance();
|
||||
$ticket = $repo->find(absint($request->get_param('id')));
|
||||
if ($ticket === null) {
|
||||
return new \WP_Error('aksrt_ticket_missing', __('工单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
$ticket['replies'] = $repo->replies((int) $ticket['id']);
|
||||
$ticket['notes'] = TicketNoteService::for_ticket((int) $ticket['id']);
|
||||
$ticket['prereplies'] = TicketPrereplyService::public_list();
|
||||
return rest_ensure_response(['success' => true, 'data' => $ticket]);
|
||||
}
|
||||
|
||||
public function create_ticket(\WP_REST_Request $request) {
|
||||
$user_id = absint($request->get_param('user_id'));
|
||||
$title = trim((string) $request->get_param('title'));
|
||||
$content = trim((string) $request->get_param('content'));
|
||||
$type_id = absint($request->get_param('ticket_type_id'));
|
||||
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return new \WP_Error('aksrt_ticket_user', __('请选择工单所属用户', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
if ($title === '' || mb_strlen($title) > 200) {
|
||||
return new \WP_Error('aksrt_ticket_title', __('请填写有效的工单标题', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
if ($content === '' || mb_strlen($content) > 5000) {
|
||||
return new \WP_Error('aksrt_ticket_content', __('请填写有效的问题描述', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
if (!TicketTypeService::exists($type_id)) {
|
||||
return new \WP_Error('aksrt_ticket_type', __('请选择工单部门', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
|
||||
$attachment = TicketAttachment::sanitize($request->get_param('attachment'));
|
||||
$id = TicketRepository::get_instance()->create([
|
||||
'ticket_num' => TicketNumber::generate(),
|
||||
'user_id' => $user_id,
|
||||
'title' => $title,
|
||||
'ticket_type_id' => $type_id,
|
||||
'content' => $content,
|
||||
'order_number' => sanitize_text_field((string) $request->get_param('order_number')),
|
||||
'attachment' => TicketAttachment::encode($attachment),
|
||||
'status' => TicketStatusService::KEY_ADMIN_REPLIED,
|
||||
'post_admin_id' => get_current_user_id(),
|
||||
'admin_id' => get_current_user_id(),
|
||||
]);
|
||||
if ($id <= 0) {
|
||||
return new \WP_Error('aksrt_ticket_db', __('工单创建失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
$ticket = TicketRepository::get_instance()->find($id);
|
||||
if (is_array($ticket)) {
|
||||
TicketNotifier::user_replied($ticket);
|
||||
}
|
||||
return rest_ensure_response(['success' => true, 'data' => ['id' => $id]]);
|
||||
}
|
||||
|
||||
public function reply(\WP_REST_Request $request) {
|
||||
$repo = TicketRepository::get_instance();
|
||||
$id = absint($request->get_param('id'));
|
||||
$ticket = $repo->find($id);
|
||||
if ($ticket === null) {
|
||||
return new \WP_Error('aksrt_ticket_missing', __('工单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
if (!empty($ticket['is_closed'])) {
|
||||
return new \WP_Error('aksrt_ticket_closed', __('工单已关闭,无法回复', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
$content = trim((string) $request->get_param('content'));
|
||||
if ($content === '' || mb_strlen($content) > 3000) {
|
||||
return new \WP_Error('aksrt_ticket_reply_content', __('请填写有效的回复内容', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
$quote_id = absint($request->get_param('quote_reply_id'));
|
||||
if ($quote_id > 0) {
|
||||
$quoted = $repo->find_reply($quote_id);
|
||||
if ($quoted === null || (int) $quoted['ticket_id'] !== $id) {
|
||||
$quote_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$attachment = TicketAttachment::sanitize($request->get_param('attachment'));
|
||||
$reply_id = $repo->create_reply($id, 'admin', get_current_user_id(), $content, $attachment, $quote_id);
|
||||
if ($reply_id <= 0) {
|
||||
return new \WP_Error('aksrt_ticket_db', __('回复提交失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
$repo->touch_reply($id, 'admin', get_current_user_id());
|
||||
$repo->apply_reply_status($id, $ticket, 'admin');
|
||||
TicketNotifier::user_replied($repo->find($id) ?? $ticket);
|
||||
return rest_ensure_response(['success' => true, 'data' => ['reply_id' => $reply_id]]);
|
||||
}
|
||||
|
||||
public function set_status(\WP_REST_Request $request) {
|
||||
$repo = TicketRepository::get_instance();
|
||||
$id = absint($request->get_param('id'));
|
||||
$ticket = $repo->find($id);
|
||||
if ($ticket === null) {
|
||||
return new \WP_Error('aksrt_ticket_missing', __('工单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
$to = sanitize_key((string) $request->get_param('status'));
|
||||
if (!TicketStatusService::can_transition((string) $ticket['status'], $to)) {
|
||||
return new \WP_Error('aksrt_ticket_state', __('工单状态不允许该变更', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
|
||||
$fields = ['status' => $to];
|
||||
$type_id = absint($request->get_param('ticket_type_id'));
|
||||
if ($type_id > 0 && TicketTypeService::exists($type_id)) {
|
||||
$fields['ticket_type_id'] = $type_id;
|
||||
}
|
||||
$admin_id = absint($request->get_param('admin_id'));
|
||||
if ($admin_id > 0 && get_userdata($admin_id)) {
|
||||
$fields['admin_id'] = $admin_id;
|
||||
}
|
||||
if (!$repo->update_fields($id, $fields)) {
|
||||
return new \WP_Error('aksrt_ticket_db', __('工单状态保存失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
$ticket = array_merge($ticket, $fields);
|
||||
if ($to === TicketStatusService::KEY_CLOSED) {
|
||||
TicketNotifier::closed($ticket, 'admin');
|
||||
}
|
||||
return rest_ensure_response(['success' => true, 'data' => $repo->find($id)]);
|
||||
}
|
||||
|
||||
public function receive(\WP_REST_Request $request) {
|
||||
return $this->simple_action($request, 'receive');
|
||||
}
|
||||
|
||||
public function resolve(\WP_REST_Request $request) {
|
||||
return $this->simple_action($request, 'resolve');
|
||||
}
|
||||
|
||||
public function close(\WP_REST_Request $request) {
|
||||
return $this->simple_action($request, 'close');
|
||||
}
|
||||
|
||||
/** 接收 / 已解决 / 关闭:三个动作都只是状态迁移,共用同一外壳。 */
|
||||
private function simple_action(\WP_REST_Request $request, string $action) {
|
||||
$repo = TicketRepository::get_instance();
|
||||
$id = absint($request->get_param('id'));
|
||||
$ticket = $repo->find($id);
|
||||
if ($ticket === null) {
|
||||
return new \WP_Error('aksrt_ticket_missing', __('工单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
$ok = match ($action) {
|
||||
'receive' => $repo->receive($id, $ticket),
|
||||
'resolve' => $repo->resolve($id, $ticket),
|
||||
default => $repo->close($id, $ticket, 'admin'),
|
||||
};
|
||||
if (!$ok) {
|
||||
return new \WP_Error('aksrt_ticket_state', __('工单状态不允许该操作', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
return rest_ensure_response(['success' => true, 'data' => $repo->find($id)]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* 内部
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function read_filters(\WP_REST_Request $request, int $per_page, int $page): array {
|
||||
return [
|
||||
'keywords' => $request->get_param('keywords'),
|
||||
'status' => $request->get_param('status'),
|
||||
'ticket_type_id' => $request->get_param('ticket_type_id'),
|
||||
'user_id' => $request->get_param('user_id'),
|
||||
'admin_id' => $request->get_param('admin_id'),
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page - 1) * $per_page,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user