feat(users): enforce real bans from report processing and fix appeal lifting

- apply_report_action now writes aksrt_user_restriction via Restriction::restrict
  and lifts via new lift_user_restriction(), replacing the display-only
  aksrt_user_ban_state meta dual-track
- ban type restrictions destroy the target user's sessions on apply
- appeal approval lifts active ban/suspend/mute restrictions instead of
  updating a nonexistent status column
- UsersBan.vue posts to /users/{id}/restrict with valid types (warn/mute/
  suspend/ban), scope and expires_at
- add comment report entry to the comment actions dropdown
- add duplicate pending report guard on submit
- remove dead ReportCenter ajax handlers, option migration, REST alias
  routes and write-only restriction meta
- fix report admin mail link to the real aksrt_user_ban page
- add report-ban contract tests
This commit is contained in:
kate522 committed 2026-09-19 21:38:56 +08:00
1 parent 4dafe96c4b
commit 835e0f983d
12 files changed
+291 -214

No files matched your search

+12 -2
View File
@@ -355,8 +355,18 @@ class Home {
return [];
}
$state = sanitize_key((string) get_user_meta($user_id, 'aksrt_user_ban_state', true));
if (!in_array($state, ['muted', 'restricted', 'banned'], true)) {
$state = '';
if (class_exists('\AKSRT\User\Restriction')) {
$svc = \AKSRT\User\Restriction::get_instance();
if ($svc->is_banned($user_id)) {
$state = 'banned';
} elseif ($svc->is_restricted($user_id, \AKSRT\User\Restriction::TYPE_SUSPEND)) {
$state = 'restricted';
} elseif ($svc->is_restricted($user_id, \AKSRT\User\Restriction::TYPE_MUTE)) {
$state = 'muted';
}
}
if ($state === '') {
return [];
}
+33 -13
View File
@@ -75,8 +75,11 @@ class Restriction {
return ['success' => false, 'message' => 'Database error.'];
}
// Update user meta for quick lookup
$this->sync_user_restriction_meta($user_id);
// 封禁立即生效:销毁目标用户全部登录会话
if ($type === self::TYPE_BAN && $user_id > 0) {
\WP_Session_Tokens::get_instance($user_id)->destroy_all();
}
do_action('aksrt_user_restriction_changed', $user_id, $type, 'applied', (string) $data['reason']);
return ['success' => true, 'id' => $wpdb->insert_id];
@@ -92,12 +95,39 @@ class Restriction {
$result = $wpdb->delete($this->table(), ['id' => $restriction_id]);
if ($result) {
$this->sync_user_restriction_meta((int) $restriction['user_id']);
do_action('aksrt_user_restriction_changed', (int) $restriction['user_id'], (string) ($restriction['type'] ?? ''), 'removed', '');
}
return (bool) $result;
}
/**
* 解除用户生效中的限制(可选按类型过滤),用于申诉通过或举报工单"解除限制"。
*
* @param int $user_id 用户 ID
* @param array $types 要解除的类型,默认解除 ban/suspend/mute(保留 warn 警告记录)
* @return int 解除的记录数
*/
public function lift_user_restriction(int $user_id, array $types = [self::TYPE_BAN, self::TYPE_SUSPEND, self::TYPE_MUTE]): int {
global $wpdb;
$types = array_values(array_intersect($types, self::TYPES));
if ($user_id <= 0 || empty($types)) {
return 0;
}
$placeholders = implode(',', array_fill(0, count($types), '%s'));
$deleted = $wpdb->query($wpdb->prepare(
"DELETE FROM {$this->table()} WHERE user_id = %d AND (expires_at IS NULL OR expires_at > %s) AND type IN ({$placeholders})",
array_merge([$user_id, current_time('mysql', true)], $types)
));
if ($deleted > 0) {
do_action('aksrt_user_restriction_changed', $user_id, implode(',', $types), 'lifted', '');
}
return (int) $deleted;
}
/**
* Get a single restriction by ID.
*/
@@ -194,16 +224,6 @@ class Restriction {
return $post_id;
}
/**
* Sync restriction meta for quick lookup.
*/
private function sync_user_restriction_meta(int $user_id): void {
$active = $this->get_user_restrictions($user_id, true);
$types = array_unique(array_column($active, 'type'));
update_user_meta($user_id, '_aksrt_active_restrictions', $types);
update_user_meta($user_id, '_aksrt_is_banned', in_array(self::TYPE_BAN, $types, true) ? 1 : 0);
}
/**
* Get restriction stats for admin overview.
*/