feat: 支持作业集,批量作战(支持自动编队和信赖干员) - #89
Conversation
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些整体性的反馈:
- 新增的
difficulty处理逻辑目前不一致:copilotSetTaskEntries把它当作位掩码来用(difficulty & 1/2),但MAACopilot.isRaid只检查difficulty == 2;建议统一这两处的处理方式(例如使用difficulty & 2 != 0),以避免对突袭关卡的细微误判。 StageNavigationNameResolver.stageCodeByIdentifier在静态属性初始化器中同步加载并解码一个相对较大的 JSON 文件,这会在首次使用时阻塞主线程;建议将这部分 I/O 移出主线程,或者在后台队列上以懒加载的方式初始化并缓存结果。
给予 AI Agent 的提示词
Please address the comments from this code review:
## Overall Comments
- The new `difficulty` handling is inconsistent: `copilotSetTaskEntries` treats it as a bitmask (`difficulty & 1/2`), but `MAACopilot.isRaid` only checks `difficulty == 2`; consider aligning these (e.g., `difficulty & 2 != 0`) to avoid subtle misclassification of raid stages.
- `StageNavigationNameResolver.stageCodeByIdentifier` synchronously loads and decodes a relatively large JSON file in a static property initializer, which can block the main thread on first use; consider moving this I/O off the main thread or initializing it lazily on a background queue and caching the result.
## Individual Comments
### Comment 1
<location path="MeoAsstMac/Navigation/CopilotDetail.swift" line_range="45" />
<code_context>
+
@ToolbarContentBuilder private func detailToolbar() -> some ToolbarContent {
ToolbarItemGroup {
Button {
</code_context>
<issue_to_address>
**issue (bug_risk):** Clipboard button no longer triggers a download, which may be an unintended UX regression.
This button used to parse the ID from the clipboard and immediately set `viewModel.downloadCopilot` / dismiss the sheet. Now it only updates `prtsCode` on successful parse and never triggers a download. If you want to preserve the previous "read & add" behavior, reintroduce the `downloadCopilot` assignment and `showAdd = false`, or explicitly split these into two buttons with separate actions.
</issue_to_address>
### Comment 2
<location path="MeoAsstMac/Navigation/CopilotContent.swift" line_range="194-203" />
<code_context>
+ private func downloadCopilotSet(id: String?) {
</code_context>
<issue_to_address>
**suggestion:** Copilot-set download loop swallows individual entry errors, making debugging partial failures harder.
Within the `for (copilotId, fileName) in zip(...)` loop, the failure path only logs the generic "下载作业集条目失败" message and drops the actual `error`. Please include `error.localizedDescription` (or similar) in the log for each failed item, or aggregate these per-item errors into a summary. This will make partial download failures much easier to diagnose.
Suggested implementation:
```
if let message = response.message {
viewModel.logError("\(message)")
} else {
```
```
viewModel.logError("下载作业集失败: \(id)")
}
self.downloading = false
return
}
guard let data = response.data else {
viewModel.logError("下载作业集失败: \(id), 响应数据为空")
self.downloading = false
return
}
let items = data.items
for (copilotId, fileName) in zip(items.map(\.id), items.map(\.fileName)) {
do {
let file = try await self.downloadCopilotItem(copilotId: copilotId, fileName: fileName)
self.selection = file
} catch {
// Include the localized error description for each failed item to aid debugging
viewModel.logError("下载作业集条目失败: id=\(copilotId), fileName=\(fileName), error=\(error.localizedDescription)")
}
}
self.downloading = false
```
If there are other similar catch blocks inside loops that handle per-item downloads for Copilot sets, you may want to apply the same pattern there: log both identifying information about the item (IDs, filenames) and `error.localizedDescription` so partial failures are easier to diagnose.
</issue_to_address>帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈持续改进 Review 质量。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The new
difficultyhandling is inconsistent:copilotSetTaskEntriestreats it as a bitmask (difficulty & 1/2), butMAACopilot.isRaidonly checksdifficulty == 2; consider aligning these (e.g.,difficulty & 2 != 0) to avoid subtle misclassification of raid stages. StageNavigationNameResolver.stageCodeByIdentifiersynchronously loads and decodes a relatively large JSON file in a static property initializer, which can block the main thread on first use; consider moving this I/O off the main thread or initializing it lazily on a background queue and caching the result.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `difficulty` handling is inconsistent: `copilotSetTaskEntries` treats it as a bitmask (`difficulty & 1/2`), but `MAACopilot.isRaid` only checks `difficulty == 2`; consider aligning these (e.g., `difficulty & 2 != 0`) to avoid subtle misclassification of raid stages.
- `StageNavigationNameResolver.stageCodeByIdentifier` synchronously loads and decodes a relatively large JSON file in a static property initializer, which can block the main thread on first use; consider moving this I/O off the main thread or initializing it lazily on a background queue and caching the result.
## Individual Comments
### Comment 1
<location path="MeoAsstMac/Navigation/CopilotDetail.swift" line_range="45" />
<code_context>
+
@ToolbarContentBuilder private func detailToolbar() -> some ToolbarContent {
ToolbarItemGroup {
Button {
</code_context>
<issue_to_address>
**issue (bug_risk):** Clipboard button no longer triggers a download, which may be an unintended UX regression.
This button used to parse the ID from the clipboard and immediately set `viewModel.downloadCopilot` / dismiss the sheet. Now it only updates `prtsCode` on successful parse and never triggers a download. If you want to preserve the previous "read & add" behavior, reintroduce the `downloadCopilot` assignment and `showAdd = false`, or explicitly split these into two buttons with separate actions.
</issue_to_address>
### Comment 2
<location path="MeoAsstMac/Navigation/CopilotContent.swift" line_range="194-203" />
<code_context>
+ private func downloadCopilotSet(id: String?) {
</code_context>
<issue_to_address>
**suggestion:** Copilot-set download loop swallows individual entry errors, making debugging partial failures harder.
Within the `for (copilotId, fileName) in zip(...)` loop, the failure path only logs the generic "下载作业集条目失败" message and drops the actual `error`. Please include `error.localizedDescription` (or similar) in the log for each failed item, or aggregate these per-item errors into a summary. This will make partial download failures much easier to diagnose.
Suggested implementation:
```
if let message = response.message {
viewModel.logError("\(message)")
} else {
```
```
viewModel.logError("下载作业集失败: \(id)")
}
self.downloading = false
return
}
guard let data = response.data else {
viewModel.logError("下载作业集失败: \(id), 响应数据为空")
self.downloading = false
return
}
let items = data.items
for (copilotId, fileName) in zip(items.map(\.id), items.map(\.fileName)) {
do {
let file = try await self.downloadCopilotItem(copilotId: copilotId, fileName: fileName)
self.selection = file
} catch {
// Include the localized error description for each failed item to aid debugging
viewModel.logError("下载作业集条目失败: id=\(copilotId), fileName=\(fileName), error=\(error.localizedDescription)")
}
}
self.downloading = false
```
If there are other similar catch blocks inside loops that handle per-item downloads for Copilot sets, you may want to apply the same pattern there: log both identifying information about the item (IDs, filenames) and `error.localizedDescription` so partial failures are easier to diagnose.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| @ToolbarContentBuilder private func detailToolbar() -> some ToolbarContent { | ||
| ToolbarItemGroup { | ||
| Button { |
There was a problem hiding this comment.
issue (bug_risk): 剪贴板按钮现在不再触发下载,这可能是一个非预期的交互回退。
这个按钮之前的行为是:从剪贴板解析 ID,并立即设置 viewModel.downloadCopilot / 关闭弹窗。现在它只会在解析成功时更新 prtsCode,但不会触发下载。如果你希望保留之前的「读取并添加」行为,请重新加入对 downloadCopilot 的赋值以及 showAdd = false,或者显式地拆分成两个按钮,分别承担「读取」和「下载」两个独立动作。
Original comment in English
issue (bug_risk): Clipboard button no longer triggers a download, which may be an unintended UX regression.
This button used to parse the ID from the clipboard and immediately set viewModel.downloadCopilot / dismiss the sheet. Now it only updates prtsCode on successful parse and never triggers a download. If you want to preserve the previous "read & add" behavior, reintroduce the downloadCopilot assignment and showAdd = false, or explicitly split these into two buttons with separate actions.
| private func downloadCopilotSet(id: String?) { | ||
| guard let id else { return } | ||
|
|
||
| Task { | ||
| self.downloading = true | ||
| do { | ||
| let response = try await requestCopilotSet(id: id) | ||
| guard response.statusCode == 200 else { | ||
| if let message = response.message { | ||
| viewModel.logError("\(message)") |
There was a problem hiding this comment.
suggestion: 作业集下载循环会吞掉单个条目的错误信息,导致排查部分失败变得更困难。
在 for (copilotId, fileName) in zip(...) 循环中,失败分支只记录了通用的「下载作业集条目失败」日志,而忽略了实际的 error。建议在每个失败的条目日志中包含 error.localizedDescription(或类似信息),或者汇总为一个包含各条目错误的摘要。这会让排查部分下载失败问题变得容易得多。
建议的实现方式:
if let message = response.message {
viewModel.logError("\(message)")
} else {
viewModel.logError("下载作业集失败: \(id)")
}
self.downloading = false
return
}
guard let data = response.data else {
viewModel.logError("下载作业集失败: \(id), 响应数据为空")
self.downloading = false
return
}
let items = data.items
for (copilotId, fileName) in zip(items.map(\.id), items.map(\.fileName)) {
do {
let file = try await self.downloadCopilotItem(copilotId: copilotId, fileName: fileName)
self.selection = file
} catch {
// Include the localized error description for each failed item to aid debugging
viewModel.logError("下载作业集条目失败: id=\(copilotId), fileName=\(fileName), error=\(error.localizedDescription)")
}
}
self.downloading = false
如果在其他针对作业集逐条下载的循环里也有类似的 catch 逻辑,建议同样应用这一模式:同时记录条目的标识信息(ID、文件名等)以及 error.localizedDescription,以便更容易诊断部分失败的问题。
Original comment in English
suggestion: Copilot-set download loop swallows individual entry errors, making debugging partial failures harder.
Within the for (copilotId, fileName) in zip(...) loop, the failure path only logs the generic "下载作业集条目失败" message and drops the actual error. Please include error.localizedDescription (or similar) in the log for each failed item, or aggregate these per-item errors into a summary. This will make partial download failures much easier to diagnose.
Suggested implementation:
if let message = response.message {
viewModel.logError("\(message)")
} else {
viewModel.logError("下载作业集失败: \(id)")
}
self.downloading = false
return
}
guard let data = response.data else {
viewModel.logError("下载作业集失败: \(id), 响应数据为空")
self.downloading = false
return
}
let items = data.items
for (copilotId, fileName) in zip(items.map(\.id), items.map(\.fileName)) {
do {
let file = try await self.downloadCopilotItem(copilotId: copilotId, fileName: fileName)
self.selection = file
} catch {
// Include the localized error description for each failed item to aid debugging
viewModel.logError("下载作业集条目失败: id=\(copilotId), fileName=\(fileName), error=\(error.localizedDescription)")
}
}
self.downloading = false
If there are other similar catch blocks inside loops that handle per-item downloads for Copilot sets, you may want to apply the same pattern there: log both identifying information about the item (IDs, filenames) and error.localizedDescription so partial failures are easier to diagnose.
支持作业集功能,一键按顺序执行作业集的所有作业(支持突袭)(支持自动编队和信赖干员)
本地构建测试正常


Summary by Sourcery
添加对下载和运行带有共享配置选项的 Copilot 任务集(job sets)的支持,包括自动编队和信赖干员选项。
新功能:
改进:
Original summary in English
Summary by Sourcery
Add support for downloading and running copilot job sets with shared configuration options, including automatic formation and trust operators.
New Features:
Enhancements: