Files
meijiaka-zy/docs/database-design.md
T

358 lines
12 KiB
Markdown
Raw 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.
# 美家卡智影 - 数据库设计规范
> 企业级统一数据库命名和设计规范,遵循 "轻量云 + 全本地业务数据" 架构设计。
---
## 一、整体设计原则
| 原则 | 说明 |
|------|------|
| **统一前缀** | 所有业务表统一使用 `mjk_` 前缀(美家卡全称缩写)|
| **无外键设计** | 不使用数据库外键约束,数据一致性由业务层保证,架构更简洁灵活 |
| **软删除优先** | 使用 `deleted_at` 时间戳做软删除,保留历史数据便于排查 |
| **全小写下划线** | 命名全小写,单词用下划线分隔 |
---
## 二、表命名规范
### 命名格式
```
mjk_{module}_{description}[_logs]
```
- `mjk_` - 统一项目前缀
- `module` - 业务模块名称
- `description` - 内容描述
- `_logs` 后缀 - 日志/统计类表(按事件增长)
### 示例
| 表名 | 说明 |
|------|------|
| `mjk_users` | 用户账户表 |
| `mjk_model_usage_logs` | AI 模型调用日志表 |
| `mjk_avatars` | 数字人名片表(已废弃,数据迁移到本地)|
| `mjk_interface_request_logs` | 接口请求记录表 |
---
## 三、字段命名规范
| 场景 | 规则 | 示例 |
|------|------|------|
| **主键** | 统一命名 `id`,类型 `BIGSERIAL` / `BIGINT` | `id BIGSERIAL PRIMARY KEY` |
| **外键引用** | 格式 `{referenced_table}_{primary_key}`,不要加前缀 | 引用 `mjk_users.id``user_id` |
| **布尔类型** | 前缀 `is_``has_` | `is_deleted`, `has_attachment` |
| **时间戳** | 后缀 `_at`,类型 `TIMESTAMP WITH TIME ZONE` | `created_at`, `updated_at`, `started_at`, `finished_at` |
| **状态字段** | 字段名固定 `status`,类型 `VARCHAR(N)`,存储枚举字符串 | `status VARCHAR(20) NOT NULL` |
| **软删除** | 字段名 `deleted_at`,允许 `NULL``NULL` 表示未删除 | `deleted_at TIMESTAMP WITH TIME ZONE` |
---
## 四、约束与索引命名规范
| 对象 | 命名格式 | 示例 |
|------|---------|------|
| **主键** | `{table_name}_pkey`PostgreSQL 默认) | `mjk_interface_request_logs_pkey` |
| **唯一约束** | `uk_{table_name}_{column_list}` | `uk_mjk_interface_request_logs_request_id` |
| **普通索引** | `idx_{table_name}_{column_list}` | `idx_mjk_interface_request_logs_user_id` |
---
## 五、所有业务表结构
---
### 1. `mjk_users` - 用户基本信息表
存储用户基本认证信息,云端只存账户,不存业务数据。
```sql
CREATE TABLE mjk_users (
id BIGSERIAL PRIMARY KEY,
mobile VARCHAR(20) UNIQUE NOT NULL,
nickname VARCHAR(64),
avatar_url TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE UNIQUE INDEX idx_mjk_users_mobile ON mjk_users(mobile);
```
**字段说明**
| 字段 | 说明 |
|------|------|
| `id` | 用户唯一ID |
| `mobile` | 手机号(登录账号,唯一)|
| `nickname` | 用户昵称 |
| `avatar_url` | 头像URL |
| `created_at` | 创建时间 |
| `updated_at` | 最后更新时间 |
---
### 2. `mjk_user_credits` - 用户积分账户记录表
记录用户积分账户的所有变动(充值、消费),每个变动一条记录。
```sql
CREATE TABLE mjk_user_credits (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
change_type VARCHAR(20) NOT NULL, -- recharge / consume
change_credits INTEGER NOT NULL, -- 变动积分数(充值正,消费负)
balance_before INTEGER NOT NULL, -- 变动前余额
balance_after INTEGER NOT NULL, -- 变动后余额
interface_type VARCHAR(50), -- 消费接口类型(消费时才有)
request_id VARCHAR(64), -- 关联接口请求ID
remark VARCHAR(200), -- 备注(充值订单号等)
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_mjk_user_credits_user_id ON mjk_user_credits(user_id);
CREATE INDEX idx_mjk_user_credits_created_at ON mjk_user_credits(created_at);
CREATE INDEX idx_mjk_user_credits_change_type ON mjk_user_credits(change_type);
```
**字段说明**
| 字段 | 说明 |
|------|------|
| `id` | 记录ID |
| `user_id` | 关联用户ID |
| `change_type` | 变动类型:`recharge`(充值) / `consume`(消费) |
| `change_credits` | 变动积分,充值为正,消费为负 |
| `balance_before` | 变动前积分余额 |
| `balance_after` | 变动后积分余额 |
| `interface_type` | 消费接口类型(仅消费时有)|
| `request_id` | 关联接口请求ID(可用于追溯)|
| `remark` | 备注,充值时存订单号 |
| `created_at` | 变动时间 |
**余额计算**:用户当前余额 = `sum(change_credits)`,可以随时计算,也可以在用户表存冗余字段加速查询。
---
### 3. `mjk_model_usage_logs` - AI 模型调用日志表
记录每一次 AI 模型调用,用于成本统计和监控。
```sql
CREATE TABLE mjk_model_usage_logs (
id BIGSERIAL PRIMARY KEY,
model_id VARCHAR(100) NOT NULL,
platform_id VARCHAR(50) NOT NULL,
task_type VARCHAR(50) NOT NULL,
prompt_tokens INTEGER NOT NULL DEFAULT 0,
completion_tokens INTEGER NOT NULL DEFAULT 0,
total_tokens INTEGER NOT NULL DEFAULT 0,
cost_cny FLOAT NOT NULL DEFAULT 0.0,
response_time_ms INTEGER,
success BOOLEAN NOT NULL DEFAULT TRUE,
error_message TEXT,
user_id VARCHAR(50),
project_id VARCHAR(50),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_mjk_model_usage_logs_user_id ON mjk_model_usage_logs(user_id);
CREATE INDEX idx_mjk_model_usage_logs_created_at ON mjk_model_usage_logs(created_at);
```
**字段说明**
- `model_id` - AI 模型ID
- `platform_id` - AI 平台IDopenai/volcengine/klingai 等)
- `task_type` - 任务类型(script/polish/chat 等)
- `prompt_tokens` - 输入 Token 数
- `completion_tokens` - 输出 Token 数
- `total_tokens` - 总 Token 数
- `cost_cny` - 消耗金额(人民币元)
- `response_time_ms` - 响应时间(毫秒)
- `success` - 是否成功
- `error_message` - 错误信息
- `user_id` - 关联用户ID
- `project_id` - 关联项目ID
- `created_at` - 创建时间
---
### 4. `mjk_interface_request_logs` - 接口请求记录表(新增)
**按后端接口类型记录所有用户请求,统计积分消耗**。这张表是顶层的接口请求统计,每一次前端调用后端接口都记一条。
```sql
CREATE TABLE mjk_interface_request_logs (
id BIGSERIAL PRIMARY KEY,
request_id VARCHAR(64) NOT NULL,
user_id VARCHAR(50) NOT NULL,
interface_type VARCHAR(50) NOT NULL,
interface_name VARCHAR(100),
status VARCHAR(20) NOT NULL,
cost_credits INTEGER NOT NULL DEFAULT 0,
started_at TIMESTAMP WITH TIME ZONE NOT NULL,
finished_at TIMESTAMP WITH TIME ZONE,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 唯一约束
ALTER TABLE mjk_interface_request_logs
ADD CONSTRAINT uk_mjk_interface_request_logs_request_id
UNIQUE (request_id);
-- 索引
CREATE INDEX idx_mjk_interface_request_logs_user_id
ON mjk_interface_request_logs(user_id);
CREATE INDEX idx_mjk_interface_request_logs_interface_type
ON mjk_interface_request_logs(interface_type);
CREATE INDEX idx_mjk_interface_request_logs_status
ON mjk_interface_request_logs(status);
CREATE INDEX idx_mjk_interface_request_logs_created_at
ON mjk_interface_request_logs(created_at);
```
**字段说明**
| 字段 | 说明 |
|------|------|
| `id` | 日志记录自增ID |
| `request_id` | 本次请求唯一ID(全局唯一)|
| `user_id` | 请求用户ID |
| `interface_type` | 接口类型(枚举见下方)|
| `interface_name` | 接口名称(可读描述)|
| `status` | 请求状态:`success` / `failed` |
| `cost_credits` | 消耗积分数 |
| `started_at` | 请求开始时间 |
| `finished_at` | 请求结束时间 |
| `error_message` | 失败原因 |
| `created_at` | 记录创建时间 |
**`interface_type` 枚举值**
| 值 | 说明 |
|----|------|
| `script_generate` | 脚本生成 |
| `script_polish` | 脚本润色 |
| `avatar_clone` | 数字人克隆 |
| `video_generate` | 数字人视频生成 |
| `subtitle_generate` | 字幕打轴生成 |
| `image_generate` | 封面图片生成 |
---
### 5. `mjk_avatars` - 数字人名片表(已废弃)
> **迁移计划**:原 `avatars` 表已废弃,所有数字人元数据全量迁移到用户本地存储。
> 路径:`~/Documents/Meijiaka/avatars/{avatar_id}/meta.json`
保留本表仅用于存量数据兼容,后续可删除。
```sql
CREATE TABLE mjk_avatars (
id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
name VARCHAR(64) NOT NULL,
voice_id VARCHAR(64),
element_id BIGINT,
voice_task_id VARCHAR(128),
element_task_id VARCHAR(128),
video_url TEXT NOT NULL,
trial_url TEXT,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
fail_reason TEXT,
deleted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
);
-- 索引
CREATE INDEX idx_mjk_avatars_user_id ON mjk_avatars(user_id);
CREATE INDEX idx_mjk_avatars_voice_task_id ON mjk_avatars(voice_task_id);
CREATE INDEX idx_mjk_avatars_element_task_id ON mjk_avatars(element_task_id);
```
---
## 六、本地存储结构(业务数据)
所有业务数据(项目、脚本、数字人)都存在用户本地磁盘,云端只存储日志和统计:
```
~/Documents/Meijiaka/
├── config.json # 全局应用配置
├── projects/
│ └── {project_id}/
│ ├── meta.json # 项目元数据
│ ├── segments.json # 脚本/分镜数据
│ └── assets/ # 媒体文件
├── avatars/
│ └── {avatar_id}/
│ ├── meta.json # 数字人元数据(id/name/voice_id/element_id/status 等)
│ └── source.mp4 # 原始上传视频
└── cache/ # 临时文件
```
**`avatars/{avatar_id}/meta.json` 结构**
```json
{
"id": "avt_xxx",
"name": "我的数字人",
"voiceId": "kling-voice-id",
"elementId": 12345678,
"voiceTaskId": "kling-task-id",
"elementTaskId": "kling-task-id",
"videoUrl": "https://.../source.mp4",
"trialUrl": "https://.../trial.wav",
"status": "succeed",
"failReason": null,
"createdAt": "2026-04-16T10:00:00Z",
"updatedAt": "2026-04-16T10:05:00Z"
}
```
---
## 七、架构总结
| 数据类型 | 存储位置 | 说明 |
|---------|----------|------|
| 用户基本信息 | 云端 `mjk_users` | 必须存云端 |
| 用户积分变动记录 | 云端 `mjk_user_credits` | 记录充值/消费流水,统计用户余额 |
| AI 模型调用日志 | 云端 `mjk_model_usage_logs` | AI 模型细粒度调用日志(成本统计)|
| 接口请求记录 | 云端 `mjk_interface_request_logs` | 按后端接口记录请求、状态、消耗积分 |
| 项目/脚本/分镜 | 用户本地 JSON | 全本地业务数据 |
| 数字人元数据/原始视频 | 用户本地文件 | 全本地业务数据(原云端表已废弃)|
| 合成输出视频 | 用户本地文件 | 全本地 |
完美符合设计理念:**轻量云账号 + 全本地业务数据**。
---
## 八、迁移说明
### 从无前缀版本迁移到统一前缀版本
1. 使用 Alembic 自动重命名所有现有表
```sql
ALTER TABLE users RENAME TO mjk_users;
ALTER TABLE model_usage_logs RENAME TO mjk_model_usage_logs;
ALTER TABLE avatars RENAME TO mjk_avatars;
```
2. 新建两张表:
- `mjk_user_credits` - 用户积分变动记录表
- `mjk_interface_request_logs` - 接口请求记录表
3. 修改所有 SQLAlchemy 模型中的 `__tablename__`
4. 后续:将 `mjk_avatars` 数据迁移到用户本地后可删除该表
---
*版本:v1.1*
*创建日期:2026-04-16*
*更新:新增 `mjk_user_credits` 积分账户表*