Skip to content

fix(maa): 用户配置页在缺少分服关卡数据时不再整页崩溃 - #618

Open
qiyinxi wants to merge 1 commit into
AUTO-MAS-Project:devfrom
qiyinxi:fix/sentry-maastage-20260908
Open

fix(maa): 用户配置页在缺少分服关卡数据时不再整页崩溃#618
qiyinxi wants to merge 1 commit into
AUTO-MAS-Project:devfrom
qiyinxi:fix/sentry-maastage-20260908

Conversation

@qiyinxi

@qiyinxi qiyinxi commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Closes #614

问题

TypeError: Cannot read properties of undefined (reading 'Official')(Sentry AUTO-MAS-DESKTOP-W),MAA 用户配置页打开即整页渲染失败

loadActivityStageOptionsoverview.StageByServer 无条件赋给 stageOverviewByServer

const overview = response.data as HomeOverviewResponse
stageOverviewByServer.value = overview.StageByServer   // 字段缺席 -> ref 变成 undefined

234 行 ref<HomeOverviewResponse['StageByServer']>({}) 特意给的 {} 默认值因此被抹掉。紧接着 applyServerStageOptions 里:

const stageOverview = stageOverviewByServer.value[server]   // ← 先在这里抛
if (!stageOverview) {                                      // ← 这道防线晚了一步
  return
}

那道 if 只防住「某个服务器没有条目」,防不住「容器本身没了」。而 applyServerStageOptions 由 1259 行服务器切换的 watcher 触发,loadUserData 填表期间就会跑到——所以是打开页面即崩,不是点某个按钮才崩。

852 行的 as HomeOverviewResponse 是无校验断言:后端 InfoOut.data 声明为 Dict[str, Any]StageByServer 不进 schema,生成的 API 类型里也没有这个字段,TS 在这里提供不了任何保护。

改动

const overview = response.data as Partial<HomeOverviewResponse> | undefined
if (!overview?.StageByServer) {
  logger.error('活动关卡数据缺少 StageByServer 字段,后端版本可能与前端不匹配')
  activityStageError.value = '加载活动关卡失败:返回数据缺少关卡信息'
  return
}
stageOverviewByServer.value = overview.StageByServer

三点:

  1. 永不写入 undefined{} 默认值得以保留,watcher 后续再跑也只会安全地走 return
  2. 断言放宽成 Partial<...> | undefined让 TS 强制这次判空,而不是靠人记得;
  3. 缺字段时给出「后端版本可能不匹配」的提示,而不是留一个空的活动关卡列表让用户猜。失败路径不清空已有数据,避免把上一次成功加载的结果连带擦掉。

关于触发条件

字段为何缺席未验证。后端 app/api/info.py:286 在 200 路径上总会带上它(InfoOut.dataDict[str, Any],不会被 response_model 裁掉),500 路径又已被 response.code !== 200 提前拦掉。最可能是后端目录没跟前端一起更新,即 #544 那个非原子部署留下的混版安装。

但触发条件在外部不影响这一行该自守——所以本 PR 只修前端这一侧,没有去动后端。

本地验证

corepack yarn typecheck   # 退出码 0
corepack yarn lint        # 退出码 0
corepack yarn test        # Test Files 1 failed | 43 passed,Tests 1 failed | 413 passed

那条失败是 electron/services/backendService.test.ts > managed 模式 > 不传 --repo、不先跑 environment ensure,--app-root 就是用户数据根已确认是 dev 上的既有失败:把本次改动 stash 掉后在干净的 upstream/dev 上重跑,同一条同样失败(1 failed | 413 passed),与本 PR 无关。

yarn format 会顺带重排 ZZZ-OD 那批尚未格式化的文件,本 PR 已把它们全部还原,diff 只有 MAAUserEdit.vue 一个文件。

没有做真机手测:复现需要一个 StageByServer 缺席的后端,本地起的开发后端总会返回该字段。

请审阅

@DLmaster361 这个页面主要是你写的。确认后请你自行合并,我不代合。

Sourcery 摘要

在 MAA 用户配置页面中安全处理缺失的活动阶段数据。

错误修复:

  • 当活动阶段数据缺少 StageByServer 字段时,防止 MAA 用户配置页面崩溃。
  • 当缺少活动阶段信息时,显示清晰的后端兼容性错误,同时保留之前已加载的数据。
Original summary in English

Summary by Sourcery

Handle missing activity stage data safely in the MAA user configuration page.

Bug Fixes:

  • Prevent the MAA user configuration page from crashing when activity stage data lacks the StageByServer field.
  • Show a clear backend compatibility error when activity stage information is missing while preserving previously loaded data.

loadActivityStageOptions 把 overview.StageByServer 无条件赋给
stageOverviewByServer,字段缺席时 ref 被抹成 undefined,把 234 行特意给的
{} 默认值也一起抹掉;紧接着 applyServerStageOptions 里的
`if (!stageOverview) return` 只防住「某服务器没有条目」,防不住「容器本身没了」,
于是上一行 stageOverviewByServer.value[server] 先抛 TypeError。

该函数由服务器切换的 watcher 触发,loadUserData 填表期间就会跑到,
所以表现为打开页面即整页渲染失败。

改为缺字段时给出可读提示并保留原有数据,不再写入 undefined;断言类型同时
放宽成 Partial<...> | undefined,让 TS 强制这次判空——后端 InfoOut.data 是
Dict[str, Any],该字段不进 schema,原来的断言提供不了任何保护。

Closes AUTO-MAS-Project#614

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@qiyinxi qiyinxi added bug Something isn't working Sentry daily Sentry daily issues labels Sep 8, 2026
@qiyinxi
qiyinxi requested a review from DLmaster361 September 8, 2026 08:35
@sourcery-ai

sourcery-ai Bot commented Sep 8, 2026

Copy link
Copy Markdown
审查者指南(在较小的 PR 中折叠显示)

审查者指南

该 PR 针对省略 StageByServer 的响应,增强了活动阶段加载的可靠性:在修改状态之前验证可选字段,保留安全默认值或之前已加载的数据,并同时报告诊断错误和面向用户的错误,从而避免服务器切换监听器导致页面崩溃。该 PR 还更新了版本元数据。

安全加载活动阶段的时序图

sequenceDiagram
    participant User
    participant MAAUserEdit
    participant Backend
    participant Logger
    participant ActivityStageError

    User->>MAAUserEdit: loadActivityStageOptions()
    MAAUserEdit->>Backend: 加载概览数据
    Backend-->>MAAUserEdit: response.data
    alt StageByServer 存在
        MAAUserEdit->>MAAUserEdit: stageOverviewByServer.value = overview.StageByServer
        MAAUserEdit->>MAAUserEdit: applyServerStageOptions()
    else StageByServer 缺失
        MAAUserEdit->>Logger: error()
        MAAUserEdit->>ActivityStageError: 设置阶段缺失错误
        MAAUserEdit-->>User: 保留现有阶段数据并显示失败信息
    end
Loading

文件级变更

变更 详细信息 文件
在更新响应式状态之前检查活动阶段响应,防止缺少 StageByServer 数据导致用户配置页面崩溃。
  • 扩大响应断言范围,以允许部分数据或未定义数据,并显式验证 StageByServer
  • 失败时保留现有阶段映射,而不是写入 undefined
  • 记录后端版本不匹配错误,并公开面向用户的活动阶段加载错误。
frontend/src/views/EditView/User/MAAUserEdit.vue
更新仓库版本元数据。
  • 调整记录的应用程序版本。
res/version.json

可能关联的问题


提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以使用 @sourcery-ai issue 回复审查评论,以根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,以随时(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary,即可在你指定的位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,以随时(重新)生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理了所有评论并且不想再看到它们,这一功能会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这一功能尤其有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义你的体验

访问你的仪表板以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR hardens activity-stage loading against responses that omit StageByServer: it validates the optional field before mutating state, preserves the safe default or previously loaded data, and reports both diagnostic and user-facing errors instead of allowing the server-switch watcher to crash the page. It also updates version metadata.

Sequence diagram for safe activity-stage loading

sequenceDiagram
    participant User
    participant MAAUserEdit
    participant Backend
    participant Logger
    participant ActivityStageError

    User->>MAAUserEdit: loadActivityStageOptions()
    MAAUserEdit->>Backend: load overview data
    Backend-->>MAAUserEdit: response.data
    alt StageByServer is present
        MAAUserEdit->>MAAUserEdit: stageOverviewByServer.value = overview.StageByServer
        MAAUserEdit->>MAAUserEdit: applyServerStageOptions()
    else StageByServer is missing
        MAAUserEdit->>Logger: error()
        MAAUserEdit->>ActivityStageError: set missing-stage error
        MAAUserEdit-->>User: preserve existing stage data and show failure
    end
Loading

File-Level Changes

Change Details Files
Guard the activity-stage response before updating reactive state, preventing missing StageByServer data from crashing the user configuration page.
  • Widen the response assertion to allow partial or undefined data and explicitly validate StageByServer.
  • Preserve the existing stage map on failure instead of writing undefined.
  • Log a backend-version mismatch error and expose a user-facing activity-stage loading error.
frontend/src/views/EditView/User/MAAUserEdit.vue
Update the repository version metadata.
  • Adjust the recorded application version.
res/version.json

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

嘿——我已经审阅了你的更改,整体看起来很棒!

Sourcery 评估

已批准。


Sourcery 对开源项目免费——如果你喜欢我们的审查结果,欢迎考虑分享 ✨
Original comment in English

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Sentry daily Sentry daily issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant