From cbb4b806beadf18d23585c7bd38e27fe784c091e Mon Sep 17 00:00:00 2001 From: ShukeBta Date: Sat, 16 May 2026 17:57:34 +0800 Subject: [PATCH] feat: merge conflict resolution, site management, UI fixes --- .workbuddy/memory/2026-05-15.md | 119 ++ .workbuddy/memory/2026-05-16.md | 58 + .workbuddy/memory/MEMORY.md | 85 ++ docs/current-implementation-analysis.md | 456 ++++++ docs/data-model-diagram.mermaid | 298 ++++ docs/prd-mediastation-full-features.md | 998 +++++++++++++ docs/refactor-architecture.md | 834 +++++++++++ docs/task-dependency-graph.mermaid | 12 + internal/config/config.go | 68 +- internal/handler/api_config_handler.go | 186 +++ internal/handler/auth.go | 19 +- internal/handler/download_client_handler.go | 235 ++++ internal/handler/handler.go | 254 ++++ internal/handler/notify_handler.go | 196 +++ internal/handler/permission_handler.go | 139 ++ internal/handler/refresh_handler.go | 82 ++ internal/handler/response.go | 69 + internal/handler/scheduler_handler.go | 47 + internal/handler/site_handler.go | 115 ++ internal/middleware/emby_auth.go | 95 ++ internal/middleware/middleware.go | 65 +- internal/middleware/permission.go | 111 ++ internal/model/api_config.go | 62 + internal/model/download_client.go | 16 + internal/model/emby_types.go | 530 +++++++ internal/model/model.go | 66 +- internal/model/notify_channel.go | 14 + internal/model/permission.go | 103 ++ internal/model/refresh_token.go | 38 + internal/model/site.go | 33 + internal/model/strm.go | 35 + internal/repository/download_client_repo.go | 92 ++ internal/repository/notify_channel_repo.go | 67 + internal/repository/repository.go | 190 ++- internal/repository/site_repo.go | 56 + internal/repository/strm_repo.go | 82 ++ internal/service/api_config_svc.go | 383 +++++ internal/service/aria2_adp.go | 424 ++++++ internal/service/auth.go | 91 +- internal/service/crypto.go | 5 + internal/service/download_adapter.go | 69 + internal/service/download_manager_svc.go | 253 ++++ internal/service/image_proxy.go | 76 + internal/service/notify_bark.go | 77 + internal/service/notify_email.go | 147 ++ internal/service/notify_svc.go | 185 +++ internal/service/notify_telegram.go | 100 ++ internal/service/notify_webhook.go | 119 ++ internal/service/notify_wechat.go | 77 + internal/service/permission_svc.go | 117 ++ internal/service/qbittorrent_adp.go | 344 +++++ internal/service/service.go | 64 +- internal/service/site_adapter.go | 1387 +++++++++++++++++++ internal/service/site_search_svc.go | 214 +++ internal/service/site_svc.go | 246 ++++ internal/service/sse_hub.go | 228 +++ internal/service/strm_svc.go | 235 ++++ internal/service/token_svc.go | 186 +++ internal/service/transmission_adp.go | 417 ++++++ server.exe~ | Bin 0 -> 24910848 bytes web/src/App.tsx | 18 +- web/src/api/apiConfig.ts | 58 + web/src/api/auth.ts | 6 +- web/src/api/client.ts | 71 +- web/src/api/downloadClient.ts | 51 + web/src/api/notify.ts | 50 + web/src/api/permission.ts | 38 + web/src/api/refresh.ts | 27 + web/src/api/sites.ts | 32 + web/src/components/APIConfigsPanel.tsx | 218 +++ web/src/components/DownloadClientCard.tsx | 250 ++++ web/src/components/Layout.tsx | 26 +- web/src/components/NotifyChannelCard.tsx | 282 ++++ web/src/components/PermissionGuard.tsx | 67 + web/src/hooks/usePermission.ts | 133 ++ web/src/hooks/useSSE.ts | 127 ++ web/src/pages/AdminPage.tsx | 103 +- web/src/pages/FavouritesPage.tsx | 69 +- web/src/pages/LoginPage.tsx | 4 +- web/src/pages/SearchPage.tsx | 103 +- web/src/pages/SitesPage.tsx | 530 +++++++ web/src/stores/auth.ts | 75 +- web/src/stores/permissions.ts | 139 ++ web/src/types/index.ts | 182 +++ 84 files changed, 13826 insertions(+), 202 deletions(-) create mode 100644 .workbuddy/memory/2026-05-15.md create mode 100644 .workbuddy/memory/2026-05-16.md create mode 100644 .workbuddy/memory/MEMORY.md create mode 100644 docs/current-implementation-analysis.md create mode 100644 docs/data-model-diagram.mermaid create mode 100644 docs/prd-mediastation-full-features.md create mode 100644 docs/refactor-architecture.md create mode 100644 docs/task-dependency-graph.mermaid create mode 100644 internal/handler/api_config_handler.go create mode 100644 internal/handler/download_client_handler.go create mode 100644 internal/handler/notify_handler.go create mode 100644 internal/handler/permission_handler.go create mode 100644 internal/handler/refresh_handler.go create mode 100644 internal/handler/response.go create mode 100644 internal/handler/scheduler_handler.go create mode 100644 internal/handler/site_handler.go create mode 100644 internal/middleware/emby_auth.go create mode 100644 internal/middleware/permission.go create mode 100644 internal/model/api_config.go create mode 100644 internal/model/download_client.go create mode 100644 internal/model/emby_types.go create mode 100644 internal/model/notify_channel.go create mode 100644 internal/model/permission.go create mode 100644 internal/model/refresh_token.go create mode 100644 internal/model/site.go create mode 100644 internal/model/strm.go create mode 100644 internal/repository/download_client_repo.go create mode 100644 internal/repository/notify_channel_repo.go create mode 100644 internal/repository/site_repo.go create mode 100644 internal/repository/strm_repo.go create mode 100644 internal/service/api_config_svc.go create mode 100644 internal/service/aria2_adp.go create mode 100644 internal/service/download_adapter.go create mode 100644 internal/service/download_manager_svc.go create mode 100644 internal/service/notify_bark.go create mode 100644 internal/service/notify_email.go create mode 100644 internal/service/notify_svc.go create mode 100644 internal/service/notify_telegram.go create mode 100644 internal/service/notify_webhook.go create mode 100644 internal/service/notify_wechat.go create mode 100644 internal/service/permission_svc.go create mode 100644 internal/service/qbittorrent_adp.go create mode 100644 internal/service/site_adapter.go create mode 100644 internal/service/site_search_svc.go create mode 100644 internal/service/site_svc.go create mode 100644 internal/service/sse_hub.go create mode 100644 internal/service/strm_svc.go create mode 100644 internal/service/token_svc.go create mode 100644 internal/service/transmission_adp.go create mode 100644 server.exe~ create mode 100644 web/src/api/apiConfig.ts create mode 100644 web/src/api/downloadClient.ts create mode 100644 web/src/api/notify.ts create mode 100644 web/src/api/permission.ts create mode 100644 web/src/api/refresh.ts create mode 100644 web/src/api/sites.ts create mode 100644 web/src/components/APIConfigsPanel.tsx create mode 100644 web/src/components/DownloadClientCard.tsx create mode 100644 web/src/components/NotifyChannelCard.tsx create mode 100644 web/src/components/PermissionGuard.tsx create mode 100644 web/src/hooks/usePermission.ts create mode 100644 web/src/hooks/useSSE.ts create mode 100644 web/src/pages/SitesPage.tsx create mode 100644 web/src/stores/permissions.ts diff --git a/.workbuddy/memory/2026-05-15.md b/.workbuddy/memory/2026-05-15.md new file mode 100644 index 0000000..b5aaf2d --- /dev/null +++ b/.workbuddy/memory/2026-05-15.md @@ -0,0 +1,119 @@ +# 2026-05-15 工作记录 + +## MediaStationGo 编译错误修复 + +修复了 8 个 Go 编译错误,构建通过: + +| # | 文件 | 错误 | 修复 | +|---|------|------|------| +| 1 | emby_svc.go:63 | `ServerId` 字段不存在 | 改为 `ServerID` (EmbyAuthResponse 结构体字段名) | +| 2 | emby_svc.go:90 | 同上 | 同上 | +| 3 | emby_svc.go:930 | `proxy.Fetch undefined` | 给 ImageProxy 添加 `Fetch(ctx, url) ([]byte, string, error)` 方法 | +| 4 | scheduler_svc.go:279 | `cannot take address of now.Add()` | 引入 `nextRun` 中间变量 | +| 5 | site_adapter.go:16 | `model` imported and not used | 移除 import (仅注释中引用) | +| 6 | site_adapter.go:1280 | `ri.Description undefined` | 移除该行 (TorrentItem 无 Description 字段,TorrentDetail 才有) | +| 7 | site_svc.go:141 | `*SiteConfig` 不能作为 `SiteConfig` 值 | 解引用 `*cfg` | +| 8 | notify_webhook.go:5 | `bytes` imported and not used | 移除 import | + +**新增代码**: `ImageProxy.Fetch()` — 约 65 行,带缓存和 MIME 检测的图片拉取方法。 + +**构建结果**: `go build ./cmd/server` → 通过,`server.exe` 24.7MB。 + +## 部署浏览测试 (22:26) + +- 停止旧进程 (PID 10784),启动新版 server.exe +- **健康检查**: `GET /api/health` → `{"status":"ok"}` +- **登录 API**: `POST /api/auth/login` → 返回 JWT + user 信息 +- **统计 API**: `GET /api/stats` → 正常返回系统硬件信息 (Go 1.25.0, 17 goroutines) +- **媒体库 API**: `GET /api/libraries` → `[]` (空库,符合预期) +- **前端**: `http://localhost:8080` 浏览器预览已打开,Web 界面正常加载 + +## 拉取合并上游代码 + 修复编译错误 (22:40-22:52) + +### Git 操作 +- `git fetch origin` → 4 commits ahead (PRs #5, #6) +- `git stash push -m "local fixes"` 暂存 8 个本地修改 +- `git pull origin main --no-edit` → fast-forward, 49 files, +4181/−0 +- `git stash pop` → 3 处合并冲突 (handler.go, model.go, service.go) +- 冲突已手动合并 + +### 删除的重复类型名文件 +| 本地文件 | 上游替代 | +|---|---| +| `internal/service/crypto_svc.go` | `crypto.go` | +| `internal/service/emby_svc.go` | `emby_compat.go` | +| `internal/service/scheduler_svc.go` | `scheduler.go` | + +### CryptoService API 签名适配 +上游 `CryptoService.Encrypt(string) string` 和 `Decrypt(string) string` 不返回 error。 +修复了以下文件中的调用:`api_config_svc.go`, `download_manager_svc.go`, `notify_svc.go`, `site_svc.go` + +### 本轮修复的编译错误 (7 errors) +| # | 文件 | 问题 | 修复 | +|---|------|------|------| +| 1 | service.go:105 | `crypto` 变量重复声明 + `NewCryptoService(cfg)` 签名错误 | 删除行105(已有行91的正确初始化) | +| 2 | service.go:111 | `schedulerSvc` 未使用 + `NewSchedulerService(log)` 签名错误 | 删除行111(已有行102的正确初始化) | +| 3 | service.go:184-186 | `RegisterHandler`/`RegisterDefaultTasks` 不存在于上游 SchedulerService | 删除这些调用,保留 `Scheduler.Start()` | +| 4 | service.go:220-262 | 整个 `registerSchedulerHandlers()` 方法不兼容 | 删除方法(43行) | +| 5 | api_config_svc.go:65 | `Decrypt` 2变量赋值(返回1值) | 改为单变量赋值 | +| 6 | site_svc.go:97 | `encryptSite()` 返回 void 却用作条件 | 移除 err 检查 | +| 7 | site_svc.go:219-222 | `Decrypt` 后检查不存在的 `err` | 移除 err 检查块 | +| 8 | handler/ 多个文件 | `Encrypt` 2变量赋值 | 全部改为单变量赋值 | +| 9 | handler/scheduler_handler.go | `GetTasks`/`UpdateTask`/`RunTaskNow`/`TaskConfig` 不存在 | 重写,使用 `Status()`/`RunNow()` | +| 10 | handler/handler.go:388 | `h.UpdateTask` 不存在 | 删除 UpdateTask 函数和对应路由 | + +### 冒烟测试验证 +- 构建: `go build ./cmd/server` → ✅ 通过 +- 健康检查: `/api/health` → ✅ `{"status":"ok"}` +- 登录: `/api/auth/login` → ✅ 返回 JWT +- 统计: `/api/stats` → ✅ 系统信息正常 +- 媒体库: `/api/libraries` → ✅ 空数组 +- 前端: ✅ 浏览器预览正常 + +## 全功能部署浏览测试 (23:04-23:18) + +### 发现并修复的问题 + +| # | 问题 | 根因 | 修复 | +|---|------|------|------| +| 1 | 前端 dist 过期 | dist 构建于 19:31,后端代码最后更新于 22:52 | `npm run build` 重建 | +| 2 | `/api/admin/api-configs` 500 — `api_configs.deleted_at` 列不存在 | model.Base 嵌入 gorm.DeletedAt,旧 DB 无此列 | `ALTER TABLE api_configs ADD COLUMN deleted_at datetime` + `created_at datetime` | +| 3 | `/api/sites` 404 | 站点管理路由未注册到 handler.go | 创建 `site_handler.go`(114行),在 handler.go 注册 7 条路由 | +| 4 | 前端 AdminPage.tsx 编译错误 | `listTasks`/`updateTask`/`runTask` 引用不存在的 API 方法 | 改为 `status()/run(name)`,对齐后端实际 API | + +### 新增文件 + +- `internal/handler/site_handler.go` — PT 站点 CRUD Handler(114行),支持 List/Get/Create/Update/Delete/Test/SiteTypes/AuthTypes + +### 修改文件 + +- `internal/service/service.go` — 添加 `Site *SiteService` 字段 + `siteSvc := NewSiteService(...)` 初始化 +- `internal/handler/handler.go` — 注册 `/api/sites` 路由组(7条) +- `web/src/pages/AdminPage.tsx` — SchedulerPanel 对齐后端 `JobStatus` 类型 + +### API 全面测试结果 (30+ 端点) + +| 模块 | 端点 | 结果 | +|------|------|------| +| 健康检查 | `/api/health` | ✅ 200 | +| 认证 | `/api/auth/login` | ✅ 200, 返回 JWT | +| 用户 | `/api/me`, `/api/auth/permissions` | ✅ 200 | +| 媒体库 | `/api/libraries`, `/api/media` | ✅ 200 | +| 站点管理 | `/api/sites`, `/sites/types`, `/sites/auth-types` | ✅ 200 (新增) | +| 下载 | `/api/downloads`, `/api/download-clients` | ✅ 200 | +| 通知 | `/api/notify-channels`, `/notify-channels/types` | ✅ 200 | +| API配置 | `/api/api-config`, `/api/api-config/providers/list`, `/api/admin/api-configs` | ✅ 200 | +| 发现 | `/api/discover/trending`, `/api/discover/popular` | ✅ 200 | +| 播放 | `/api/history`, `/api/favourites`, `/api/playlists` | ✅ 200 | +| 文件 | `/api/files?path=/`, `/api/storage` | ✅ 200 | +| 订阅 | `/api/subscriptions` | ✅ 200 | +| AI | `/api/ai/status` | ✅ 200 | +| 管理 | `/api/admin/users`, `/admin/settings`, `/admin/scheduler`, `/admin/backups` | ✅ 200 | +| 调度器 | `/api/scheduler/tasks`, `/scheduler/status`, `POST .../run` | ✅ 200 | +| 其他 | `/api/recycle`, `/api/dlna/devices`, `/api/ws` | ✅ 200 | +| 缺失 | `/api/services` | ❌ 404 (Handler 未实现,非核心) | + +### 前端验证 + +- 页面正常加载:WatchHistoryPage, PlaylistsPage, PosterWallPage, FavouritesPage +- 服务器日志无错误,仅 `/api/services` 404 为预期行为 diff --git a/.workbuddy/memory/2026-05-16.md b/.workbuddy/memory/2026-05-16.md new file mode 100644 index 0000000..1761d5a --- /dev/null +++ b/.workbuddy/memory/2026-05-16.md @@ -0,0 +1,58 @@ +# 2026-05-16 工作日志 + +## 站点管理前端页面迁移 + +### 新增文件 +1. **web/src/api/sites.ts** — 站点管理 API 客户端(list/get/create/update/remove/test/types/authTypes),对接后端 8 个端点 +2. **web/src/pages/SitesPage.tsx** — 完整站点管理页面(~400 行) + - 站点卡片网格布局(响应式 1/2/3 列) + - 添加/编辑弹窗(类型选择、认证方式、Cookie/API Key/Auth Header) + - 馒头 M-Team 专用配置提示 + - 启用/停用开关、默认站点开关 + - 高级选项折叠面板(Extra JSON) + - 测试连接(异步+loading 状态) + - 删除确认 + - 空状态提示 + - 暗色主题风格 + +### 修改文件 +3. **web/src/types/index.ts** — 新增 Site、SiteTypeInfo、AuthTypeInfo 接口 +4. **web/src/App.tsx** — 新增 /sites 路由(RequireAdmin)和 lazy import +5. **web/src/components/Layout.tsx** — 侧边栏管理区新增「站点管理」链接(Globe 图标) +6. **web/src/pages/AdminPage.tsx** — 新增「站点管理」Tab,默认激活,嵌入 SitesPage 组件 + +### API 验证(8/8 通过) +- GET /api/sites ✅ +- POST /api/sites ✅(创建成功) +- GET /api/sites/:id ✅ +- PUT /api/sites/:id ✅(更新成功) +- DELETE /api/sites/:id ✅(删除成功) +- POST /api/sites/:id/test ✅ +- GET /api/sites/types ✅(6 种类型) +- GET /api/sites/auth-types ✅(3 种认证方式) + +### 构建状态 +- npm run build ✅(SitesPage-hRHsM4G0.js 14.21 kB) +- go build ./cmd/server ✅ +- 服务器运行中(localhost:8080) + +--- + +## UI 修复与优化(第二阶段) + +### 搜索页 & 收藏页空白问题修复 +- **收藏页根因**:后端 `GET /api/favourites` 无数据时返回 `{"items": null}`(非空数组),前端 `playbackAPI.listFavourites()` 提取 null 后 `items.length` 抛出 TypeError → React 白屏 +- **修复**:`FavouritesPage.tsx` 增加 `data ?? []` 空值保护、`.catch()` 错误处理、重试按钮、空状态面板 +- **搜索页修复**:`SearchPage.tsx` 增加错误处理、空查询不发请求、idle/empty/error 三态 UI + +### AdminPage 去重 & API 配置合并 +- AdminPage 新增「外部API」Tab,嵌入新组件 `APIConfigsPanel` +- 原「设置」Tab 重命名「系统设置」,功能独立(key-value 存储) +- 侧边栏移除「API 配置」独立链接 +- `/api-configs` 路由改为重定向到 `/admin` +- `APIConfigsPanel.tsx`:紧凑表格布局(Provider | 密钥掩码 | 状态徽章 | 操作),点击编辑行内展开 + +### 修改文件 +- 新增:`web/src/components/APIConfigsPanel.tsx` +- 修改:`FavouritesPage.tsx`、`SearchPage.tsx`、`AdminPage.tsx`、`Layout.tsx`、`App.tsx` +- npm run build ✅(AdminPage 25.38 kB 含 APIConfigsPanel) diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md new file mode 100644 index 0000000..7d75ee2 --- /dev/null +++ b/.workbuddy/memory/MEMORY.md @@ -0,0 +1,85 @@ +# MediaStationGo 项目约定 + +## 项目信息 +- **仓库**:https://github.com/ShukeBta/MediaStationGo +- **本地路径**:D:\项目\MediaStationGo +- **技术栈**:Go 1.25 + Gin + GORM + SQLite / React 18 + Vite + Tailwind CSS + Zustand + +## 构建方式(裸机 Windows) +```bash +# 后端 +cd D:/项目/MediaStationGo +go build ./cmd/server # 生成 server.exe + +# 前端 +cd D:/项目/MediaStationGo/web +npm install && npm run build # 生成 web/dist/ + +# 配置 +cp config.example.yaml config.yaml +mkdir -p data cache +``` + +## 启动命令 +```bash +cd D:/项目/MediaStationGo +./server.exe +# 或带环境变量: +MEDIASTATION_APP_PORT=8080 MEDIASTATION_APP_DATA_DIR=./data MEDIASTATION_APP_WEB_DIR=./web/dist ./server.exe +``` + +## 默认配置 +- 端口:8080 +- 管理员:admin / admin123(首次登录提示改密) +- 数据目录:./data +- 缓存目录:./cache + +## 关键 API 端点 +- `GET /api/health` - 健康检查(无需认证) +- `POST /api/auth/login` - 登录获取 JWT +- `GET /api/stats` - 系统统计(需认证) +- `GET /api/libraries` - 媒体库列表(需认证) +- `GET /api/admin/users` - 用户管理(需 admin) + +## 注意事项 +- Docker Desktop 未启动时使用裸机构建 +- Go 1.22 会自动下载 1.25 工具链(GOTOOLCHAIN=auto) +- FFmpeg/ffprobe 需要单独安装才能使用媒体扫描和转码功能 +- **DB 迁移**:model.Base 嵌入 gorm.DeletedAt,如从旧版升级需手动 `ALTER TABLE api_configs ADD COLUMN deleted_at datetime, created_at datetime` +- **前端重建**:修改后端代码后记得 `npm run build` 重建 dist + +## 服务容器 (internal/service/service.go) +- `SiteService` 已注册为 `Container.Site` +- `SiteHandler` 位于 `internal/handler/site_handler.go`,支持 List/Get/Create/Update/Delete/Test/SiteTypes/AuthTypes +- 站点路由注册于 authed 组:`/api/sites`(7条) + +## 前端 Admin 调度器面板 +- 后端只支持 `schedulerAPI.status()` 和 `schedulerAPI.run(name)` +- 不支持 enable/disable 切换(后端无对应 API) + +## 前端站点管理页面 (2026-05-16 新增) +- **API 客户端**:`web/src/api/sites.ts`,对接 8 个端点(list/get/create/update/remove/test/types/authTypes) +- **页面组件**:`web/src/pages/SitesPage.tsx`,完整 CRUD + 测试 + 弹窗 +- **路由**:`/sites`(RequireAdmin),已集成到 App.tsx +- **侧边栏**:管理区「站点管理」链接(Globe 图标) +- **AdminPage Tab**:新增「站点管理」默认激活 Tab,内嵌 SitesPage +- **站点类型**:nexusphp / gazelle / unit3d / mteam / discuz / custom_rss +- **认证方式**:cookie / api_key / auth_header +- **模型字段**:Site{Name, Type(not "site_type"), URL(not "base_url"), AuthType, Cookie, APIKey, AuthHeader, Enabled, IsDefault, Extra} — 注意 Go 后端与 Python 后端的字段名差异 + +## UI 修复与优化 (2026-05-16 第二阶段) + +### 搜索页 & 收藏页空白修复 +- **收藏页根因**:后端 `GET /api/favourites` 在无数据时返回 `{"items": null}`,前端 `playbackAPI.listFavourites()` 提取 `r.data.items` 得到 null,`items.length` 抛出 TypeError → React 白屏 +- **修复**:`FavouritesPage` 增加 `data ?? []` 空值保护,添加 .catch 错误处理、重试按钮、空状态提示 +- **搜索页修复**:增加错误处理,空查询不再发请求(避免无效 API 调用),添加 idle/empty/error 三态 UI + +### AdminPage 去重 & APIConfigs 重构 +- **问题**:原「API 配置」独立页面 (`/api-configs`) 与 AdminPage 内「设置」Tab 功能重叠 +- **方案**: + - AdminPage 新增「外部API」Tab(`api`),嵌入新建的 `APIConfigsPanel` 组件 + - 原「设置」Tab 重命名为「系统设置」以区分用途 + - 侧边栏移除「API 配置」独立链接(`KeyRound`图标) + - `/api-configs` 路由改为 `` +- **APIConfigsPanel**:紧凑表格布局(Provider | 密钥掩码 | 状态徽章 | 操作按钮),点击编辑后行内展开表单,替代原先卡片式布局 +- **文件变更**:新增 `web/src/components/APIConfigsPanel.tsx`;修改 `AdminPage.tsx`、`Layout.tsx`、`App.tsx`;保留 `APIConfigsPage.tsx` 但不再路由引用 diff --git a/docs/current-implementation-analysis.md b/docs/current-implementation-analysis.md new file mode 100644 index 0000000..bf32d2f --- /dev/null +++ b/docs/current-implementation-analysis.md @@ -0,0 +1,456 @@ +# MediaStationGo 当前实现分析报告 + +> **项目路径**: `D:\项目\MediaStationGo` +> **技术栈**: Go 1.25 + Gin + GORM + SQLite (WAL) + Viper + Zap + JWT (后端) | React 18 + Vite + TailwindCSS + Zustand + HLS.js (前端) +> **分析日期**: 2026-02-04 +> **分析者**: Architect (Bob) + +--- + +## 一、总体架构概览 + +### 后端架构 +``` +cmd/server/main.go # 应用入口 +├── internal/config/config.go # 分层配置(默认值/YAML/环境变量) +├── internal/model/model.go # GORM 数据模型(12个实体) +├── internal/repository/ # 数据访问层(12个Repository) +├── internal/service/ # 业务逻辑层(28个服务文件) +├── internal/handler/ # HTTP 路由处理层(19个Handler文件) +├── internal/middleware/ # 中间件(日志/CORS/JWT/Admin) +├── internal/database/ # 数据库初始化与迁移 +``` + +### 前端架构 +``` +web/src/ +├── App.tsx # 路由定义(18个页面路由) +├── main.tsx # React 入口 +├── api/ # API 调用层(15个模块) +│ ├── client.ts # Axios 实例 + 拦截器 +│ ├── auth.ts, library.ts, playback.ts, downloads.ts, ... +│ └── ... +├── pages/ # 页面组件(18个页面) +│ ├── HomePage, LoginPage, LibraryPage, SearchPage, ... +│ └── ... +├── components/ # 公共组件(4个) +│ ├── Layout.tsx, MediaCard.tsx, RequireAuth.tsx, GlobalEvents.tsx +├── stores/auth.ts # Zustand 认证状态管理 +└── types/index.ts # TypeScript 类型定义(14个接口) +``` + +### 数据模型(12个实体) +| 实体 | 说明 | +|------|------| +| User | 用户账户(角色: admin/user) | +| Library | 媒体库根目录(类型: movie/tv/anime/music) | +| Media | 单个可播放媒体项 | +| Series | 电视剧集分组 | +| PlaybackHistory | 播放进度记录 | +| Favorite | 收藏标记 | +| Playlist / PlaylistItem | 用户播放列表 | +| DownloadTask | 下载任务(qBittorrent) | +| Subscription | RSS订阅规则 | +| Setting | 系统键值配置 | +| AccessLog | 操作审计日志 | + +--- + +## 二、功能模块详细分析 + +### 1. 认证与用户系统 ✅ 已完整实现 + +#### 后端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 用户注册(首用户自动提升为admin) | ✅ | `service/auth.go` - `Register()` | +| 用户登录(JWT签发,24h有效期) | ✅ | `service/auth.go` - `Login()` | +| 密码修改(验证旧密码) | ✅ | `service/auth.go` - `ChangePassword()` | +| 初始化Admin种子用户 | ✅ | `service/auth.go` - `SeedAdmin()` | +| JWT中间件认证 | ✅ | `middleware/middleware.go` - `AuthRequired()` | +| Admin权限守卫 | ✅ | `middleware/middleware.go` - `AdminRequired()` | +| CORS跨域支持 | ✅ | `middleware/middleware.go` - `CORS()` | +| 用户列表/删除(管理员) | ✅ | `handler/admin.go`, `handler/profile.go` | +| 角色更新(管理员) | ✅ | `handler/profile.go` - `adminUpdateRoleHandler` | +| 个人资料更新 | ✅ | `service/profile.go`, `handler/profile.go` | + +**API端点**: +- `POST /api/auth/login` - 登录 +- `POST /api/auth/register` - 注册 +- `GET /api/me` - 获取当前用户 +- `PATCH /api/me` - 更新资料 +- `POST /api/me/password` - 修改密码 +- `GET /api/admin/users` - 用户列表(管理员) +- `PATCH /api/admin/users/:id/role` - 更新角色(管理员) +- `DELETE /api/admin/users/:id` - 删除用户(管理员) + +#### 前端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 登录页面 | ✅ | `pages/LoginPage.tsx` | +| 注册入口 | ✅ | 登录页集成 | +| JWT状态管理 | ✅ | `stores/auth.ts` (Zustand) | +| 路由守卫 | ✅ | `components/RequireAuth.tsx` | +| 自动401跳转登录 | ✅ | `api/client.ts` 拦截器 | +| Profile页面 | ✅ | `pages/ProfilePage.tsx` | +| Admin管理页面 | ✅ | `pages/AdminPage.tsx` | + +--- + +### 2. 媒体库管理 ✅ 已完整实现 + +#### 后端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 创建媒体库 | ✅ | `service/media.go` - `CreateLibrary()` | +| 列出所有媒体库 | ✅ | `service/media.go` - `ListLibraries()` | +| 删除媒体库(级联删除媒体) | ✅ | `service/media.go` - `DeleteLibrary()` | +| 扫描媒体库(发现视频文件) | ✅ | `service/scanner.go` - `ScanLibrary()` | +| FFprobe元数据提取 | ✅ | `service/ffprobe.go` | +| 文件系统监控自动扫描 | ✅ | `service/watcher.go` (fsnotify) | +| 剧集季/集解析 | ✅ | `service/episode_parser.go` - `ParseEpisode()` | +| 媒体分页查询 | ✅ | `service/media.go` - `ListMedia()` | +| 媒体搜索(LIKE模糊匹配) | ✅ | `service/media.go` - `SearchMedia()` | +| 媒体详情查询 | ✅ | `service/media.go` - `GetMedia()` | +| TV剧按季分组API | ✅ | `handler/series.go` - `listSeasonsHandler` | +| 软删除/恢复/永久删除 | ✅ | `service/media.go` (回收站功能) | + +**支持的视频格式**: `.mkv`, `.mp4`, `.m4v`, `.avi`, `.mov`, `.webm`, `.ts`, `.rmvb`, `.rm`, `.3gp`, `.mpg`, `.mpeg`, `.strm` + +**API端点**: +- `GET /api/libraries` - 列表 +- `POST /api/libraries` - 创建(需管理员) +- `DELETE /api/libraries/:id` - 删除(需管理员) +- `POST /api/libraries/:id/scan` - 扫描(需管理员) +- `POST /api/libraries/:id/scrape` - 刮削(需管理员) +- `GET /api/libraries/:id/media` - 媒体列表(分页) +- `GET /api/libraries/:id/seasons` - 按季分组 +- `GET /api/media/:id` - 媒体详情 +- `GET /api/media?q=` - 搜索 +- `DELETE /api/media/:id` - 软删除 +- `POST /api/media/:id/restore` - 恢复 +- `DELETE /api/media/:id/purge` - 永久删除 +- `POST /api/media/:id/probe` - 重新探测 + +#### 前端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 首页(继续观看+最近添加) | ✅ | `pages/HomePage.tsx` | +| 媒体库详情页 | ✅ | `pages/LibraryPage.tsx` | +| 媒体详情页 | ✅ | `pages/MediaDetailPage.tsx` | +| 搜索页面 | ✅ | `pages/SearchPage.tsx` | +| 媒体卡片组件 | ✅ | `components/MediaCard.tsx` | +| 回收站页面 | ✅ | `pages/RecycleBinPage.tsx` | + +--- + +### 3. 刮削系统 ✅ 已完整实现(多数据源) + +#### 后端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| TMDb电影刮削 | ✅ | `service/tmdb.go` - `SearchMovie()` | +| Bangumi动漫刮削 | ✅ | `service/bangumi.go` - `Search()` | +| TheTVDB电视剧刮削 | 🔶 | `service/thetvdb.go` (结构存在,需确认实现完整性) | +| Fanart.tv封面升级 | 🔶 | `service/fanart.go` (结构存在,需确认实现完整性) | +| 文件名智能清洗 | ✅ | `service/scraper.go` - `CleanQuery()` | +| 年份提取 | ✅ | 正则 `yearPattern` | +| 噪声词过滤 | ✅ | 35+噪声词(分辨率、编码、字幕组等) | +| 季/集号正则提取 | ✅ | `service/episode_parser.go` | +| 单个媒体刮削 | ✅ | `service/scraper.go` - `EnrichOne()` | +| 批量库刮削(后台执行,4 RPS限流) | ✅ | `service/scraper.go` - `EnrichLibrary()` | +| 刮削进度WebSocket推送 | ✅ | 通过WSHub发布"scrape"事件 | +| TMDb代理支持(GFW穿透) | ✅ | 配置 `tmdb_api_proxy` | +| 图片CDN代理 | ✅ | 配置 `tmdb_image_proxy` | +| NFO导出(Kodi/Jellyfin兼容) | ✅ | `service/nfo.go` | + +**刮削策略链**: +``` +library.type == "anime" → Bangumi (fallback: TMDb) +library.type == "tv" → TheTVDB (fallback: TMDb) +default → TMDb +匹配后可选 Fanart.tv 封面升级 +``` + +**API端点**: +- `POST /api/media/:id/scrape` - 单个刮削(需管理员) +- `POST /api/libraries/:id/scrape` - 批量刮削(需管理员,异步) +- `POST /api/media/:id/nfo` - 导出NFO(需管理员) +- `POST /api/libraries/:id/nfo` - 批量导出NFO(需管理员) +- `GET /api/img?url=...` - 图片代理 + +--- + +### 4. 播放与转码 ✅ 已完整实现 + +#### 后端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 直接播放(HTTP Range支持) | ✅ | `service/stream.go` - `ServeFile()` | +| HLS转码播放 | ✅ | `service/transcoder.go` | +| HLS M3U8播放列表服务 | ✅ | `service/stream.go` - `ServeHLSPlaylist()` | +| HLS分段服务 | ✅ | `service/stream.go` - `ServeHLSSegment()` | +| 转码任务管理 | ✅ | `TranscoderService` (启动/停止/活跃列表) | +| 软件编码 (libx264) | ✅ | 默认编码器 | +| NVIDIA NVENC硬件加速 | ✅ | encoder = "nvenc" | +| Intel QSV硬件加速 | ✅ | encoder = "qsv" | +| VAAPI硬件加速 | ✅ | encoder = "vaapi" | +| FFprobe媒体信息探测 | ✅ | `service/ffprobe.go` | +| 字幕发现(同目录/subs/子目录) | ✅ | `service/subtitle.go` - `Discover()` | +| SRT→WebVTT转换 | ✅ | `service/subtitle.go` - `srtToVTT()` | +| ASS/SSA→WebVTT转换 | ✅ | `service/subtitle.go` - `assToVTT()` | +| 字幕语言检测 | ✅ | 正则语言标签识别 | +| 转码进度WebSocket推送 | ✅ | 通过WSHub发布"transcode"事件 | + +**转码参数**(可配置): +- 视频码率: 1500k(默认) +- 最大码率: 1800k +- 缓冲区: 3000k +- 最大高度: 720p(默认) +- 分段时长: 4秒(默认) +- 音频: AAC 128kHz 立体声 + +**API端点**: +- `GET /api/stream/:id` - 直接播放 +- `GET /api/hls/:id/index.m3u8` - HLS播放列表 +- `GET /api/hls/:id/:seg` - HLS分段 +- `DELETE /api/hls/:id` - 停止转码 +- `GET /api/media/:id/subtitles` - 字幕列表 +- `GET /api/subtitles/:id?path=...` - 字幕内容 + +#### 前端实现 +| 功能 | 状态 | 文件位置 | +|------|------|----------| +| 播放器页面 | ✅ | `pages/PlayerPage.tsx` | +| HLS.js集成 | ✅ | 通过HLS.js播放m3u8 | +| 直接播放回退 | ✅ | `