概览

后端 Drizzle Schema 位于 apps/pf-service/src/db/schema.ts,约 300 行、20+ 张表。所有表共享 baseFields

1
2
3
4
5
6
7
const baseFields = {
createdBy: int().default(0),
createdAt: datetime().default(new Date()),
updatedBy: int().default(0),
updatedAt: datetime().default(new Date()),
deletedAt: datetime(), // 软删除
};

软删除(deletedAt)覆盖所有业务表;查询时必带 isNull(table.deletedAt)

三大业务域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐
│ 模板域 │ │ 订单域 │ │ 素材库域 │
│ (templates) │ │ (orders) │ │ (background_*) │
│ │ │ │ │ (font_variants) │
│ — 描述"能定制 │ │ — 描述"用户买了 │ │ — 描述"能用的 │
│ 出什么" │ │ 什么 + 交付什么"│ │ 元素有什么" │
└──────────┬─────────┘ └──────────┬─────────┘ └────────────────────┘
│ N ──────── M │ 1 ───────── N
│ album_templates │ order_template_submissions
▼ ▼
┌──────┐ ┌───────────────────────────┐
│albums│ │ order_template_submission │
└──────┘ │ _items(每模板一条) │
└───────────────────────────┘

表关系图(核心)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
albums ─┬─< album_size_relations >── album_sizes
├─< album_page_count_relations >── album_page_counts
└─< album_templates >── templates

│ templates.sceneJson (Leafer 场景树)
│ templates.width/height
│ templates.templateType


orders ──< order_templates >── templates
orders ──< order_albums >── albums
orders ──< order_template_submissions
│ 1

N
order_template_submission_items
├── templateId
├── sceneJson (提交时的快照)
├── userUploads (用户上传素材)
├── userTexts (用户填入文字)
└── composedImageUrls (合成产物)

关键表速查

templates — 模板表

字段 类型 说明
id serial 主键
userId int 所属用户/管理员
name varchar(100) 模板名
categoryId int 分类(名片/海报/相册/证件照…)
templateType varchar(10) 模板类型
width / height int 物理尺寸(mm 或 px)
sceneJson json 核心字段:Leafer 场景树描述,可编辑/合成共用
previewUrl varchar(500) 缩略图 URL
status enum normal / disabled

sceneJson 是整个项目的”协议中枢”——编辑器写、用户端读、合成器消费。结构示例:

1
2
3
4
5
6
7
8
9
{
"children": [
{ "tag": "Rect", "x": 0, "y": 0, "width": 800, "height": 600, "fill": "#fff" },
{ "tag": "Image", "x": 50, "y": 50, "width": 400, "height": 400,
"editable": true, "placeholder": "请上传图片", "faceSafe": true },
{ "tag": "Text", "x": 50, "y": 500, "text": "在此处编辑文字",
"editable": true, "fontFamily": "SourceHanSans" }
]
}

相册体系(albums + 关联表)

相册是”多模板成册”的一等公民,独立于普通模板:

  • albums — 相册定义
  • album_sizes — 可选尺寸(如 6×8 寸、8×12 寸)
  • album_page_counts — 可选 P 数(20P / 40P / 60P)
  • album_size_relations — 相册可选哪些尺寸
  • album_page_count_relations — 相册可选哪些 P 数
  • album_templates — 相册由哪些模板按 relationType ∈ {inner, cover, back_cover} 组成,含 sort

用户在 pf-app 选一本相册 + 一个尺寸 + 一个 P 数后,系统按 album_templates 展开成具体模板列表,逐张套用模板。

订单与提交(orders + 提交主/明细表)

1
2
3
4
5
orders                            ← 业务订单(含金额、商品、相册尺寸/P数、过期时间)
├ order_templates ← 一个订单包含哪些模板(购物车视角)
├ order_albums ← 一个订单包含哪些相册
└ order_template_submissions ← 用户每次"提交合成"产生一条
└ order_template_submission_items ← 每张模板一条提交明细

order_template_submissionsorder_template_submission_itemsstatus 都是 pending / processing / completed / failed 四态——这是后台合成 Job 的工作台账,支持任意一张图失败而不影响其它图重试,也支持失败原因 failReason 字段定位问题。

提交明细的快照字段

order_template_submission_items 中存了三份快照:

字段 类型 说明
sceneJson json 提交时刻的模板场景树(防止管理员后续改模板影响历史合成)
userUploads json 用户上传素材:{ instanceKey: { elementKey: { url } } }
userTexts json 用户填入文字:{ instanceKey: { elementKey: { text } } }
composedImageUrls json string[] 合成产物 URL 列表

为什么要按 instanceKey + elementKey 二级索引? 因为同一张模板可能被同一个订单引用多次(templateNum > 1),用户给每”次”实例分别上传素材;元素 key 则对应 sceneJson 中可编辑节点的 id。

素材库

  • background_categories — 背景分类
  • background_assets — 背景图(userId=0 表示全局素材;否则为个人)
  • font_variants — 字体文件(ttf/otf/woff/woff2)

设计上素材与模板解耦,方便统一管理版权与体量;模板里引用素材时只存 URL,不存二进制。

字段约定

  • 主键serial 自增 ID
  • 外键:仅在 schema 注释中体现,不建物理外键约束(保留迁移弹性)
  • 金额int 存最小货币单位(分)
  • 状态mysqlEnum,业务无关字段统一 normal / disabled,业务相关字段用专用枚举
  • JSONjson() 列承载 sceneJson、用户上传、文字、合成结果列表,配合 $type<T>() 标注 TS 类型
  • 审计baseFields 强制每张表都有 created/updated/deleted

索引策略

业务热点路径都建了索引,避免线性扫表:

1
2
3
4
5
6
7
8
9
10
album_sizes        — uk(name), idx(status)
album_page_counts — uk(page_count), uk(name), idx(status)
albums — idx(status)
album_size_relations — uk(album_id, size_id), idx(album_id)
album_page_count_relations — uk(album_id, page_count_id), idx(album_id)
album_templates — uk(album_id, template_id), idx(album_id), idx(template_id)
background_* — idx(category), idx(user_id), idx(status)
font_variants — idx(status)
order_template_submissions — idx(order_id)
order_template_submission_items — idx(order_id, template_id), idx(submission_id), idx(album_id)

迁移与种子

1
2
3
4
cd apps/pf-service
bun db:generate # 生成迁移 SQL(drizzle-kit generate)
bun db:push # 推送 schema 到目标库(开发环境)
bun db:studio # Drizzle Studio 可视化

种子数据见 apps/pf-service/src/db/seed.ts——首次拉起本地环境时建议先跑一次,会带上默认管理员、若干分类、几套样板模板。

下一步