# 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 Icons；WEB 通过 `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["handler：HTTP 参数和响应"]
    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 连接。
