Files
lumaplayer/CODE_REVIEW_2026-09-06.md
beyondandClaude Opus 5 6e5c9fce95 全面安全加固 + UI一致性 + 功能修复 + 性能优化
安全修复:
- usesCleartextTraffic=false + network_security_config.xml (LAN例外)
- 备份规则排除数据库/SP/缓存 (backup_rules.xml, data_extraction_rules.xml)
- ProGuard Release移除 Log.d/Log.v
- EmbyRepository 日志脱敏 + API Key安全注释
- EmbyServer URL长度/主机名/端口验证

UI一致性:
- LumaDesignSystem 新增16个颜色令牌 + LumaBorders + 2个排版令牌
- 6个屏幕文件硬编码颜色统一到LumaColors
- 排版统一 17sp→18sp, 30sp→32sp, 34sp→32sp

功能修复:
- 首页MyMediaGrid点击导航到库列表
- 搜索齿轮图标导航到设置
- FavoritesAllScreen/HomeLibraryAllScreen 添加下拉刷新
- RecordsScreen 13+硬编码中文→stringResource() + 修复count bug
- 新增16个日历字符串(中英文)

性能优化:
- TMDB数据类添加@Immutable注解
- HomeContentCache添加LRU驱逐策略(最多3个快照)
- VideoPlayerScreen内部轮询从250ms降至500ms

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:43:25 +08:00

313 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LumaPlayer 全面代码审查报告
**项目**: LumaPlayer (com.example.myapplication) — Emby 媒体库客户端
**审查日期**: 2026-09-06
**审查范围**: UI 一致性 / 安全漏洞 / 性能问题 / 功能缺失 / 代码质量
**审查方法**: 5 个专项代理并行审查,阅读全部 50+ 源文件
---
## 📊 总览
| 审查分类 | 🔴 Critical | 🟠 Major | 🟡 Minor | 合计 |
|----------|:-----------:|:--------:|:--------:|:----:|
| 🎨 UI 样式一致性 | 4 | 11 | 15 | **30** |
| 🔒 安全漏洞 | 3 | 7 | 5 | **15** |
| ⚡ 性能问题 | 0 | 7 | 8 | **15** |
| 📋 功能缺失 | 3 | 6 | 14 | **23** |
| 🏗️ 代码质量 | 1 | 7 | 4 | **12** |
| **合计** | **11** | **38** | **46** | **95** |
---
## 🚨 Top 10 最关键问题
| # | 严重度 | 分类 | 问题 | 文件 |
|---|--------|------|------|------|
| 1 | 🔴 | 安全 | 明文密码存储在 Room 数据库(无加密),设备 Root 后密码可读 | `ServerEntity.java:19` |
| 2 | 🔴 | 安全 | `usesCleartextTraffic="true"` 全局允许 HTTP 明文传输 | `AndroidManifest.xml:11` |
| 3 | 🔴 | 安全 | 认证接口无证书固定,MITM 可截获用户密码 | `EmbyRepository.kt:181` |
| 4 | 🔴 | 代码质量 | 全项目零 ViewModel,所有业务逻辑在 Composable 内,配置变更丢失状态 | 全局架构 |
| 5 | 🔴 | UI | 多处硬编码 `Color(0xFF152036)` 等暗色值,浅色主题下不可见 | `MediaDetailScreen.kt:1842` |
| 6 | 🔴 | 功能 | 播放设置点击无反应(`onClick = {}`),连播偏好每次重置 | `SettingsScreen.kt:158` |
| 7 | 🔴 | 功能 | 首页"我的媒体"网格点击无反应,空注释 `// Handle selection` | `HomeScreen.kt:1063` |
| 8 | 🔴 | 功能 | 搜索栏齿轮图标点击无反应 | `AppNavHost.kt:214` |
| 9 | 🟠 | 安全 | 备份规则为空,数据库含密码被自动备份到云端 | `AndroidManifest.xml:11-12` |
| 10 | 🟠 | 安全 | 50+ 处 `catch (_: Exception)` 静默吞掉异常,用户看不到真实错误 | `EmbyRepository.kt` 多处 |
---
## 🎨 一、UI 样式一致性(30 个发现)
### 🔴 Critical(4 个)
**C1. 硬编码暗色背景色破坏浅色模式**
- **文件**: `MediaDetailScreen.kt:1842, 1929`
- **问题**: `Color(0xFF152036)` 作为剧集卡片背景,浅色主题下几乎不可见
- **建议**: 替换为 `LocalDetailSurface.current` 或从沉浸式调色板派生
**C2. EpisodeListDialog 复制了 GlassDialog 的逻辑**
- **文件**: `MediaDetailScreen.kt:1730-1750`
- **问题**: 手动实现 `background(pageBottom.copy(alpha = 0.98f))` + `dialogBorder`,与 `GlassDialog` 不一致
- **建议**: 统一使用 `GlassDialog` 或 `LumaColors.glassBorder`
**C3. 在线/离线状态颜色绕过设计令牌**
- **文件**: `LibraryScreen.kt:119-123`,`HomeScreen.kt:1260`,`VideoPlayerScreen.kt:1676`
- **问题**: 多处直接用 `Color(0xFF34C759)` / `Color(0xFFFF3B30)` / `Color(0xFFFF453A)`,三种不同的红色表示"离线"。`LumaColors.success` / `LumaColors.danger` 已定义但被绕过
- **建议**: 统一到 `LumaColors.success` / `LumaColors.danger`
**C4. LibraryScreen 对话框 overlay 破坏浅色主题**
- **文件**: `LibraryScreen.kt:934, 1097, 1287`
- **问题**: `surfaceOverlay = Color.White.copy(alpha = 0.94f)` 在暗色主题下产生近不透明白色面板
- **建议**: 使用 `LumaColors.background.copy(alpha = 0.94f)` 或移除 overlay
### 🟠 Major(11 个)
| # | 文件 | 问题 | 建议 |
|---|------|------|------|
| M1 | `HomeScreen.kt:407` / `MediaDetailScreen.kt:938` | 星级评分 `Color(0xFFFFC107)` 无令牌 | 添加 `LumaColors.ratingStar` |
| M2 | `HomeScreen.kt:837-843` | `RainbowNumber` 渐变色未纳入设计系统 | 添加到 `LumaColors.rainbowGradient` |
| M3 | `SettingsScreen.kt:406-416` | 弹窗颜色绕过 `LumaColors.regularTint` | 统一使用令牌 |
| M4 | `MediaDetailScreen.kt:1278-1280` | "最佳资源"徽章金色无令牌 | 复用 `LumaColors.accent` |
| M5 | `LibraryScreen.kt:844,946,1106` | 图标库渐变色重复 3 次 | 提取为命名常量 |
| M6 | `VideoPlayerScreen.kt:1387-1855` | 播放器面板 7 种硬编码颜色 | 创建 `PlayerColors` 对象 |
| M7 | `SearchScreen.kt:730` / `RecordsScreen.kt:357` | 海报卡片手动实现而非用 `LumaMediaCard` | 重构使用统一组件 |
| M8 | `SearchScreen.kt:554` / `RecordsScreen.kt:259` 等 | 多处直接用 `AsyncImage` 而非 `LumaAsyncImage` | 统一替换 |
| M9 | 多文件 | 章节标题字体大小不一致(17sp/18sp) | 统一到 `LumaTypography.title` (18sp) |
| M10 | `HomeScreen.kt:650` | 使用 `ExtraBold`(800) 而设计系统定义 `Bold`(700) | 统一为 Bold |
| M11 | `AppBottomBar.kt:35` | 底栏激活色 `#0091FF` 与 `LumaColors.accent` `#3B82F6` 不同 | 使用 `LumaColors.accent` |
### 🟡 Minor(15 个)
| # | 问题 |
|---|------|
| m1 | 间距值 `8.dp`/`10.dp`/`12.dp` 直接写而非用 `LumaSpacing.*` |
| m2 | `LumaSettingRow` 使用 `14.dp` 非标准间距 |
| m3 | `LumaSettingRow` 标题用 `15.sp` 字面值而非 `LumaTypography.body` |
| m4 | `12.sp` 字号未纳入排版系统 |
| m5 | `LumaPageTopBar` 用 `20.sp` 非标准字号 |
| m6 | Hero 标题用 `30.sp` + `FontWeight.Black` 非标准 |
| m7 | `MediaDetailScreen` 剧集标题 `34.sp` 非标准 |
| m8-12 | 5 处缺少 `contentDescription`(轮播指示器、网格卡片、日历箭头、播放器剧集项) |
| m13 | `HomeScreen` 离线红色 `#FF453A` 与其他处 `#FF3B30` 不同 |
| m14 | `MediaDetailScreen` 返回按钮绕过 `LumaIconButton` |
| m15 | 边框宽度不一致(0.5dp ~ 1.5dp),无 `LumaBorders` 令牌 |
---
## 🔒 二、安全漏洞(15 个发现)
### 🔴 Critical(3 个)
**S1. 明文密码存储** (`CWE-312`)
- `ServerEntity.java:19` — 密码和 accessToken 以明文存储在 Room 数据库
- **建议**: 使用 SQLCipher 加密数据库,或至少用 `EncryptedSharedPreferences`
**S2. 全局允许明文流量** (`CWE-319`)
- `AndroidManifest.xml:11` — `usesCleartextTraffic="true"` 允许所有 HTTP 明文传输
- **建议**: 设置为 `false`,添加 `network_security_config.xml`
**S3. 认证无证书固定** (`CWE-295/300`)
- `EmbyRepository.kt:181` — 密码通过 JSON POST 发送,OkHttp 无 `CertificatePinner`
- **建议**: 实现证书固定,认证仅走 HTTPS
### 🟠 Major(7 个)
| # | CWE | 问题 | 文件 |
|---|-----|------|------|
| S4 | CWE-312 | 遗留迁移从明文 SP 写入明文 Room | `ServerRepository.kt:200` |
| S5 | CWE-798 | TMDB Token 编译进 BuildConfig,APK 反编译可提取 | `build.gradle.kts:28` |
| S6 | CWE-598 | `api_key` 作为 URL 查询参数传输 | `EmbyRepository.kt:869` |
| S7 | CWE-921 | `allowBackup="true"` + 空备份规则,数据库被云端备份 | `AndroidManifest.xml:11-12` |
| S8 | CWE-319 | `buildServerUrl` 支持 HTTP 明文协议 | `EmbyServer.kt:142` |
| S9 | CWE-311 | Room 数据库未加密 (`emby_player.db`) | `AppDatabase.java:152` |
| S10 | 异常处理 | 50+ 处 `catch (_: Exception)` 静默吞掉错误 | `EmbyRepository.kt` 等 |
### 🟡 Minor(5 个)
| # | 问题 |
|---|------|
| S11 | `Log.d("EmbyAuth", ...)` 在 Release 中仍可见 |
| S12 | 服务器 URL 输入无长度/格式验证 |
| S13 | `IconLibrary.kt:167` 用 `java.net.URL` 而非共享 OkHttp 客户端 |
| S14 | `Log.e` 输出异常堆栈可能泄露服务器细节 |
| S15 | ProGuard 未配置 `-assumenosideeffects` 移除 `Log.d/v` |
---
## ⚡ 三、性能问题(15 个发现)
### 🟠 Major(7 个)
| # | 问题 | 文件 | 影响 |
|---|------|------|------|
| P1 | `TmdbCastMember` / `TmdbRecommendation` 等缺少 `@Immutable` | `TmdbRepository.kt:21-59` | 不必要的重组 |
| P2 | `FavoriteEntity` 是 Java 可变类传入 Compose,永远不稳定 | `FavoriteEntity.java:13` | LazyGrid 每项重组 |
| P3 | `MediaDetailScreen` 用 `Column + verticalScroll` 而非 `LazyColumn` | `MediaDetailScreen.kt:489` | 所有子组件同时组合,内存高 |
| P4 | 播放器 250ms 轮询 + 内部重复轮询,4次/秒重组整个控件 | `VideoPlayerScreen.kt:389-434` | UI 持续重组 |
| P5 | `FavoritePosterCard` 在主线程解析 JSON | `RecordsScreen.kt:363` | 滚动时卡顿 |
| P6 | `HomeContentCache` 无限增长无驱逐 | `HomeScreen.kt:136` | 内存泄漏风险 |
| P7 | `VideoPlayerScreen` 单函数 ~1600 行,~40 个状态变量 | `VideoPlayerScreen.kt:260` | 任何状态变更重组整个播放器 |
### 🟡 Minor(8 个)
| # | 问题 |
|---|------|
| P8 | `MyMediaGrid` LazyRow 缺少 `key` |
| P9 | `SearchResultsCache` 每次服务器完成都 `.toList()` 拷贝 |
| P10 | `castMembers` 每次重组做 O(n×m) 名称匹配 |
| P11 | `FavoriteSectionHeader` 显示 `"$count/$count"` 无意义 |
| P12 | 播放器剧集 LazyColumn 缺少 `key` |
| P13 | `CalendarEpisodeCard` 每次重组调 `LocalDate.now()` |
| P14 | 搜索并行化可改进(季/集可缓存) |
| P15 | Hero `shuffled()` 在主线程执行 |
---
## 📋 四、功能缺失(23 个发现)
### 🔴 Critical(3 个)
**F1. 播放设置为空壳**
- `SettingsScreen.kt:158` — 点击无反应,无播放速度/连播/字幕偏好等设置
- 连播偏好 (`confirmBeforeContinuousPlayback`) 每次打开播放器重置
**F2. 首页"我的媒体"网格点击无反应**
- `HomeScreen.kt:1063` — `// Handle selection` 空注释,点击无导航
**F3. 搜索栏齿轮图标无功能**
- `AppNavHost.kt:214` — `onGearClick = {}` 空实现
### 🟠 Major(6 个)
| # | 问题 | 文件 |
|---|------|------|
| F4 | `HomeLibraryAllScreen` 无下拉刷新 | `HomeLibraryAllScreen.kt:149` |
| F5 | `RecordsScreen` 收藏/日历 Tab 无下拉刷新 | `RecordsScreen.kt:137` |
| F6 | `FavoritesAllScreen` 无下拉刷新 | `FavoritesAllScreen.kt:59` |
| F7 | `SearchAllScreen` 无分页/无限滚动 | `SearchAllScreen.kt:59` |
| F8 | `FavoriteSectionHeader` 显示 `"$count/$count"` 数字重复 | `RecordsScreen.kt:321` |
| F9 | 日历 Tab 13+ 个中文字符串硬编码,英文模式不翻译 | `RecordsScreen.kt:580-899` |
### 🟡 Minor(14 个)
| # | 问题 |
|---|------|
| F10 | `HomeLibraryAllScreen` 滚动加载无底部 loading 指示器 |
| F11 | 搜索历史清除无确认对话框 |
| F12 | 播放速度偏好不持久化 |
| F13 | 无 Deep Link 支持 |
| F14 | 收藏 Tab 无 loading 状态 |
| F15 | 详情页加载失败无重试按钮 |
| F16 | 所有导航转场相同,详情→播放器无特殊过渡 |
| F17 | 播放器错误无重试机制 |
| F18 | 空库分区显示标题但无内容提示 |
| F19 | 缓存管理仅清除图标缓存,不含 Coil/搜索历史 |
| F20 | 日历依赖 TMDB 无回退,失败静默空白 |
| F21 | 排序弹窗固定高度 `304.dp` |
| F22-23 | 多处交互元素缺少 `contentDescription` |
---
## 🏗️ 五、代码质量(12 个发现)
### 🔴 Critical(1 个)
**Q1. 零 ViewModel 架构 — 全项目无 ViewModel**
- 所有业务逻辑在 `@Composable` 函数中
- `MainApp.kt:146-206` 中 `addServer`/`updateServer` 含认证逻辑
- `AppNavHost.kt:397-519` 中 PlayerDestination 有 8 个 `remember` 状态变量
- **影响**: 配置变更(旋转/深色切换)丢失状态,无法单元测试
- **建议**: 至少引入 `HomeViewModel`, `MediaDetailViewModel`, `PlayerViewModel`
### 🟠 Major(7 个)
| # | 问题 | 文件 |
|---|------|------|
| Q2 | 所有 Repository 是 `object` 单例,无 DI 框架 | 全局 |
| Q3 | 50+ 处 `catch (_: Exception)` 静默吞掉异常 | `EmbyRepository.kt` 等 |
| Q4 | `addServer` / `updateServer` 80% 代码重复 | `MainApp.kt:152-206` |
| Q5 | 5 个文件超 1000 行(最大 LibraryScreen 2031 行) | 见上方 |
| Q6 | 无结构化协程作用域,单例 CoroutineScope 永不取消 | `EmbyRepository.kt:151` |
| Q7 | 仅 9 个测试文件覆盖 ~18K 行代码,Repository 层零测试 | `test/` 目录 |
| Q8 | `playbackReportState` MutableMap 无同步文档 | `EmbyRepository.kt:150` |
### 🟡 Minor(4 个)
| # | 问题 |
|---|------|
| Q9 | 包名 `com.example.myapplication` 是模板默认值 |
| Q10 | `EmbyAuthRequest` 字段用 PascalCase 违反 Kotlin 命名 |
| Q11 | `LumaMotion` 持续时间命名缺描述性后缀 |
| Q12 | `ExperimentalAnimationApi` opt-in 可能不再需要 |
---
## 🛣️ 优化路线图
### 📅 短期(1 周)— 安全修复 + 关键 Bug
1. **安全**: `usesCleartextTraffic="false"` + `network_security_config.xml`
2. **安全**: 配置备份规则排除数据库
3. **安全**: 添加 ProGuard 规则移除 `Log.d/v`
4. **安全**: API Key 改用 HTTP Header 传输
5. **功能**: 修复 `MyMediaGrid` 点击无反应 → 导航到库列表
6. **功能**: 修复搜索齿轮图标 → 导航到设置或移除
7. **功能**: 修复 `"$count/$count"` 显示 bug
8. **UI**: 统一在线/离线颜色到 `LumaColors` 令牌
### 📅 中期(1 个月)— 架构重构 + 安全加固
1. **安全**: 实现数据库加密(SQLCipher 或 EncryptedSharedPreferences)
2. **安全**: 添加 OkHttp 证书固定
3. **架构**: 引入至少 3 个 ViewModel(Detail/Player/Home)
4. **架构**: 引入 Hilt 依赖注入
5. **性能**: MediaDetailScreen 改用 LazyColumn
6. **性能**: VideoPlayerScreen 拆分子组件减少重组范围
7. **功能**: 实现播放设置页面(速度/连播/字幕偏好)
8. **功能**: 添加 3 个页面的下拉刷新
9. **国际化**: 日历 Tab 中文硬编码迁移到 strings.xml
### 📅 长期(3 个月)— 完善 + 可维护性
1. **架构**: 拆分 5 个大文件(LibraryScreen/MediaDetailScreen/VideoPlayerScreen/HomeScreen/EmbyRepository)
2. **安全**: TMDB Token 移至后端代理
3. **测试**: Repository 层单元测试覆盖
4. **测试**: Compose UI 测试覆盖关键流程
5. **UI**: 统一排版系统(消除 17sp/20sp/30sp/34sp 离群值)
6. **UI**: 创建 `LumaBorders` 设计令牌
7. **功能**: Deep Link 支持
8. **功能**: 完善缓存管理(Coil/搜索历史/视频缓存)
---
## 📈 综合健康评分
### 评分:6.5 / 10
| 维度 | 评分 | 说明 |
|------|:----:|------|
| 🎨 UI 一致性 | 6/10 | 设计系统完善但被大量绕过,硬编码颜色/字号泛滥 |
| 🔒 安全性 | 4/10 | 密码明文存储 + 明文传输 + 无证书固定,多项严重漏洞 |
| ⚡ 性能 | 6/10 | 已做三轮优化但核心架构问题(无 LazyColumn、大重组范围)仍在 |
| 📋 功能完整度 | 7/10 | 核心功能齐全,3 个空壳占位需实现 |
| 🏗️ 代码质量 | 5/10 | 无 ViewModel、无 DI、静默异常、大文件、测试不足 |
### 优势
- ✅ 自有 LumaDesignSystem 设计令牌体系
- ✅ LiquidGlass 创新 UI 效果
- ✅ 已完成三轮系统性性能优化(@Immutable、remember、并行化)
- ✅ 分层架构清晰(data/screens/navigation/ui)
- ✅ 双语支持框架完善
- ✅ Room + Migration 管理规范
### 核心短板
- ❌ 安全基础薄弱(明文密码 + 明文传输)
- ❌ 缺少 ViewModel 导致状态管理脆弱
- ❌ 设计令牌定义完善但被大量绕过
- ❌ 静默异常处理掩盖真实问题
---
*报告由 5 个专项审查模块并行审查后汇总生成。审查时间:2026-09-06。*