抽出 presentAuthFailure 收口登录与发码的失败处理

- 登录/发码两处 catch 的"分流 + 提示 + 上报"逻辑抽成 presentAuthFailure(error, fallback, action)
- 错误池指纹的 action 由调用点传入,避免新增认证动作时各自拼一套导致指纹漂移
This commit is contained in:
2026-10-01 16:44:36 +08:00
parent d1e637b9fd
commit 698b3ffc90
@@ -327,6 +327,28 @@ export function AuthenticatedClient({
return () => window.clearInterval(timer);
}, [codeCooldownSeconds]);
/**
* 认证动作失败的统一收口:可识别的业务 / 会话拒绝只给用户提示;真故障与未识别变体带上文交给
* 错误池。`action` 决定错误池指纹,新增认证动作必须走这里、不要在各自的 catch 里另拼一套。
*/
function presentAuthFailure(
error: unknown,
fallback: string,
action: string,
) {
const failure = resolveClientAuthFailure(error, fallback);
setLoginStatus(failure.message);
if (failure.kind === 'report') {
void captureClientError(
new ClientActionError(
failure.message,
{ source: 'auth', action },
error,
),
);
}
}
async function handleSendCode() {
if (codeBusy || codeCooldownSeconds > 0) {
return;
@@ -348,18 +370,7 @@ export function AuthenticatedClient({
setCodeCooldownSeconds(Math.max(0, Math.floor(response.cooldownSeconds)));
setLoginStatus(`验证码已发送,${response.expiresInSeconds} 秒内有效`);
} catch (error) {
// 业务拒绝(手机号格式 / 发码限流等)只给提示;系统变体与未识别变体带上文交给错误池。
const failure = resolveClientAuthFailure(error, '发送验证码失败');
setLoginStatus(failure.message);
if (failure.kind === 'report') {
void captureClientError(
new ClientActionError(
failure.message,
{ source: 'auth', action: 'send-login-code' },
error,
),
);
}
presentAuthFailure(error, '发送验证码失败', 'send-login-code');
} finally {
setCodeBusy(false);
}
@@ -424,19 +435,7 @@ export function AuthenticatedClient({
setCode('');
setPassword('');
} catch (error) {
// 预期业务拒绝(密码输错 / 密码长度不合规 / 手机号格式等)在这里消化掉,永不进错误池;
// 真故障与未识别变体带上文交给错误池,指纹仍与显式采集时一致。
const failure = resolveClientAuthFailure(error, '登录失败');
setLoginStatus(failure.message);
if (failure.kind === 'report') {
void captureClientError(
new ClientActionError(
failure.message,
{ source: 'auth', action: 'login' },
error,
),
);
}
presentAuthFailure(error, '登录失败', 'login');
} finally {
setLoginBusy(false);
}