Files
JuYou/文档/项目架构文档.md
2026-08-25 17:59:42 +08:00

688 lines
32 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.
# JuYouAI 项目架构文档
## 1. 架构概览
项目采用“双前端、单 Go 服务进程”的结构:
- `WEB`:面向普通用户的创作端。
- `ADMIN`:面向管理员的运营管理端。
- `API`Go 后端,同时承载 HTTP API、静态文件托管、异步任务 Worker 和定时任务。
- PostgreSQL:保存用户、项目、任务、积分、渠道和媒体元数据,是业务状态的主要数据源。
- Redis:提供 Asynq 队列和运行时缓存,不作为业务任务最终状态的数据源。
- 腾讯云 COS:保存图片、音频、视频和任务结果等二进制对象。
- 第三方渠道:由 ADMIN 动态配置基础网址和 API Key,后端统一调用。
```mermaid
flowchart LR
User["用户浏览器"] --> Nginx["Nginx"]
AdminUser["管理员浏览器"] --> Nginx
Nginx -->|"/"| WebStatic["WEB 静态文件"]
Nginx -->|"/admin/"| AdminStatic["ADMIN 静态文件"]
Nginx -->|"/api/web/*"| API["Go API 进程"]
Nginx -->|"/api/admin/*"| API
API --> PostgreSQL["PostgreSQL"]
API --> Redis["Redis / Asynq"]
API --> COS["腾讯云 COS"]
API --> Channel["第三方渠道接口"]
Redis --> Worker["同一 Go 进程内的 Worker"]
Worker --> PostgreSQL
Worker --> COS
Worker --> Channel
Worker --> LocalMedia["FFmpeg / Python 语音识别"]
```
生产环境由一个名为 `juyou_ai` 的 systemd 服务启动 Go 二进制,进程内部并行运行 HTTP Server 和 Asynq Worker。
## 2. 根目录结构
```text
JuYouAI/
├─ WEB/ 用户端 Vue 应用
├─ ADMIN/ 管理端 Vue 应用
├─ API/ Go API、Worker、迁移和部署文件
├─ 文档/ 架构、部署和业务说明
├─ docker-compose.yml Windows 本地 PostgreSQL、Redis
├─ start_api.bat Windows 本地 API 启动脚本
└─ vite-port.ts 两个前端共用的开发端口选择逻辑
```
三个应用相互独立安装依赖,但前端构建结果会进入 API 目录:
```text
WEB build → API/static/web/
ADMIN build → API/static/admin/
Go build → API/bin/jcf-api
```
## 3. 技术栈
### 3.1 用户端和管理端
| 类别 | 技术 |
| ------ | -------------------------------------------------------- |
| 框架 | Vue 3 |
| 语言 | TypeScript |
| 构建 | Vite 5 |
| 路由 | Vue Router 4 |
| 状态 | Pinia |
| 请求 | Axios |
| 组件库 | Element Plus |
| 图标 | Element Plus IconsWEB 通过 `src/icons.ts` 提供语义映射 |
| 图表 | ECharts,仅 WEB 使用 |
| 样式 | SCSS、统一变量文件和全局样式 |
### 3.2 后端
| 类别 | 技术 |
| ------------ | ------------------------------------------ |
| 语言 | Go 1.26 |
| HTTP 框架 | Gin |
| ORM | GORM |
| 数据库 | PostgreSQL 16 |
| 队列 | Asynq |
| 队列存储 | Redis 7 |
| 对象存储 | AWS SDK v2,通过 S3 协议访问腾讯云 COS |
| 身份认证 | JWT、随机 Refresh Token |
| 密码哈希 | Argon2id |
| 敏感配置加密 | AES-256-GCM |
| 本地媒体处理 | FFmpeg、FFprobe、Python |
## 4. 前端总体架构
`WEB``ADMIN` 都遵循相同的基础分层:
```text
main.ts
├─ 注册 Pinia
├─ 注册 Vue Router
├─ 注册 Element Plus 中文语言包
└─ 加载全局样式
views/ 路由级页面,负责编排业务页面
layouts/ 页面整体框架、侧栏和内容区
components/ 可复用展示和交互组件
composables/ 跨组件复用的组合式逻辑
features/ 按业务功能聚合的前端实现(按需使用)
api/ Axios 实例及业务接口封装
stores/ 登录态、提示消息和界面状态
utils/ 时间、下载、媒体、计费展示等纯工具
assets/ 图片、统一样式变量和全局样式
icons.ts WEB 语义图标到 Element Plus Icons 的统一映射
```
两个前端通过 Vite 动态导入路由页面,降低首次加载体积。API 请求均使用相对地址,开发环境由 Vite 代理,生产环境由 Nginx 代理,因此前端不保存后端服务器地址和第三方密钥。
### 4.1 样式组织
两个前端分别维护:
```text
src/assets/styles/variables.scss
src/assets/styles/main.scss
```
- `variables.scss` 定义颜色、边框、圆角、间距等统一语义变量。
- `main.scss` 放置全局重置、公共基础样式和组件库覆盖。
- 页面或组件自身样式放在对应 `.vue` 的 scoped style 或同名样式文件中。
- WEB 业务页面和组件的颜色均通过 `variables.scss` 中的语义令牌引用;ECharts 等 Canvas 场景先从 CSS 令牌读取实际色值。
- WEB 当前不使用渐变色,也不在业务组件中直接散落十六进制或 `rgb/rgba` 色值。
- 复杂组件按职责拆分样式。例如 `AssetPanel` 依次加载核心布局、媒体状态、预览覆盖和全局对话框主题文件。
- 响应式布局主要通过统一断点下的媒体查询实现。
## 5. WEB 用户端
### 5.1 入口和地址
| 项目 | 值 |
| --------- | ------------------------------------ |
| 入口 | `WEB/src/main.ts` |
| 路由 | `WEB/src/router/index.ts` |
| API 前缀 | `/api/web` |
| Vite base | `/` |
| 构建输出 | `API/static/web` |
| 开发端口 | 从 `127.0.0.1:5501` 开始选择可用端口 |
### 5.2 路由和业务页面
| 前端路由 | 页面 | 主要职责 |
| ------------------------------------------------- | --------------------------------- | -------------------------------- |
| `/login` | `LoginView.vue` | 用户登录 |
| `/drama-projects` | `RedrawProjectsView.vue` | 短剧创作项目列表 |
| `/drama-projects/:projectId` | `DramaProjectView.vue` | 短剧项目和剧集管理 |
| `/drama-projects/:projectId/episodes/:episodeId` | `DramaWorkbenchView.vue` | 短剧分镜工作台 |
| `/redraw-projects` | `RedrawProjectsView.vue` | 剧本反推项目列表 |
| `/redraw-projects/:projectId` | `RedrawProjectView.vue` | 反推项目详情 |
| `/redraw-projects/:projectId/episodes/:episodeId` | `RedrawWorkbenchView.vue` | 视频分析、字幕、反推与生成工作台 |
| `/script-analysis` | `ScriptAnalysisView.vue` | 剧本分析列表 |
| `/script-analysis/:scriptId` | `ScriptAnalysisWorkbenchView.vue` | 剧本分析处理页 |
| `/account` | `AccountView.vue` | 个人资料、积分和消费记录 |
`RedrawProjectsView.vue` 通过路由元信息区分短剧创作和剧本反推列表,复用同一套项目列表逻辑。
### 5.3 主要组件
`WEB/src/components/creative/` 是创作工作台的公共组件区,主要包含:
- 项目创建和编辑对话框;
- 素材面板、素材提示词编辑器;
- 模型和提示词配置对话框;
- 分镜时间线、任务状态和结果历史;
- 视频播放器、视频对比和加载状态;
- 剧本导入、剧本分析结果;
- 反推来源、文本、剧本和素材面板。
其中 `AssetPanel.vue` 的样式按职责拆分为:
```text
AssetPanel.core.scss 面板主体、资产卡片和编辑器基础布局
AssetPanel.media.css 资产操作、历史记录和分镜媒体状态
AssetPanel.preview.css 预览尺寸、缩放入口和播放器覆盖
AssetPanel.dialog.css 非 scoped 的预览对话框全局主题
```
这些文件保持固定加载顺序,同名选择器按该顺序形成稳定的覆盖关系。WEB 图标统一从 `@/icons` 语义映射或 `@element-plus/icons-vue` 引入,不再依赖 Lucide。
页面负责组合这些组件,公共请求和 URL 组装由 `WEB/src/api/creative.ts` 统一封装。
### 5.4 登录态和请求处理
用户端的认证数据保存在浏览器 `localStorage`
```text
web_access_token
web_refresh_token
web_profile
```
请求流程:
1. Axios 请求拦截器读取 `web_access_token`,写入 `Authorization: Bearer ...`
2. 后端返回 401 时,前端使用 Refresh Token 请求 `/api/web/auth/refresh`
3. 同一时间只允许一个刷新请求,其余失败请求复用该 Promise。
4. 刷新成功后重放原请求。
5. 刷新失败后清理本地登录态并跳转登录页。
用户端还会在创建、分析、生成、取消任务后通知积分余额刷新;发现响应中仍有活动任务时会节流同步余额。
## 6. ADMIN 管理端
### 6.1 入口和地址
| 项目 | 值 |
| --------- | ------------------------------------ |
| 入口 | `ADMIN/src/main.ts` |
| 路由 | `ADMIN/src/router/index.ts` |
| API 前缀 | `/api/admin` |
| Vite base | `/admin/` |
| 构建输出 | `API/static/admin` |
| 开发端口 | 从 `127.0.0.1:5500` 开始选择可用端口 |
### 6.2 页面职责
| 前端路由 | 页面 | 主要职责 |
| ------------------- | --------------------- | ------------------------------------ |
| `/login` | `LoginView.vue` | 管理员登录 |
| `/redemption-codes` | `RedemptionsView.vue` | 兑换码批次和兑换码管理 |
| `/styles` | `StylesView.vue` | 项目风格管理和排序 |
| `/prompts` | `PromptsView.vue` | 系统提示词、历史版本和状态管理 |
| `/channels` | `ChannelsView.vue` | 第三方渠道网址、密钥、汇率和并发限制 |
| `/models` | `ModelsView.vue` | 渠道下的模型配置和价格管理 |
| `/users` | `UsersView.vue` | 用户创建、批量操作、状态和积分管理 |
| `/security` | `SecurityView.vue` | 管理员密码和安全操作 |
### 6.3 通用管理页面结构
- `AdminLayout.vue`:管理后台整体布局。
- `AdminMenu.vue`:后台导航菜单。
- `MasterDetailLayout.vue`:列表与详情编辑的公共布局。
- `PageHeader.vue`:统一页面标题区。
- `AdminNumberInput.vue`:统一数字输入行为。
- `ChannelFormFields.vue`:渠道连接、密钥、汇率和并发限制表单。
- `AppIcon.vue`:第三方图标库的统一封装。
渠道的完整 API Key 只在新增或主动更换时提交。查询渠道列表时,后端只返回掩码,不把完整密钥返回浏览器。
### 6.4 管理员认证和二次认证
管理端在 `localStorage` 保存:
```text
admin_access_token
admin_refresh_token
admin_profile
```
Access Token 过期后的刷新机制与 WEB 类似。创建兑换码批次等敏感操作还需要调用 `/api/admin/auth/reauth` 完成短时二次认证,随后通过 `X-Admin-Reauth-Token` 提交受保护请求。
## 7. 后端分层
后端入口是 `API/cmd/api/main.go`。启动时按依赖顺序完成配置加载、数据库连接、Redis 连接、安全组件、存储组件、Service、Handler、Worker 和 HTTP Server 组装。
```mermaid
flowchart TD
Route["Gin Route"] --> Middleware["认证、CORS、Trace ID、Recovery"]
Middleware --> Handler["handlerHTTP 参数和响应"]
Handler --> Module["modules:独立业务模块公开接口"]
Handler --> Service["service:共享业务规则和事务"]
Module --> Service
Module --> DB
Service --> Model["model:领域数据结构"]
Service --> DB["PostgreSQL / GORM"]
Service --> Queue["Asynq Client"]
Handler --> Storage["COS 文件流"]
Queue --> Worker["worker:后台任务"]
Worker --> DB
Worker --> Provider["provider:第三方渠道协议"]
Worker --> Storage
```
### 7.1 后端目录职责
| 目录 | 职责 |
| -------------------- | ------------------------------------------- |
| `cmd/api/` | 进程入口、依赖组装、启动和优雅关闭 |
| `internal/config/` | 读取并校验环境变量 |
| `internal/server/` | Gin、路由、中间件、静态站点托管 |
| `internal/handler/` | HTTP 参数解析、上传限制、状态码和响应 |
| `internal/modules/` | 按业务边界组织的独立模块及公开 Service 接口 |
| `internal/service/` | 业务规则、数据库事务、任务创建和资源删除 |
| `internal/model/` | GORM 模型和领域数据结构 |
| `internal/database/` | PostgreSQL 连接、空库基线和兼容初始化 |
| `internal/cache/` | Redis 普通客户端 |
| `internal/queue/` | Asynq 客户端、服务端和任务类型 |
| `internal/worker/` | 生成、媒体分析、短剧解析、剧本分析任务 |
| `internal/provider/` | 第三方渠道 HTTP 协议适配 |
| `internal/storage/` | COS 对象存储封装 |
| `internal/security/` | 密码哈希、JWT、Refresh Token、密钥加解密 |
| `internal/billing/` | 任务计费、积分预扣、结算和退款 |
| `internal/drama/` | 短剧文本内容处理 |
| `internal/media/` | 媒体命名等公共规则 |
| `internal/datetime/` | Asia/Shanghai 时间语义 |
| `workers/` | Python 本地语音识别脚本 |
| `migrations/` | 正式环境增量数据库迁移 |
| `static/` | WEB 和 ADMIN 构建产物 |
### 7.2 独立业务模块
后端包含两个独立业务模块目录,并在 `cmd/api/main.go` 完成依赖装配:
| 模块 | 目录 | 对外职责 |
| ---------- | -------------------------- | ---------------------------------------------------------------------- |
| 管理业务 | `internal/modules/admin/` | 模型列表与保存、文本模型计费校验、兑换码查询与批量生成、管理员操作审计 |
| 提示词业务 | `internal/modules/prompt/` | 系统提示词与版本历史、用户提示词查询和选择、自定义提示词增删改 |
调用关系如下:
```mermaid
flowchart LR
Main["cmd/api/main.go"] --> AdminModule["modules/admin.Service"]
Main --> PromptModule["modules/prompt.Service"]
AdminHandler["handler/AdminData"] --> AdminModule
AdminHandler --> PromptModule
CreativeHandler["handler/Creative"] --> PromptModule
AdminModule --> DB["GORM / PostgreSQL"]
PromptModule --> DB
```
模块通过构造函数接收数据库依赖,并使用明确的参数、返回值和错误类型对外提供能力。Handler 通过模块公开接口调用业务能力,模块测试位于各自目录的 `service_test.go`
### 7.3 Handler、Service、Worker 边界
Handler 负责:
- 读取路径、Query、JSON 和 multipart 参数;
- 验证文件体积和基础输入格式;
- 从认证上下文取得用户或管理员;
- 调用 Service
- 统一输出 HTTP 状态码、错误码和 Trace ID。
当前 `internal/handler/` 已不再通过 `h.service.DB``h.data.DB` 或同类入口直接执行 GORM 查询和事务。重绘、剧集和资产媒体上传所需的目标校验、记录写入、旧媒体缓存及记录清理由 `internal/service/creative_media.go` 统一提供。
Service 负责:
- 业务权限和资源归属判断;
- 项目、剧集、素材、分镜和任务规则;
- 数据库事务和行锁;
- 积分预扣、结算或退款;
- 创建持久任务并发送 Asynq 唤醒消息。
Worker 负责:
- 从 PostgreSQL 重新读取任务和配置快照;
- 调用第三方渠道;
- 轮询异步结果;
- 下载结果并写入 COS
- 执行本地 FFmpeg、FFprobe 和 Python 处理;
- 持久化任务状态、输出和计费结果;
- 服务重启后恢复运行中的持久任务。
## 8. HTTP API 架构
### 8.1 API 分区
| 前缀 | 使用方 | 认证 |
| ------------------- | -------------------------------- | ------------------------------- |
| `/health` | 运维健康检查 | 无 |
| `/api/web/health` | WEB 健康检查 | 无 |
| `/api/web/auth/*` | 用户登录、刷新、退出 | 登录接口无 Access Token |
| `/api/web/*` | 用户账户和创作业务 | Web JWT |
| `/api/admin/health` | ADMIN 健康检查 | 无 |
| `/api/admin/auth/*` | 管理员登录、刷新、退出和二次认证 | 按接口区分 |
| `/api/admin/*` | 后台资源管理 | Admin JWT,敏感接口增加二次认证 |
### 8.2 通用中间件
Gin 全局中间件顺序包括:
1. Trace ID:复用请求的 `X-Request-ID`,没有时生成 UUID,并写回响应头。
2. 开发日志:仅 `DEBUG=true` 时启用 Gin Logger。
3. Recovery:捕获未处理 panic。
4. CORS:仅允许 `CORS_ORIGINS` 中明确配置的来源。
HTTP Server 主要超时:
| 配置 | 时间 |
| ------------------- | ------ |
| Read Header Timeout | 5 秒 |
| Read Timeout | 30 秒 |
| Write Timeout | 5 分钟 |
| Idle Timeout | 60 秒 |
| 优雅关闭等待 | 10 秒 |
长时间生成任务不会占用一个 HTTP 请求等待完成。HTTP 接口只创建任务,页面通过任务查询接口获取进度。
## 9. 身份认证和安全
### 9.1 用户和管理员隔离
用户和管理员使用不同的数据表、Token Claims、接口前缀和前端存储键:
| 类型 | 账号表 | Refresh Token 表 | Access Token 类型 |
| ------ | ------------- | ---------------------- | ----------------- |
| 用户 | `web_users` | `web_refresh_tokens` | `web_access` |
| 管理员 | `admin_users` | `admin_refresh_tokens` | `access` |
- 密码使用 Argon2id 哈希,不保存明文。
- Access Token 使用 JWT HS256 签名。
- Refresh Token 使用加密安全随机数生成,数据库只保存哈希。
- 管理员二次认证令牌有效期为 5 分钟。
- 渠道 API Key 使用 `CONFIG_ENCRYPTION_KEY` 进行 AES-256-GCM 加密。
### 9.2 请求追踪
前后端错误处理会携带或展示:
```text
错误码
错误描述
X-Request-ID / trace_id
```
运维使用请求追踪 ID 对照 API 日志定位请求。第三方响应体限制长度后进入错误信息,日志不记录认证请求头和密钥。
## 10. 异步任务架构
### 10.1 任务类型
Asynq 当前注册的任务包括:
| 队列任务 | 处理器 | 用途 |
| ----------------------- | ---------------------- | ---------------------------------- |
| `ai:dispatch-channel` | Generation Worker | 按渠道和用户并发限制选取待提交任务 |
| `ai:submit-task` | Generation Worker | 向第三方渠道提交生成任务 |
| `ai:poll-task` | Generation Worker | 轮询第三方异步任务 |
| `ai:download-task` | Generation Worker | 下载完成结果并写入 COS |
| `media:analyze-episode` | Media Worker | 视频探测、抽帧、语音识别和内容反推 |
| `drama:parse-episode` | Drama Parse Worker | 解析剧集文本并形成结构化内容 |
| `script:analyze` | Script Analysis Worker | 执行剧本分析任务 |
队列名称主要为 `ai`Asynq Worker 并发数由 `AI_WORKER_CONCURRENCY` 控制。
### 10.2 生成任务状态流
```mermaid
stateDiagram-v2
[*] --> pending_submission
pending_submission --> submitting: 调度器取得渠道槽位
submitting --> submitted: 上游返回任务 ID
submitting --> result_ready: 上游直接返回结果 URL
submitting --> submit_unknown: 无法确认提交结果
submitted --> processing: 轮询仍在处理
processing --> result_ready: 上游完成
result_ready --> downloading
downloading --> succeeded: COS 持久化成功
pending_submission --> cancelled
submitted --> cancel_requested
processing --> cancel_requested
cancel_requested --> cancelled
submitting --> failed
submitted --> failed
processing --> failed
downloading --> failed
```
实际状态更新以 PostgreSQL `generation_tasks` 为准。Redis 中的 Asynq 任务只负责唤醒处理逻辑,Redis 队列长度不代表业务任务状态。
### 10.3 持久任务流程
1. WEB 发起分析或生成请求。
2. Service 在 PostgreSQL 事务中校验用户、项目、渠道、配置和积分。
3. 创建 `generation_tasks` 等持久任务记录,必要时预扣积分。
4. 向 Redis/Asynq 写入轻量任务 ID。
5. Worker 根据 ID 回查 PostgreSQL,完整业务快照保存在 PostgreSQL。
6. 调度器使用数据库锁和 `SKIP LOCKED` 控制多 Worker 竞争,并执行渠道总并发、单用户并发限制。
7. Worker 解密渠道密钥并提交第三方请求。
8. 异步上游由轮询任务持续更新状态;直接结果跳过轮询。
9. 完成后将结果下载到本地临时文件,再上传 COS。
10. 在数据库事务中创建 `generation_outputs`、更新分镜或素材的活动输出并结算积分。
11. 失败或取消时按业务规则退款、保留预扣或记录错误。
### 10.4 重启恢复
Go 进程启动时会执行恢复逻辑:
- 找出 `pending_submission` 任务,重新唤醒渠道调度;
- 找出 `submitted``processing` 和部分 `cancel_requested` 任务,恢复轮询;
- 找出 `result_ready``downloading` 任务,恢复结果下载;
- 把中断的剧本分析处理状态恢复到可重新消费状态;
- 恢复短剧解析任务。
因此 PostgreSQL 中的任务记录是恢复依据。Redis 清理或状态变化不改变 PostgreSQL 中持久化的业务任务状态。
## 11. 数据库架构
### 11.1 数据领域
| 领域 | 主要数据表 |
| ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| 管理员安全 | `admin_users``admin_refresh_tokens``admin_audit_logs` |
| 用户账户 | `web_users``web_refresh_tokens` |
| 积分和兑换 | `point_ledger``point_grants``point_ledger_allocations``point_packages``redemption_batches``redemption_codes` |
| 渠道和模型 | `channels``channel_balance_snapshots``models``model_prices``user_model_configs``project_model_configs` |
| 提示词和风格 | `prompts``user_prompt_preferences``project_styles` |
| 创作项目 | `creative_projects``project_episodes``project_assets``episode_storyboards``episode_sources` |
| 短剧解析 | `drama_import_sessions``drama_parse_batches``drama_parse_tasks``episode_continuity_contexts` |
| 生成任务 | `generation_tasks``generation_outputs` |
| 剧本分析 | `script_analyses``script_analysis_characters` |
| 媒体 | `media_assets``channel_asset_cache` |
| 其他运营 | `announcements``payment_configs``recharge_orders``inspiration_gallery_items` 等 |
### 11.2 数据库初始化和迁移
数据库结构有三个相关机制:
1. 空库基线:`API/internal/database/schema.sql` 通过 `go:embed` 编译进二进制。数据库完全没有业务表时,API 启动会在事务中执行该基线。
2. 缺表补全:启动时只对不存在的 GORM 模型表执行 `AutoMigrate`,不会依赖 AutoMigrate 修改已有表的全部结构。
3. 增量迁移:生产部署脚本按文件名执行 `API/migrations/*.up.sql`,成功后记录在 `jcf_schema_migrations`
`internal/database/postgres.go` 还包含旧安装兼容 DDL。数据库结构由 GORM Model、空库基线和增量迁移共同表达,部署脚本负责执行正式迁移。
### 11.3 数据库连接语义
- 时区固定为 `Asia/Shanghai`
- GORM 最大连接数为 20,最大空闲连接数为 5。
- 单连接最长复用时间为 30 分钟。
- 启动时先 Ping PostgreSQL,连接失败则整个进程退出。
## 12. Redis 和队列
Redis 使用两个逻辑 DB
| 配置 | 默认值 | 用途 |
| ---------------- | ------ | --------------------------- |
| `REDIS_DB` | `0` | 普通 Redis 客户端和健康检查 |
| `ASYNQ_REDIS_DB` | `1` | Asynq 队列 |
API 启动时会同时检查 PostgreSQL 和 Redis。任一基础依赖无法连接,服务不会继续启动。
Redis 数据用于队列和调度,PostgreSQL 保存任务真实状态、租约、上游任务 ID、错误和计费结果。
## 13. 媒体和对象存储
### 13.1 COS 存储职责
COS 保存:
- 用户头像和上传素材;
- 视频源文件、字幕、音频;
- 视频抽帧和缩略图;
- 本地识别结果;
- 第三方原始响应;
- 图片和视频生成结果。
PostgreSQL 的 `media_assets` 保存对象键、公开 URL、MIME、大小、SHA-256、显示名称和归属关系,不把大文件二进制放入数据库。
### 13.2 本地媒体处理
Media Worker 调用:
- FFprobe:读取时长、分辨率等媒体信息;
- FFmpeg:抽帧、提取音频和其他媒体转换;
- Python 脚本:执行本地语音识别;
- 第三方渠道:执行文本或视觉内容反推;
- COS:持久化中间产物和最终结果。
临时文件处理完成后清理,长期资源进入 COS,并在数据库保留元数据。
## 14. 第三方渠道适配
渠道基础网址和 API Key 由 ADMIN 保存到 `channels` 表,具体业务模型配置保存在 `models` 表。渠道密钥以 AES-256-GCM 密文保存,只有 Worker 调用时解密。
`internal/provider/apimart/` 负责统一的渠道 HTTP 协议:
- Bearer Token 认证;
- 文本请求;
- 图片任务提交;
- 视频任务提交;
- 异步任务查询;
- 渠道余额查询;
- 上游错误码、`Retry-After` 和响应体截断。
Service 和 Worker 通过 `internal/provider` 中的适配器访问第三方接口。
## 15. 计费和积分
计费逻辑集中在 `internal/billing/`,而不是由前端计算最终扣费:
- 前端只展示估算价格和积分余额;
- Service 创建任务时计算预估积分并按规则预扣;
- Worker 从第三方响应或任务结果取得实际用量;
- 任务完成时结算,多退少补按现有业务规则执行;
- 失败、取消和上游状态不确定分别使用不同处理规则;
- `point_ledger` 保存流水,`point_grants` 和分配表支持永久积分批次的顺序扣减及原批次退款;
扣款、退款、任务终态和输出激活操作在数据库事务中执行,结算以后端持久数据为依据。
## 16. 静态文件和部署架构
### 16.1 开发环境
```mermaid
flowchart LR
WebDev["WEB Vite"] -->|"/api 代理"| Go["Go API :8123"]
AdminDev["ADMIN Vite"] -->|"/api 代理"| Go
Go --> PG["Docker PostgreSQL :25432"]
Go --> Redis["Docker Redis :26379"]
```
- `docker-compose.yml` 只启动 PostgreSQL 和 Redis。
- Go API 直接在 Windows 运行。
- WEB 和 ADMIN 分别运行 Vite 开发服务器。
- 当前两个 Vite 配置都把 `/api` 代理到 `127.0.0.1:8123`
### 16.2 生产环境
```mermaid
flowchart LR
Internet["公网请求"] --> Nginx["Nginx :80/:443"]
Nginx -->|"静态文件"| Static["/opt/juyou_ai/static"]
Nginx -->|"API / Health"| Service["juyou_ai :8123"]
Service --> PG["本机 PostgreSQL"]
Service --> Redis["本机 Redis"]
```
- API 只监听 `127.0.0.1:8123`,不直接暴露公网。
- Nginx 提供 WEB 根路径和 `/admin/` 静态站点。
- `/api/``/health` 等请求反向代理到 Go API。
- systemd 服务名为 `juyou_ai`
- 环境文件位于 `/etc/juyou_ai/juyou_ai.env`
- Go 自身也具备静态文件托管能力,适合本地或没有 Nginx 的场景;生产环境主要由 Nginx 直接提供静态文件。
## 17. 配置边界
后端配置统一来自 `API/.env` 或生产环境文件,主要分为:
| 配置组 | 内容 |
| ---------- | ------------------------------------------------ |
| 应用 | 运行环境、Debug、监听地址、静态目录 |
| PostgreSQL | 主机、端口、库名、用户、密码或完整 URL |
| Redis | 主机、端口、密码、普通 DB、Asynq DB |
| JWT | 签名密钥和 Token 有效期 |
| 管理员引导 | 首次管理员账号和密码 |
| 密码哈希 | Argon2 参数 |
| 敏感配置 | 渠道密钥加密主密钥和版本 |
| COS | Region、Endpoint、Bucket、SecretId、SecretKey、公开域名、体积限制 |
| Worker | 并发、轮询间隔、HTTP 连接池、本地工具路径 |
| CORS | 允许访问 API 的前端来源 |
前端运行时不保存 COS SecretKey、渠道 API Key、数据库密码或 JWT Secret。浏览器只接收 API 输出的非敏感展示数据。
## 18. 测试与验证
### 18.1 后端测试
后端测试文件与实现同目录,覆盖计费、数据库基线、文本解析、Provider、Service、业务模块、Storage 和 Worker。`internal/modules/admin``internal/modules/prompt` 均包含模块级测试。运行:
```bash
cd API
go test ./...
```
涉及真实数据库的基线测试通过专用环境变量启用,默认不会连接生产数据库。
### 18.2 前端验证
两个前端的构建命令都会先执行 TypeScript/Vue 类型检查:
```bash
npm --prefix WEB run build
npm --prefix ADMIN run build
```
格式检查:
```bash
npm --prefix WEB run format:check
npm --prefix ADMIN run format:check
```
### 18.3 健康检查
```text
GET /health
GET /api/web/health
GET /api/admin/health
```
健康检查会同时验证 PostgreSQL 和 Redis 连接。