Skip to content

fix: 改进Last.fm认证流程,错误处理和用户体验#895

Open
lrst6963 wants to merge 1 commit intoimsyy:devfrom
lrst6963:lastfm-fix
Open

fix: 改进Last.fm认证流程,错误处理和用户体验#895
lrst6963 wants to merge 1 commit intoimsyy:devfrom
lrst6963:lastfm-fix

Conversation

@lrst6963
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings February 17, 2026 00:42
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lrst6963, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在全面改进 Last.fm 认证流程的健壮性、错误处理和用户体验。通过引入更精细的错误消息、重构 API 请求以更好地处理服务器端错误,并优化认证逻辑,用户在连接 Last.fm 时将获得更清晰的反馈和更顺畅的体验,从而减少因认证问题而导致的困惑。

Highlights

  • 改进 Last.fm API 错误处理: 引入了详细的错误消息映射,并重构了响应拦截器,以提供更具体和本地化的错误提示,并根据特定错误代码自动断开连接。
  • 重构 Last.fm API 请求函数: 修改了 lastfmRequestlastfmPostRequest 函数,使其支持泛型以指定返回类型,并在 Last.fm API 返回错误时抛出包含详细错误信息的 AxiosError
  • 新增 TypeScript 接口: 为 Last.fm 认证令牌和会话响应新增了 LastfmAuthTokenResponseLastfmSessionResponse 接口,提高了类型安全性。
  • 优化 Last.fm 认证流程: 在认证流程中增加了预检查和更精细的错误处理逻辑,以改善用户体验和反馈,尤其是在授权等待期间。
  • 延长认证超时时间: 将 Last.fm 认证的超时时间从 30 秒延长至 60 秒,为用户提供更充足的授权时间。
Changelog
  • src/api/lastfm.ts
    • 新增了 LastfmAuthTokenResponseLastfmSessionResponse 接口,以明确 Last.fm 认证和会话响应的数据结构。
    • 引入了 getLastfmErrorMessage 函数,根据 Last.fm 错误代码提供本地化的详细错误描述。
    • 重构了响应拦截器,利用 getLastfmErrorMessage 提供更具体的错误提示,并根据特定错误代码(如会话密钥无效、认证失败)自动断开 Last.fm 连接。
    • 修改了 lastfmRequestlastfmPostRequest 函数,使其支持泛型以指定返回类型,并在 Last.fm API 返回错误时抛出包含详细错误信息的 AxiosError
    • 更新了 getAuthTokengetSession 函数的返回类型,以使用新定义的接口。
  • src/components/Setting/config/network.ts
    • 导入了 getLastfmErrorMessage 函数,以便在设置页面中使用统一的错误消息。
    • 在打开 Last.fm 认证 URL 之前,增加了对 getSession 的预检查,以捕获并处理可能立即发生的认证错误。
    • 改进了 checkAuth 定时器中的错误处理逻辑,针对不同的 Last.fm 错误代码提供更精确的用户反馈,并在某些错误情况下停止轮询并关闭认证窗口。
    • 将 Last.fm 认证的超时时间从 30 秒延长至 60 秒,为用户提供更充足的授权时间。
    • 增强了 Last.fm 连接失败时的整体错误处理,确保即使在非 API 错误(如网络问题)发生时也能提供恰当的用户提示。
Activity
  • 此拉取请求自创建以来,没有检测到人工活动。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

此 PR 显著改进了 Last.fm 集成,特别是在认证流程和错误处理方面。这些更改带来了更好的类型安全、更精细的错误消息,以及在连接账户时更流畅的用户体验。我的审查包含一个建议,通过重新引入对通用网络和 HTTP 错误的后备处理,进一步增强错误处理的稳健性,确保用户不会错过任何错误情况。总的来说,这是一次高质量的贡献。

Comment on lines 71 to 97
lastfmClient.interceptors.response.use(
(response) => response,
(error: AxiosError<LastfmErrorResponse>) => {
const response = error.response;
if (!response) {
window.$message.error("Last.fm 请求失败,请检查网络连接");
return Promise.reject(error);
}

const { status, data } = response;

switch (status) {
case 403: {
const code = data?.error;
if (code === 9 || code === 4 || code === 26) {
window.$message.error("Last.fm 认证失败,需要重新授权,已断开与 Last.fm 的连接!");
disconnect();
} else {
window.$message.error("Last.fm 认证失败,可能需要重新授权");
}
break;
const code = data?.error;
if (typeof code === "number") {
if (code === 14 || code === 15) {
return Promise.reject(error);
}
case 401:
window.$message.error("Last.fm 未授权,已断开与 Last.fm 的连接!");
if (shouldDisconnect(code)) {
window.$message.error("Last.fm 认证失败,需要重新授权,已断开与 Last.fm 的连接!");
disconnect();
break;
case 429:
window.$message.error("Last.fm 请求过于频繁,请稍后再试");
break;
case 500:
case 502:
case 503:
window.$message.error("Last.fm 服务暂时不可用,请稍后再试");
break;
default:
window.$message.error("Last.fm 请求失败");
break;
} else {
window.$message.error(getLastfmErrorMessage(code, data?.message || "Last.fm 请求失败"));
}
} else if (status === 401) {
window.$message.error("Last.fm 未授权,已断开与 Last.fm 的连接!");
disconnect();
}
return Promise.reject(error);
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

新的错误拦截器在处理 Last.fm 特定 API 错误方面做得很好。然而,它似乎移除了旧代码中对通用网络错误和标准 HTTP 错误状态(如 429、5xx)的处理。这可能导致在这些情况下出现静默失败,用户不会收到任何反馈。

例如:

  1. 如果发生网络错误(error.response 为假值),不会显示任何消息,而旧代码会显示“请检查网络连接”。
  2. 如果服务器返回 500 错误并附带 HTML 页面,data.error 将是 undefined,状态也不是 401,因此不会显示任何消息。旧代码对此有后备处理。

建议重新引入对这些情况的处理,以确保在所有错误条件下都能提供稳健的用户体验。

lastfmClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError<LastfmErrorResponse>) => {
    const response = error.response;
    if (!response) {
      window.$message.error("Last.fm 请求失败,请检查网络连接");
      return Promise.reject(error);
    }

    const { status, data } = response;

    const code = data?.error;
    if (typeof code === "number") {
      if (code === 14 || code === 15) {
        return Promise.reject(error);
      }
      if (shouldDisconnect(code)) {
        window.$message.error("Last.fm 认证失败,需要重新授权,已断开与 Last.fm 的连接!");
        disconnect();
      } else {
        window.$message.error(getLastfmErrorMessage(code, data?.message || "Last.fm 请求失败"));
      }
    } else {
      switch (status) {
        case 401:
          window.$message.error("Last.fm 未授权,已断开与 Last.fm 的连接!");
          disconnect();
          break;
        case 429:
          window.$message.error("Last.fm 请求过于频繁,请稍后再试");
          break;
        case 500:
        case 502:
        case 503:
          window.$message.error("Last.fm 服务暂时不可用,请稍后再试");
          break;
        default:
          if (status >= 400) {
            window.$message.error(`Last.fm 请求失败 (${status})`);
          }
          break;
      }
    }
    return Promise.reject(error);
  },
);

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 旨在改进 Last.fm 的认证流程与错误处理,使用户在授权、失败与超时等场景下获得更清晰的反馈,并增强 API 层对错误码的识别与提示能力。

Changes:

  • 在设置页的 Last.fm 连接流程中细化错误码处理与提示,并延长授权超时等待时间。
  • 在 Last.fm API 封装中新增错误码到中文提示的映射,并为 getAuthToken/getSession 增加明确的返回类型。
  • 在 API 请求层对“2xx 但响应体包含 error”的场景进行显式抛错处理。

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/components/Setting/config/network.ts 调整 Last.fm 授权轮询与错误提示逻辑、超时策略与兜底提示
src/api/lastfm.ts 增加错误码提示映射、泛型返回类型、以及对响应体 error 的统一抛错处理
Comments suppressed due to low confidence (1)

src/components/Setting/config/network.ts:171

  • window.open 可能因浏览器/系统弹窗拦截而返回 null。当前在 authWindow 为 null 的情况下仍会启动轮询并最终超时,但用户看不到授权页面也没有明确提示。建议在 open 结果为空时立即终止流程、恢复 loading,并提示用户允许弹窗或改用外部浏览器打开。
      if (typeof window !== "undefined") {
        const authWindow = window.open(authUrl, "_blank", "width=800,height=600");
        const checkAuth = setInterval(async () => {
          if (authWindow?.closed) {

Comment on lines +81 to +86
const code = data?.error;
if (typeof code === "number") {
if (code === 14 || code === 15) {
return Promise.reject(error);
}
case 401:
window.$message.error("Last.fm 未授权,已断开与 Last.fm 的连接!");
if (shouldDisconnect(code)) {
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里仅在 data.error 为 number 时才处理错误码;但调用方(如 Setting 里的 connectLastfm)已在用 Number(...) 做兼容,说明 error 字段可能是 string。若这里严格判断 number,会导致部分错误既不提示也不触发 disconnect。建议对 string/number 都解析为 number(并校验非 NaN)后再走分支。

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +185
const data = response.data as LastfmErrorResponse | T;
if (data && typeof data === "object" && "error" in data) {
const apiError = new Error(
typeof data.message === "string" && data.message ? data.message : "Last.fm 请求失败",
) as AxiosError<LastfmErrorResponse>;
apiError.response = {
status: response.status,
data: data as LastfmErrorResponse,
statusText: response.statusText,
headers: response.headers,
config: response.config,
};
throw apiError;
}
return data as T;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastfmRequest 在 HTTP 成功响应里检测到 { error, message } 后会手动 throw,但这条错误不会进入 axios 的 response error interceptor,因此 shouldDisconnect/disconnect 逻辑不会执行。像 session key 失效(9)这类需要自动断开连接的情况,可能会导致状态不一致(仍然保留旧 sessionKey)。建议在这里检测到 error 时复用 shouldDisconnect 并主动 disconnect(以及按需提示)。

Copilot uses AI. Check for mistakes.
Comment on lines +216 to +230
const data = response.data as LastfmErrorResponse | T;
if (data && typeof data === "object" && "error" in data) {
const apiError = new Error(
typeof data.message === "string" && data.message ? data.message : "Last.fm 请求失败",
) as AxiosError<LastfmErrorResponse>;
apiError.response = {
status: response.status,
data: data as LastfmErrorResponse,
statusText: response.statusText,
headers: response.headers,
config: response.config,
};
throw apiError;
}
return data as T;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastfmPostRequest 同样在 2xx 响应体里发现 { error, message } 时手动 throw,这会绕过 axios 的 response error interceptor,从而跳过 shouldDisconnect/disconnect 逻辑(例如 code=9 时不会自动清理 sessionKey)。建议与 lastfmRequest 一样在此处复用 shouldDisconnect 并执行 disconnect,保证行为一致。

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +163
if (errorData && errorData.error) {
const code = Number(errorData.error);
if (code !== 14 && code !== 15) {
lastfmAuthLoading.value = false;
const errorMessage = errorData.message || "未知错误";
window.$message.error(getLastfmErrorMessage(code, `认证失败: ${errorMessage}`));
return;
}
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里把 Last.fm 错误码 15(token 过期/无效)当作“继续流程/继续轮询”的情况处理:初次预检时 code===15 仍会继续打开授权页,轮询时 code===15 也会一直等待到超时。这样会导致用户永远无法完成授权。建议对 15 单独处理:停止轮询/关闭窗口并重新获取 token(或提示用户重试)。

Copilot uses AI. Check for mistakes.
Comment on lines +192 to +195
const code = Number(errorData.error);
if (code === 14) {
window.$message.info("等待在 Last.fm 授权页面完成授权");
return;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

轮询中遇到错误码 14 时每 2 秒都会调用一次 $message.info,容易刷屏并影响使用体验。建议加一个本地 flag/节流逻辑,仅在首次检测到 14 时提示一次(后续静默等待即可)。

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@kazukokawagawa kazukokawagawa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review

@kazukokawagawa kazukokawagawa marked this pull request as draft February 22, 2026 04:55
@lrst6963 lrst6963 marked this pull request as ready for review February 24, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants