fix(ban): state-aware ban copy and real login failure reasons

- fix(profile): 封号用户的公开主页提示不再沿用「当前账号处于限制状态,
  部分互动能力已关闭」——封号=全面限制(无法登录),该描述与语境不符。
  提示尾句按治理展示态分级:封号→禁封期间无法登录、账号功能全面停用;
  小黑屋→发布、评论等操作已受限制;禁言→暂时无法发表评论。
  UserRuntime 不再烘焙通用默认文案,缺省时由 get_profile_notice 按态取
- fix(auth): 登录失败透传真实原因——REST auth_login 此前将
  Service::login_user 返回的具体错误硬编码为「账号或密码错误」,
  禁封用户登录时 Toast 语境错误;现透传 error 字段,
  friendly_login_error 识别 user_banned 错误码后直接返回禁封文案
  (无法登录 + 解封时间/永久 + 原因)
- fix(auth): 禁封闸口覆盖全部登录路径——此前仅密码登录经
  enforce_login_ban 拦截,短信验证码、公众号扫码、第三方 OAuth 均
  直连 complete_login 可绕过封号限制;现将闸口收敛至 complete_login
  (返回 WP_Error),各调用方中止登录并透出原因;OAuth 重定向新增
  account_banned 错误码,auth.js 与 user-center.js 同步文案映射
- test: 新增登录禁封面契约测试(文案单一来源、Toast 透传、免密闸口、
  统一收口、前端映射),全量 362 项通过
This commit is contained in:
2026-09-22 18:36:09 +08:00
parent f9bc0f69c4
commit 11eda33e58
12 changed files with 169 additions and 21 deletions
@@ -260,10 +260,14 @@ trait UserControllerAuthPhoneOperations {
if (!$this->auth_channel_allowed($configured, $channel)) {
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
}
$user = $this->auth_find_user($target, $channel);
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']), $channel)) {
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
}
$user = $this->auth_find_user($target, $channel);
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']), $channel)) {
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
}
// 禁封用户禁止登录(与密码登录 enforce_login_ban 同语义:封号=无法登录)。
if (class_exists('\AKSRT\User\Restriction') && \AKSRT\User\Restriction::get_instance()->is_banned((int) $user->ID)) {
return $this->error(\AKSRT\User\Restriction::get_instance()->login_ban_message((int) $user->ID), 403);
}
if ($oauth_data) {
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
if (!$oauth_data) {
@@ -293,7 +297,11 @@ trait UserControllerAuthPhoneOperations {
$resolved_login = \AKSRT\Auth\Service::resolve_email_to_username($target);
$user = wp_authenticate($resolved_login, (string) ($payload['password'] ?? $payload['pwd'] ?? ''));
if (is_wp_error($user) || !$user instanceof \WP_User) {
return $this->error(__('账号或密码错误', 'aksrt'), 401);
// 透传具体失败原因(禁封等),不得泛化为“账号或密码错误”。
$bind_error = $user instanceof \WP_Error
? \AKSRT\Auth\Service::friendly_login_error($user->get_error_message(), (string) $user->get_error_code())
: '';
return $this->error($bind_error !== '' ? $bind_error : __('账号或密码错误', 'aksrt'), 401);
}
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
if (!$oauth_data) {
@@ -309,7 +317,9 @@ trait UserControllerAuthPhoneOperations {
}
$result = \AKSRT\Auth\Service::login_user($payload);
if (empty($result['success'])) {
return $this->error(__('账号或密码错误', 'aksrt'), 401);
// 透传具体失败原因:禁封等场景不得泛化为“账号或密码错误”。
$login_error = trim((string) ($result['error'] ?? ''));
return $this->error($login_error !== '' ? $login_error : __('账号或密码错误', 'aksrt'), 401);
}
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'aksrt') : __('登录成功', 'aksrt'), 'redirect' => $redirect]);
}