ecb74a2aed
后端:
- 异步生成: POST /api/v1/generate 立即返回 taskId,后台执行管线
- 任务轮询: GET /api/v1/tasks/:id + GET /api/v1/tasks/:id/assets
- 图片保存: 生成图片写入 ../generation/{projectId}/{taskId}/,静态服务
- 图片编辑: POST /api/v1/images/edit (multipart/form-data)
- JWT 中间件: mildware/auth.go 保护生成/编辑端点
- config.yml 清空敏感默认值,交由 .env 控制
- ImageGenConfig 新增 Quality 字段
前端:
- api/generate.ts: 对接真实 API (submitGenerate + poll getTask/getAssets)
- api/types.ts: 新增 GenerateResponse, AssetsResponse, Task 类型
- stores/generation.ts: 异步提交→轮询进度→获取素材→完成
- stores/task.ts: 默认分辨率 256→1024
- GenerateForm: 分辨率范围 1024-1536
- GeneratePage: 显示状态文本,完成后可查看结果/继续生成
- ResultPage: 从 store 读取,下载功能实现
7.9 KiB
Executable File
7.9 KiB
Executable File
数据存储
选型
| 用途 | 方案 | 说明 |
|---|---|---|
| 持久化存储 | SQLite 3 | 工程、任务、素材元数据、风格配置全部落库,单文件部署 |
| 文件存储 | 本地文件系统 | 生成的图片与 spritesheet 文件保存到本地目录,通过 Gin 静态文件服务访问 |
| 认证 | JWT + Cookie | 简单注册登录,httpOnly Cookie 鉴权 |
| 缓存 / 去重 | 应用层内存 | 请求去重通过应用层 sync.Map 实现短期去重窗口,无需外部依赖 |
选用 SQLite 的理由:gen2d 是单实例部署的工具型应用,SQLite 零配置、单文件、性能足够,无需额外数据库服务。
表结构
user — 用户
CREATE TABLE user (
id TEXT NOT NULL PRIMARY KEY, -- 用户 ID,如 user_a1B2c3
username TEXT NOT NULL,
email TEXT NOT NULL,
password_hash TEXT NOT NULL, -- bcrypt 哈希
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX uk_username ON user (username);
CREATE UNIQUE INDEX uk_email ON user (email);
project — 工程
CREATE TABLE project (
id TEXT NOT NULL PRIMARY KEY, -- 项目 ID,如 proj_abc123
user_id TEXT NOT NULL,
name TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
);
CREATE INDEX idx_project_user ON project (user_id, created_at);
project_style — 工程风格
工程级键值对,保证同一工程下所有素材风格一致。每个工程恰好一行。
CREATE TABLE project_style (
id TEXT NOT NULL PRIMARY KEY,
project_id TEXT NOT NULL,
kv_pairs TEXT NOT NULL, -- JSON: {"artStyle":"pixel","palette":"warm",...}
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX uk_style_project ON project_style (project_id);
task — 生成任务
CREATE TABLE task (
id TEXT NOT NULL PRIMARY KEY, -- 任务 ID,如 task_xyz789
project_id TEXT NOT NULL,
prompt TEXT NOT NULL, -- 用户原始文本
asset_type TEXT NOT NULL, -- sprite / background / ui / animation
task_style TEXT, -- JSON: 任务级风格覆盖,可为 NULL
params TEXT NOT NULL DEFAULT '{}', -- JSON: {"resolution":64,"frames":{...},"format":"spritesheet"}
status TEXT NOT NULL DEFAULT 'pending', -- pending / running / completed / failed
stage TEXT, -- 当前管线阶段
progress INTEGER NOT NULL DEFAULT 0, -- 0-100
retry_count INTEGER NOT NULL DEFAULT 0,
error TEXT, -- 失败原因
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE
);
CREATE INDEX idx_task_project ON task (project_id, created_at);
CREATE INDEX idx_task_status ON task (status);
asset — 生成素材
CREATE TABLE asset (
id TEXT NOT NULL PRIMARY KEY, -- 素材 ID,如 asset_001
task_id TEXT NOT NULL,
url TEXT NOT NULL, -- 相对路径,如 tasks/task_xyz/output/spritesheet.png
format TEXT NOT NULL DEFAULT 'png', -- png / json
width INTEGER NOT NULL DEFAULT 0,
height INTEGER NOT NULL DEFAULT 0,
metadata TEXT, -- JSON: {"frameWidth":64,"frameHeight":64,"frameCount":4,"directions":1}
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (task_id) REFERENCES task (id) ON DELETE CASCADE
);
CREATE INDEX idx_asset_task ON asset (task_id);
ER 关系
user 1 ── N project
project 1 ── 1 project_style
project 1 ── N task
task 1 ── N asset
对应 后端工程 中的关键实体定义,所有一对多关系通过外键 + CASCADE 删除维护。
ID 生成
使用前缀 + 随机字符串生成 ID,格式为 {prefix}_{random}:
| 实体 | 前缀 | 示例 |
|---|---|---|
| user | user |
user_a1B2c3 |
| project | proj |
proj_3kF9a2 |
| project_style | sty |
sty_7xM2pQ |
| task | task |
task_9bN4vR |
| asset | asset |
asset_2wK6tY |
随机部分使用 crypto/rand 生成 6 字节 base62 编码,兼顾可读性与唯一性。
文件存储
生成的素材文件保存到本地文件系统,通过 Gin 静态文件服务访问。
目录结构
{dataDir}/
└── users/
└── {userId}/
└── projects/
└── {projectId}/
└── tasks/
└── {taskId}/
├── raw/ # AssetGenerator 输出的原始图片
│ ├── 0.png
│ ├── 1.png
│ └── ...
├── output/ # FormatAdapter 输出的最终素材
│ ├── spritesheet.png
│ └── metadata.json
└── pipeline.log # 管线执行日志
dataDir 通过环境变量 GEN2D_DATA_DIR 配置,默认 ./data。
访问方式
Gin 静态文件服务,路由 /files/* 映射到 {dataDir}/ 目录:
router.Static("/files", dataDir)
asset 表的 url 字段
存储相对路径,前端通过 /files/{url} 拼接访问:
- 如
users/user_a1B2c3/projects/proj_abc/tasks/task_xyz/output/spritesheet.png - 前端拼接为
/files/users/user_a1B2c3/projects/proj_abc/tasks/task_xyz/output/spritesheet.png
去重策略
相同 prompt + assetType + params 的并发请求,通过应用层去重避免重复生成:
- 请求到达时,计算
hash(prompt + assetType + params)作为去重键。 - 应用内存(
sync.Map)中查找该键:- 若存在且任务仍在运行中,直接返回已有 taskId。
- 若不存在,写入内存并提交任务。
- 任务完成或失败后,从内存中清除(可设置 TTL 过期兜底)。
配置
数据库连接信息通过环境变量注入:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
GEN2D_SERVER_PORT |
8080 |
HTTP 服务监听端口 |
GEN2D_DB_PATH |
./data/gen2d.db |
SQLite 数据库文件路径 |
GEN2D_DATA_DIR |
./data |
本地文件存储根目录 |
GEN2D_JWT_SECRET |
— | JWT 签名密钥(必填) |
GEN2D_JWT_EXPIRE |
7200 |
Token 过期时间(秒) |
在 config.go 中扩展字段即可,无需额外依赖。
迁移
应用启动时自动执行建表 SQL,保证 schema 与代码版本一致:
// 启动时执行
db.Exec(createUserTableSQL)
db.Exec(createProjectTableSQL)
db.Exec(createTaskTableSQL)
db.Exec(createAssetTableSQL)
使用 CREATE TABLE IF NOT EXISTS 保证幂等性。
索引说明
| 表 | 索引 | 用途 |
|---|---|---|
user |
uk_username (username) |
用户名唯一约束 |
user |
uk_email (email) |
邮箱唯一约束 |
project |
idx_project_user (user_id, created_at) |
用户下工程列表分页查询 |
project_style |
uk_style_project (project_id) |
一个工程一个风格,唯一约束 |
task |
idx_task_project (project_id, created_at) |
工程下任务列表分页查询 |
task |
idx_task_status (status) |
按状态筛选任务 |
asset |
idx_asset_task (task_id) |
任务下素材列表查询 |