# 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 连接。