Commit 3c6f3372 by luoqi

chore(auth): exchange-code 加路径埋点(consumed/IDEMPOTENT/MISS)取真证据

首开「登录已过期」到底是「宿主首屏双消费」还是「code 过期」,现有日志判不了
(BizError 不落日志)。加三条日志:
- consumed(首次):正常首次换码
- IDEMPOTENT 幂等命中:同一 code 宽限窗内被换第二次 = 双消费实锤(幂等修复已静默兜住)
- MISS 失效:code 既不在池也无幂等副本 = 过期/未知(非双消费,幂等治不了)
只记 code 前 8 位,不泄全码。对方下次一复现即可从生产日志判定根因。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent c8592cdc
Pipeline #3351 failed in 0 seconds
......@@ -123,14 +123,22 @@ export class AuthService {
async exchangeCode(code: string): Promise<ExchangeCodeResponse> {
const expiresIn = this.accessExpiresInSeconds;
const tag = code.slice(0, 8); // 埋点用前缀(不泄全码),相邻日志可对同一 code 关联
const raw = await this.redis.getDel(CODE_PREFIX + code);
if (raw) {
// 首次消费:落一份短期幂等副本,覆盖宿主首屏"同一 code 被换两次"的竞态。
await this.redis.setEx(CODE_RESULT_PREFIX + code, raw, EXCHANGE_GRACE_SECONDS);
this.logger.log(`[exchange-code] consumed(首次) code=${tag}…`);
} else {
// 已被消费:宽限窗内同一 code 返回同一票(幂等,不报 10102);过窗 → 真失效。
const cached = await this.redis.get(CODE_RESULT_PREFIX + code);
if (!cached) throw new BizError(ApiCode.AUTH_INVALID_CODE);
if (!cached) {
// 真失效:code 既不在(未消费池)也不在(幂等副本)= 过期 / 未知 code(非双消费)。
this.logger.warn(`[exchange-code] MISS 失效(过期或未知,非双消费) code=${tag}…`);
throw new BizError(ApiCode.AUTH_INVALID_CODE);
}
// ⭐ 关键证据:同一 code 在宽限窗内被换第二次 = 宿主首屏「双消费」实锤。
this.logger.warn(`[exchange-code] IDEMPOTENT 幂等命中(同一 code 重复换=首屏双消费) code=${tag}…`);
const p = JSON.parse(cached) as { accessToken: string; refreshToken: string };
return { accessToken: p.accessToken, refreshToken: p.refreshToken, expiresIn };
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment