Files
wordpress-theme-aksrt/inc/classes/class-aksrt-report-center.php
T

310 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* AKSRT Report Center — 举报治理
*
* 使用专用表 {prefix}aksrt_report 存储举报工单。
* 提供举报受理、状态流转、后台处置入口。
* 一次性迁移入口保留用于旧 option 数据导入。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Report_Center {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
}
private function table(): string {
return class_exists('AKSRT_DB_Migrator') ? AKSRT_DB_Migrator::table('report') : '';
}
/**
* 提交举报
*
* @param int $reporter_id 举报人 ID
* @param string $target_type 举报目标类型 (post/comment/user)
* @param int $target_id 目标 ID
* @param string $reason 举报原因
* @param string $category 举报分类
* @return int 举报 ID(0 表示失败)
*/
public function submit(int $reporter_id, string $target_type, int $target_id, string $reason, string $category = ''): int {
if ($reporter_id <= 0 || $target_id <= 0) return 0;
$valid_types = ['post', 'comment', 'user'];
if (!in_array($target_type, $valid_types, true)) return 0;
$table = $this->table();
if ($table === '') return 0;
global $wpdb;
$wpdb->insert($table, [
'reporter_id' => $reporter_id,
'target_type' => sanitize_key($target_type),
'target_id' => $target_id,
'reason' => sanitize_textarea_field($reason),
'category' => sanitize_key($category),
'status' => 'pending',
'created_at' => current_time('mysql'),
], ['%d', '%s', '%d', '%s', '%s', '%s', '%s']);
return $wpdb->insert_id ? (int) $wpdb->insert_id : 0;
}
/**
* 获取待处理举报数
*/
public function get_pending_count(): int {
$table = $this->table();
if ($table === '') return 0;
global $wpdb;
return (int) $wpdb->get_var(
"SELECT COUNT(*) FROM {$table} WHERE status = 'pending'"
);
}
/**
* 获取举报列表
*/
public function get_reports(array $args = []): array {
$table = $this->table();
if ($table === '') return [];
global $wpdb;
$where = [];
$params = [];
$status = sanitize_key((string) ($args['status'] ?? ''));
if ($status !== '') {
$where[] = 'status = %s';
$params[] = $status;
}
$limit = max(1, min(100, (int) ($args['limit'] ?? 20)));
$offset = max(0, (int) ($args['offset'] ?? 0));
$where_sql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT * FROM {$table} {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";
$params[] = $limit;
$params[] = $offset;
$rows = $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A);
// 附加审核备注
foreach ($rows as &$row) {
$row['id'] = (int) $row['id'];
$row['reporter_id'] = (int) $row['reporter_id'];
$row['target_id'] = (int) $row['target_id'];
$row['notes'] = isset($row['notes_json']) ? json_decode($row['notes_json'], true) ?? [] : [];
}
return $rows;
}
/**
* 更新举报状态
*/
public function update_status(int $report_id, string $new_status, string $note = ''): bool {
$valid_statuses = ['pending', 'reviewing', 'resolved', 'dismissed'];
if (!in_array($new_status, $valid_statuses, true)) return false;
$table = $this->table();
if ($table === '') return false;
global $wpdb;
$existing = $wpdb->get_row($wpdb->prepare(
"SELECT id, notes_json FROM {$table} WHERE id = %d",
$report_id
));
if (!$existing) return false;
$update_data = [
'status' => $new_status,
'updated_at' => current_time('mysql'),
];
$format = ['%s', '%s'];
if ($note !== '') {
$notes = json_decode($existing->notes_json ?? '[]', true) ?? [];
$notes[] = [
'note' => sanitize_textarea_field($note),
'status' => $new_status,
'operator' => get_current_user_id(),
'timestamp' => current_time('mysql'),
];
$update_data['notes_json'] = wp_json_encode($notes);
$format[] = '%s';
}
$result = $wpdb->update($table, $update_data, ['id' => $report_id], $format, ['%d']);
return $result !== false;
}
/**
* Get status counts for the admin dashboard.
*
* @return array{pending: int, reviewing: int, resolved: int, dismissed: int, total: int}
*/
public function get_status_counts(): array {
$table = $this->table();
if ($table === '') {
return ['pending' => 0, 'reviewing' => 0, 'resolved' => 0, 'dismissed' => 0, 'total' => 0];
}
global $wpdb;
$rows = $wpdb->get_results(
"SELECT status, COUNT(*) as cnt FROM {$table} GROUP BY status",
ARRAY_A
);
$counts = ['pending' => 0, 'reviewing' => 0, 'resolved' => 0, 'dismissed' => 0];
$total = 0;
foreach ((array) $rows as $row) {
$s = $row['status'] ?? '';
if (isset($counts[$s])) {
$counts[$s] = (int) $row['cnt'];
}
$total += (int) $row['cnt'];
}
$counts['total'] = $total;
return $counts;
}
/**
* 一次性迁移:把 option 中的举报数据搬到表中。
* 幂等 — 已存在的 ID 不会重复插入。
*/
public function migrate_from_option(): int {
$table = $this->table();
if ($table === '') return 0;
$reports = get_option('aksrt_reports', []);
if (!is_array($reports) || empty($reports)) return 0;
global $wpdb;
$migrated = 0;
foreach ($reports as $old_id => $report) {
// 检查是否已迁移(通过 reporter_id + target_type + target_id + created_at 去重)
$existing = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM {$table} WHERE reporter_id = %d AND target_type = %s AND target_id = %d AND created_at = %s",
(int) ($report['reporter_id'] ?? 0),
sanitize_key((string) ($report['target_type'] ?? '')),
(int) ($report['target_id'] ?? 0),
(string) ($report['created_at'] ?? '')
));
if ($existing) continue;
$notes_json = '';
if (!empty($report['notes']) && is_array($report['notes'])) {
$notes_json = wp_json_encode($report['notes']);
}
$wpdb->insert($table, [
'reporter_id' => (int) ($report['reporter_id'] ?? 0),
'target_type' => sanitize_key((string) ($report['target_type'] ?? '')),
'target_id' => (int) ($report['target_id'] ?? 0),
'reason' => sanitize_textarea_field((string) ($report['reason'] ?? '')),
'category' => sanitize_key((string) ($report['category'] ?? '')),
'status' => sanitize_key((string) ($report['status'] ?? 'pending')),
'created_at' => (string) ($report['created_at'] ?? current_time('mysql')),
'updated_at' => (string) ($report['updated_at'] ?? ''),
'notes_json' => $notes_json,
], ['%d', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s']);
if ($wpdb->insert_id) $migrated++;
}
if ($migrated > 0) {
update_option('aksrt_report_migrated_to_table', time());
}
return $migrated;
}
/**
* Initialize AJAX hooks for admin operations.
*/
public function init(): void {
add_action('wp_ajax_aksrt_report_update_status', [$this, 'ajax_update_status']);
add_action('wp_ajax_aksrt_report_list', [$this, 'ajax_list']);
add_action('wp_ajax_aksrt_report_counts', [$this, 'ajax_counts']);
// 一次性迁移钩子
add_action('admin_init', [$this, 'maybe_migrate'], 20);
}
/**
* 自动迁移旧 option 数据(幂等)
*/
public function maybe_migrate(): void {
if (get_option('aksrt_report_migrated_to_table')) return;
$this->migrate_from_option();
}
/**
* AJAX: Update report status (admin only).
*/
public function ajax_update_status(): void {
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
check_ajax_referer('aksrt_report', '_nonce');
$report_id = (int) ($_POST['report_id'] ?? 0);
$new_status = sanitize_key((string) ($_POST['status'] ?? ''));
$note = sanitize_textarea_field((string) ($_POST['note'] ?? ''));
if ($report_id <= 0 || $new_status === '') {
wp_send_json_error(['message' => __('参数无效', 'aksrt')]);
}
$result = $this->update_status($report_id, $new_status, $note);
if ($result) {
wp_send_json_success(['message' => __('状态已更新', 'aksrt')]);
}
wp_send_json_error(['message' => __('更新失败', 'aksrt')]);
}
/**
* AJAX: Get report list (admin only).
*/
public function ajax_list(): void {
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
$status = sanitize_key((string) ($_GET['status'] ?? ''));
$limit = max(1, min(100, (int) ($_GET['limit'] ?? 20)));
$offset = max(0, (int) ($_GET['offset'] ?? 0));
$reports = $this->get_reports(['status' => $status, 'limit' => $limit, 'offset' => $offset]);
$counts = $this->get_status_counts();
wp_send_json_success(['reports' => $reports, 'counts' => $counts]);
}
/**
* AJAX: Get status counts (admin only).
*/
public function ajax_counts(): void {
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
wp_send_json_success($this->get_status_counts());
}
}