$args * @return array{0:string,1:list} */ private function ticket_where(array $args, bool $is_admin): array { global $wpdb; $where = []; $params = []; if (!$is_admin) { $where[] = 'user_id = %d'; $params[] = (int) ($args['user_id'] ?? 0); } else { $user_id = (int) ($args['user_id'] ?? 0); if ($user_id > 0) { $where[] = 'user_id = %d'; $params[] = $user_id; } } $statuses = []; foreach ((array) ($args['status'] ?? []) as $status) { $status = sanitize_key((string) $status); if ($status !== '') { $statuses[] = $status; } } if ($statuses !== []) { $where[] = 'status IN (' . implode(', ', array_fill(0, count($statuses), '%s')) . ')'; $params = array_merge($params, $statuses); } elseif ($is_admin) { $closed = TicketStatusService::is_closed(TicketStatusService::KEY_CLOSED); if ($closed) { $where[] = 'status <> %s'; $params[] = TicketStatusService::KEY_CLOSED; } } $type_id = (int) ($args['ticket_type_id'] ?? 0); if ($type_id > 0) { $where[] = 'ticket_type_id = %d'; $params[] = $type_id; } $keywords = sanitize_text_field((string) ($args['keywords'] ?? '')); if ($keywords !== '') { $like = '%' . $wpdb->esc_like($keywords) . '%'; $where[] = '(ticket_num LIKE %s OR title LIKE %s)'; $params[] = $like; $params[] = $like; } foreach (['date_from' => '>=', 'date_to' => '<='] as $key => $operator) { $date = sanitize_text_field((string) ($args[$key] ?? '')); if ($date !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) === 1) { $where[] = 'created_at ' . $operator . ' %s'; $params[] = $operator === '>=' ? $date . ' 00:00:00' : $date . ' 23:59:59'; } } unset($wpdb); return [ $where === [] ? '1 = 1' : implode(' AND ', $where), $params, ]; } /** * @param array $args * @return list> */ private function ticket_rows(array $args, bool $is_admin): array { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('ticket'); if ($table === '') { return []; } [$where, $params] = $this->ticket_where($args, $is_admin); $limit = max(1, min(100, (int) ($args['limit'] ?? 20))); $offset = max(0, (int) ($args['offset'] ?? 0)); $sql = "SELECT * FROM {$table} WHERE {$where} ORDER BY last_reply_time DESC, id DESC LIMIT %d OFFSET %d"; $params[] = $limit; $params[] = $offset; $rows = $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A); // phpcs:ignore WordPress.DB.PreparedSQL return is_array($rows) ? $rows : []; } /** * @param array $args */ private function ticket_count(array $args, bool $is_admin): int { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('ticket'); if ($table === '') { return 0; } [$where, $params] = $this->ticket_where($args, $is_admin); $sql = "SELECT COUNT(*) FROM {$table} WHERE {$where}"; $count = $params === [] ? $wpdb->get_var($sql) // phpcs:ignore WordPress.DB.DirectDatabaseQuery : $wpdb->get_var($wpdb->prepare($sql, $params)); // phpcs:ignore WordPress.DB.PreparedSQL return (int) $count; } /** * 各状态计数:侧栏统计卡与后台 badge 用。 * 用户视角只统计自己的;管理视角统计全站(可按部门过滤)。 * * @param array $args * @return array status_key => count */ private function ticket_statistic(array $args, bool $is_admin): array { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('ticket'); $out = []; foreach (array_keys(TicketStatusService::map()) as $key) { $out[(string) $key] = 0; } if ($table === '') { return $out; } $query_args = [ 'user_id' => (int) ($args['user_id'] ?? 0), 'ticket_type_id' => $args['ticket_type_id'] ?? 0, ]; [$where, $params] = $this->ticket_where($query_args, $is_admin); $sql = "SELECT status, COUNT(*) AS total FROM {$table} WHERE {$where} GROUP BY status"; $rows = $wpdb->get_results($params === [] ? $sql : $wpdb->prepare($sql, $params), ARRAY_A); // phpcs:ignore WordPress.DB.PreparedSQL if (is_array($rows)) { foreach ($rows as $row) { $key = sanitize_key((string) ($row['status'] ?? '')); if ($key !== '') { $out[$key] = (int) ($row['total'] ?? 0); } } } return $out; } }