Files
gen2d/docs/database.md
T
wonder 5a6bcaffaf docs: 新增用户认证、OSS 存储、异步任务设计
- database.md: 新增 user 表,project 关联 user_id,恢复 OSS 对象存储,
  移除 60 分钟文件过期,新增 JWT/OSS 环境变量配置
- api.md: 新增认证章节和错误码表,补充用户注册/登录/修改密码接口,
  工程管理新增列表/详情/删除接口,各接口补充错误响应说明
- async-tasks.md: 完善任务队列设计、Worker 流程、并发控制、管线进度、
  质检重试与任务级重试策略、进度推送方式
2026-05-24 10:38:31 +08:00

9.5 KiB
Raw Blame History

数据存储

选型

用途 方案 说明
持久化存储 MySQL 8.0+ 工程、任务、素材元数据、风格配置全部落库
文件存储 OSS 对象存储 生成的图片与 spritesheet 文件上传至 S3 兼容存储桶,通过 CDN URL 访问
认证 JWT 简单注册登录,Bearer Token 鉴权
缓存 / 去重 MySQL + 应用层内存 请求去重通过数据库唯一约束 + 应用层 sync.Map 实现短期去重窗口,不引入 Redis

不用 Redis 的理由:gen2d 是单实例部署的工具型应用,并发量有限,任务状态轮询即可满足实时性需求。MySQL 完全能覆盖缓存、去重、队列语义,引入 Redis 增加运维复杂度但收益不大。


表结构

user — 用户

CREATE TABLE `user` (
    `id`            CHAR(20)     NOT NULL,               -- 用户 ID,如 user_a1B2c3
    `username`      VARCHAR(64)  NOT NULL,               -- 登录用户名
    `email`         VARCHAR(128) NOT NULL,               -- 邮箱
    `password_hash` VARCHAR(128) NOT NULL,               -- bcrypt 哈希
    `created_at`    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_username` (`username`),
    UNIQUE KEY `uk_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

project — 工程

CREATE TABLE `project` (
    `id`         CHAR(20)     NOT NULL,             -- 项目 ID,如 proj_abc123
    `user_id`    CHAR(20)     NOT NULL,             -- 所属用户
    `name`       VARCHAR(128) NOT NULL,             -- 工程名称
    `created_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_user` (`user_id`, `created_at`),
    CONSTRAINT `fk_project_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

project_style — 工程风格

工程级键值对,保证同一工程下所有素材风格一致。每个工程恰好一行。

CREATE TABLE `project_style` (
    `id`         CHAR(20)     NOT NULL,
    `project_id` CHAR(20)     NOT NULL,
    `kv_pairs`   JSON         NOT NULL,             -- {"artStyle":"pixel","palette":"warm",...}
    `created_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project` (`project_id`),
    CONSTRAINT `fk_style_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

task — 生成任务

CREATE TABLE `task` (
    `id`           CHAR(20)     NOT NULL,              -- 任务 ID,如 task_xyz789
    `project_id`   CHAR(20)     NOT NULL,
    `prompt`       TEXT         NOT NULL,              -- 用户原始文本
    `asset_type`   VARCHAR(32)  NOT NULL,              -- sprite / background / ui / animation
    `task_style`   JSON         NULL,                  -- 任务级风格覆盖,可为 NULL
    `params`       JSON         NOT NULL DEFAULT '{}', -- {"resolution":64,"frames":{...},"format":"spritesheet"}
    `status`       VARCHAR(16)  NOT NULL DEFAULT 'pending',  -- pending / running / completed / failed
    `stage`        VARCHAR(32)  NULL,                  -- 当前管线阶段:prompt_builder / asset_generator / quality_supervisor / format_adapter
    `progress`     TINYINT UNSIGNED NOT NULL DEFAULT 0, -- 0-100
    `retry_count`  TINYINT UNSIGNED NOT NULL DEFAULT 0,
    `error`        TEXT         NULL,                  -- 失败原因
    `created_at`   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_project` (`project_id`, `created_at`),
    KEY `idx_status` (`status`),
    CONSTRAINT `fk_task_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

asset — 生成素材

CREATE TABLE `asset` (
    `id`          CHAR(20)     NOT NULL,               -- 素材 ID,如 asset_001
    `task_id`     CHAR(20)     NOT NULL,
    `url`         VARCHAR(512) NOT NULL,               -- OSS 完整 URL 或相对路径
    `format`      VARCHAR(16)  NOT NULL DEFAULT 'png', -- png / json
    `width`       INT          NOT NULL DEFAULT 0,
    `height`      INT          NOT NULL DEFAULT 0,
    `metadata`    JSON         NULL,                   -- {"frameWidth":64,"frameHeight":64,"frameCount":4,"directions":1}
    `created_at`  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_task` (`task_id`),
    CONSTRAINT `fk_asset_task` FOREIGN KEY (`task_id`) REFERENCES `task` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ER 关系

user    1 ── N project
project 1 ── 1 project_style
project 1 ── N task
task    1 ── N asset

对应 后端工程 中的关键实体定义,所有一对多关系通过外键 + CASCADE 删除维护。


ID 生成

使用 Sonyflake(或类似分布式 ID 方案)生成 20 字符的字符串 ID,格式为 {前缀}_{base62}:

实体 前缀 示例
user user user_a1B2c3D4
project proj proj_3kF9a2Bc
project_style sty sty_7xM2pQ1d
task task task_9bN4vR8e
asset asset asset_2wK6tY5f

文件存储

使用 S3 兼容对象存储(如阿里云 OSS、MinIO),用户生成的素材持久化保存,便于重复利用。

OSS Key 结构

{bucket}/
└── users/
    └── {userId}/
        └── projects/
            └── {projectId}/
                └── tasks/
                    └── {taskId}/
                        ├── raw/        # AssetGenerator 输出的原始图片
                        │   ├── 0.png
                        │   ├── 1.png
                        │   └── ...
                        ├── output/     # FormatAdapter 输出的最终素材
                        │   ├── spritesheet.png
                        │   └── metadata.json
                        └── pipeline.log  # 管线执行日志

访问方式

  • 开发环境:Gin 静态文件服务,路由 /files/* 映射到本地 data/ 目录,无需 OSS。
  • 生产环境:文件上传至 OSS 存储桶,asset.url 存储完整 CDN URL,前端直接访问。

asset 表的 url 字段

  • 开发环境:相对路径,如 projects/proj_abc/tasks/task_xyz/output/spritesheet.png,前端通过 /files/{url} 拼接。
  • 生产环境:完整 URL,如 https://cdn.example.com/users/user_xxx/projects/proj_abc/tasks/task_xyz/output/spritesheet.png。

去重策略

相同 prompt + assetType + params 的并发请求,通过应用层去重避免重复生成:

  1. 请求到达时,计算 hash(prompt + assetType + params) 作为去重键。
  2. 应用内存(sync.Map)中查找该键:
    • 若存在且任务仍在运行中,直接返回已有 taskId。
    • 若不存在,写入内存并提交任务。
  3. 任务完成或失败后,从内存中清除(可设置 TTL 过期兜底)。

不依赖数据库唯一约束做去重,因为相同输入在不同时间点应该允许重新生成。


配置

数据库连接信息通过环境变量注入:

环境变量 默认值 说明
GEN2D_DB_DSN root:@tcp(127.0.0.1:3306)/gen2d?charset=utf8mb4&parseTime=True&loc=Local MySQL DSN
GEN2D_DB_MAX_OPEN 25 最大连接数
GEN2D_DB_MAX_IDLE 5 最大空闲连接数
GEN2D_DB_MAX_LIFETIME 300 连接最大存活时间(秒)
GEN2D_JWT_SECRET — JWT 签名密钥(必填)
GEN2D_JWT_EXPIRE 7200 Token 过期时间(秒)
GEN2D_OSS_ENDPOINT — OSS Endpoint(生产环境必填)
GEN2D_OSS_BUCKET — 存储桶名称
GEN2D_OSS_ACCESS_KEY — Access Key ID
GEN2D_OSS_SECRET_KEY — Access Key Secret
GEN2D_OSS_CDN_DOMAIN — CDN 域名(可选,用于拼接公开访问 URL)

在 config.go 中扩展字段即可,无需额外依赖。


迁移

使用 golang-migrate 管理 schema 版本:

backend/internal/migrations/
├── 000001_init_user.up.sql
├── 000001_init_user.down.sql
├── 000002_init_project.up.sql
├── 000002_init_project.down.sql
├── 000003_init_task.up.sql
├── 000003_init_task.down.sql
├── 000004_init_asset.up.sql
└── 000004_init_asset.down.sql

启动时自动执行 migrate.Up(),保证 schema 与代码版本一致。


索引说明

表 索引 用途
user uk_username (username) 用户名唯一约束
user uk_email (email) 邮箱唯一约束
project idx_user (user_id, created_at) 用户下工程列表分页查询
project_style uk_project (project_id) 一个工程一个风格,唯一约束
task idx_project (project_id, created_at) 工程下任务列表分页查询
task idx_status (status) 按状态筛选任务(如查找所有 pending 任务)
asset idx_task (task_id) 任务下素材列表查询