Skip to content

🛡️ Sakura AI 扫描报告 — Giggitycountless/LOL-Desktop-Assistant (78/100) #5

@sakura-ai-reviewer

Description

@sakura-ai-reviewer

Sakura AI 仓库扫描报告

扫描概览

指标
仓库 Giggitycountless/LOL-Desktop-Assistant
扫描时间 2026-05-20 15:47:36
Commit 167134c
扫描文件数 2445
🟡 健康评分 78/100

问题统计

严重性 数量
🔴 Critical 0
🟡 Major 7
🟠 Minor 6
💡 Suggestion 3

🟡 MAJOR

1. summoners_by_names 中 N+1 顺序 HTTP 调用

  • 文件: crates/adapters/src/lib.rs:941-984
  • 类别: performance
  • 置信度: 95%
  • 描述: 在 summoners_by_names 中,对每个召唤师名称顺序发起独立的 LCU HTTP 请求(最多尝试 2 个端点),当名称列表较长时延迟将线性增长。与之对比,summoners_by_ids 会先尝试批量接口。
  • 建议: 与 summoners_by_ids 类似,优先尝试批量接口(如 /lol-summoner/v2/summoners?names=...),仅在批量失败时回退到逐个查询。也可使用 rayon 并行化逐个查询的回退路径。

2. adapters/src/lib.rs 超过 3400 行,职责过于集中

  • 文件: crates/adapters/src/lib.rs:1-3419
  • 类别: maintainability
  • 置信度: 95%
  • 描述: crates/adapters/src/lib.rs 达到 3419 行,涵盖 LCU 适配器、会话管理、数据映射、游戏流程解析、URL 构造、工具函数等多种职责。项目记忆中也多次指出此问题。这使得代码导航、review 和测试都变得困难。
  • 建议: 按功能拆分为独立模块:例如 lcu_client.rs(主适配器)、match_mapping.rs(比赛数据映射)、overlay.rs(实时覆层数据)、url_builder.rs(URL 构造)、champ_select.rs(选人阶段处理)。现有的 community_dragon.rstencent_lol_api.rs 是良好的拆分范例。

3. platform/src/lib.rs 超过 3299 行,混合命令处理与事件循环

  • 文件: crates/platform/src/lib.rs:1-3299
  • 类别: maintainability
  • 置信度: 95%
  • 描述: crates/platform/src/lib.rs 达到 3299 行,将 Tauri 命令处理、WebSocket 事件循环、自动接受逻辑、选人缓存管理、覆盖层窗口管理等多种关注点混合在一个文件中。
  • 建议: 拆分为:commands.rs(所有 Tauri 命令处理)、league_events.rs(WebSocket 事件循环和自动接受)、champ_select_cache.rs(选人缓存与 hydration)、overlay.rs(覆盖层窗口管理)。AppState 定义可保留在 lib.rs 中。

4. enrich_live_overlay_players 中每个玩家 3 次串行 HTTP 请求

  • 文件: crates/adapters/src/lib.rs:1296-1339
  • 类别: performance
  • 置信度: 90%
  • 描述: 在 enrich_live_overlay_players 中,虽然外层使用 par_iter_mut 并行处理不同玩家,但每个玩家内部仍需顺序获取 items、scores、summoner_spells 三类数据。10 名玩家 × 3 请求 = 30 个请求,每个玩家内部串行延迟约 3×3s=9s 最坏情况。
  • 建议: 将每个玩家的 3 个请求也改为并行(例如使用 tokio::join!rayon 嵌套并行),或在 par_iter_mut 内部使用 futures::join! 异步并发获取三类数据。

5. 前端 Advisor.tsx 和 formatting.ts 存在多处未国际化的硬编码字符串

  • 文件: src/pages/Advisor.tsx:7-153
  • 类别: maintainability
  • 置信度: 90%
  • 描述: Advisor.tsx 中存在大量硬编码英文字符串未使用 t() 函数:"Late build"(行147)、"Situational"(行148)、"Lane"(行152)、"Teamfights"(行153)、"Score"(行133)。formatLeaguePhase 函数(formatting.ts:56-79)中 "Partial data"、"Unauthorized"、"Not logged in" 等也直接硬编码。laneLabel 函数(行221-234)同样返回硬编码字符串。
  • 建议: 将所有用户可见字符串添加到 i18n 翻译表中,使用 t() 函数引用。特别是 lanes 数组(行7-13)、laneLabel(行221-234)、AdviceBlock 标题、InfoBlock 标签等。formatLeaguePhase 中的字符串也应国际化。

6. SQLite 每次操作新建连接,无连接池

  • 文件: crates/storage/src/lib.rs:33-209
  • 类别: performance
  • 置信度: 85%
  • 描述: SqliteStore 的每个方法(get_settings、list_activity_entries 等)都调用 Connection::open(&self.database_path) 创建新连接。在高频轮询场景下(如 champ_select 刷新),频繁开关连接会带来不必要的 I/O 开销和锁竞争。
  • 建议: 引入连接池(如 r2d2deadpool-sqlite),或在 SqliteStore 中持有单一的长期 Connection(SQLite 单写者模式下足够),避免重复打开/关闭。同时也减少 configure_connection 的重复调用。

7. advisor_data_snapshots 和 ranked_champion_snapshots 的 payload_json 缺少 version 字段

  • 文件: crates/storage/migrations/0007_advisor_data_cache.sql:1-20
  • 类别: reliability
  • 置信度: 85%
  • 描述: 数据库迁移 0004 和 0007 中创建的 payload_json 列存储了序列化的 JSON 数据,但未包含 versionschema_version 字段。如果未来数据结构变更,无法区分不同版本的存储格式,必须全量迁移。
  • 建议: 在存储 JSON 时始终附带 format_version 字段(类似于 LocalDataExport.format_version 的做法)。读取时检查版本号,按版本分支解析。虽然远程数据已有 RANKED_CHAMPION_FORMAT_VERSION,但本地存储层也应记录。

🟠 MINOR

1. 多处静默失败未记录日志(if let Ok(_) 模式)

  • 文件: crates/adapters/src/lib.rs:292-309
  • 类别: reliability
  • 置信度: 90%
  • 描述: 在 resolve_champion_alias(行292-306)、read_game_asset(行322-323)、read_completed_match(行374-377)等处,HTTP 请求失败被 .ok() 静默吞掉,没有日志记录。当数据异常时难以排查根因。项目记忆中也多次标记此模式。
  • 建议: 对所有静默忽略错误的位置添加 log::warn!tracing::debug! 日志,至少记录请求路径和失败原因。例如:if let Ok(summaries) = session.get_json(...) { ... } else { log::warn!("champion summary fetch failed for alias resolution"); }

2. lock_or_recover 函数在多个模块中重复定义

  • 文件: crates/adapters/src/lib.rs:111-113
  • 类别: maintainability
  • 置信度: 85%
  • 描述: lock_or_recover 函数在 crates/adapters/src/lib.rs(行111-113)和 crates/platform/src/lib.rs(行162-170)中各自独立定义,逻辑完全相同(恢复 poisoned mutex)。属于代码重复。
  • 建议: 提取到 applicationdomain 层作为公共工具函数,或创建一个 shared 工具模块,避免两处维护同一逻辑。

3. champion-summary.json 在多个方法中被重复请求

  • 文件: crates/adapters/src/lib.rs:192-203
  • 类别: performance
  • 置信度: 80%
  • 描述: champion-summary.json 端点在 read_self_dataread_completed_matchread_participant_recent_statsread_participant_recent_stats_batch 等多个方法中各自独立请求。虽然结果通过 champion_aliases 缓存了别名映射,但 champion_name_map 结果未被缓存。
  • 建议: 将 champion_name_map 结果也纳入 champion_aliases 缓存(或新建专门的 champion_names 缓存),避免在同一个轮询周期内对同一 localhost 端点重复发起请求。

4. 缓存使用 Clone 克隆大对象而非 Arc 共享

  • 文件: crates/adapters/src/community_dragon.rs:134-186
  • 类别: performance
  • 置信度: 75%
  • 描述: CommunityDragon 的 BinChampionData(包含 HashMap<String, BinSpellData>)和 Tencent 的 TencentChampionData(包含 HashMap<String, TencentSpell>)在缓存命中时均通过 .clone() 返回副本。对于包含多个英雄数据的大型缓存,这会导致不必要的内存分配和拷贝。
  • 建议: 将缓存值类型改为 Arc<BinChampionData>Arc<TencentChampionData>,缓存命中时返回 Arc::clone(仅增加引用计数),仅在更新缓存时才克隆。读取端改为引用 Arc。

5. live overlay 定时刷新未处理组件卸载后的 setState

  • 文件: src/state/useOverlayEvents.ts:83-94
  • 类别: reliability
  • 置信度: 75%
  • 描述: 在 useOverlayEvents.ts 中,overlay 模式下使用 setInterval 每 5 秒刷新 live overlay 数据。虽然 effect 清理时清除了 interval,但 fetchLiveOverlaySnapshot().then(setLiveOverlaySnapshot) 的 Promise 可能在 interval 被清除后才 resolve,此时组件可能已卸载。
  • 建议: 使用 wasCancelled 标志或 AbortController 模式:在 cleanup 函数中设置标志,Promise resolve 后检查标志再决定是否 setState。项目记忆中也标记了此模式。

6. AppState 包含 14 个 Arc 字段,存在潜在锁竞争风险

  • 文件: crates/platform/src/lib.rs:142-160
  • 类别: architecture
  • 置信度: 70%
  • 描述: AppState 结构体包含 14 个 Arc<Mutex<...>> 字段和多个 Arc<Atomic*> 字段。在高频轮询(champ_select 每 250ms、live overlay 每 5s)下,不同线程可能同时竞争同一锁,特别是 recent_stats_cachechamp_select_cache
  • 建议: 评估使用 RwLock 替代读多写少的场景(如缓存查询),或使用 DashMap 替代 Mutex<HashMap> 以减少锁粒度。对于 champ_select_cache 等写入频率低的场景,RwLock 尤其适合。

💡 SUGGESTION

1. 仓库根目录包含测试输出文件 token_audit.txt / token_audit_after.txt

  • 文件: token_audit.txt:1-141
  • 类别: maintainability
  • 置信度: 95%
  • 描述: 仓库根目录存在 token_audit.txt(17KB)和 token_audit_after.txt(13KB)两个文件,内容为测试运行输出。这些属于本地开发产物,不应提交到仓库中。
  • 建议: 将这两个文件添加到 .gitignore 并从版本控制中移除。如需保留审计结果作为 CI 产物,应存储在 CI artifacts 中而非仓库内。

2. session 开启模式在多处重复(match open_session 模式)

  • 文件: crates/adapters/src/lib.rs:152-162
  • 类别: maintainability
  • 置信度: 90%
  • 描述: LocalLeagueClient 的几乎所有公开方法都包含相同的 session 开启模式:let session = match self.open_session() { SessionOpenResult::Ready(s) => s, SessionOpenResult::Status(st) => return Err(read_error_from_status(st)) };。这段代码在 lib.rs 中重复出现约 10 次。
  • 建议: 提取为辅助方法,例如 fn require_session(&self) -> Result<LcuSession, LeagueClientReadError>,简化所有公开方法的入口代码。

3. CommunityDragon CDN URL 硬编码 /latest/ 路径无版本协商

  • 文件: crates/adapters/src/community_dragon.rs:8
  • 类别: architecture
  • 置信度: 80%
  • 描述: CommunityDragon 的基础 URL 使用硬编码的 /latest/ 路径(https://raw.communitydragon.org/latest/game/data/characters),没有版本锁定机制。当游戏版本更新时,/latest/ 指向的数据可能突然变化,导致解析失败。
  • 建议: 从 LCU 的 game info 中获取当前游戏版本号,将 /latest/ 替换为具体版本号(如 /14.10.1/)。同时保留 /latest/ 作为回退选项,并在 CDN 请求失败时记录详细的版本信息。

此报告由 Sakura AI Reviewer 自动生成

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions