整体架构

图模工坊由 5 个相互独立的服务/应用 组成,前端三端 + 后端两端,按业务边界拆分,可单机起,也可分布式部署。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    ┌──────────────────────────────────────┐
│ 终端用户 / 管理员 / 运营 │
└──────┬──────────────┬────────────┬───┘
│ H5 │ PC Web │ PC Web
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌─────────────┐
│ pf-app │ │ pf-editor │ │ pf-manage │
│ Quasar H5 │ │ Quasar PC │ │ Naive UI │
│ 端口 8000 │ │ 端口 9000 │ │ 端口 9527 │
└──────┬───────┘ └──────┬───────┘ └──────┬──────┘
│ HTTP / Token │ HTTP / Token │ HTTP / Token
└────────────────┼────────────────┘

┌────────────────────────────┐
│ pf-service │
│ Bun + Elysia (TS) │
│ 端口 3000 │
│ ┌──────────────────────┐ │
│ │ Routes: │ │
│ │ /pf-manage/* │ │
│ │ /pf-editor/* │ │
│ │ /pf-app/* │ │
│ │ /shared/* │ │
│ ├──────────────────────┤ │
│ │ Jobs: │ │
│ │ orderTemplate- │ │
│ │ CompositionJob │ │
│ └──────────────────────┘ │
└──┬──────┬──────┬────────┬──┘
HTTP /detect │ │ │ │
▼ ▼ ▼ ▼
┌─────────────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐
│ pf-face-service │ │ MySQL │ │ Redis │ │ MinIO │
│ FastAPI/SCRFD │ │ 8.0 + │ │ 6.x │ │ S3 兼容 │
│ 端口 3010 │ │ Drizzle │ │ │ │ 对象存储 │
└─────────────────┘ └──────────┘ └──────────┘ └─────────────┘

组件职责矩阵

组件 角色 进程/技术 主要协议 默认端口
pf-app 终端用户 H5:选模板、传图、编辑、提交 Quasar / Vue 3.5 HTTP 8000
pf-editor 模板编辑器:拖拽出版、编辑 sceneJson Quasar / Vue 3.5 + Leafer HTTP 9000
pf-manage 管理后台:用户/角色/订单/模板/审核 Naive UI / soybean-admin HTTP 9527
pf-service 后端中台:路由、鉴权、合成任务调度 Bun + Elysia + Drizzle HTTP / JWT 3000
pf-face-service 人脸框检测(避脸裁剪) Python 3.12 + FastAPI + InsightFace HTTP 3010
MySQL 业务数据持久化 MySQL 8.0 TCP 3306
Redis 缓存、合成任务状态 Redis 6.x TCP 6379
MinIO 原始素材 / 模板预览 / 合成成品 S3 兼容 HTTP 1280

后端路由分区

pf-service 按”调用方”做命名分区,避免不同端点鉴权策略与字段命名约定混在一起:

1
2
3
4
5
src/routes/projects/
├── pf-app/ # 用户端接口(orderTemplate 提交、订单查询...)
├── pf-editor/ # 编辑器接口(模板创建、模板素材、素材库读取...)
├── pf-manage/ # 管理后台接口(auth/users/roles/menus/orders/审核/相册管理...)
└── shared/ # 跨端共用(OSS 上传、字体库、背景库、人脸检测代理)

后端入口在 apps/pf-service/src/index.ts,按顺序注册各端路由 + Swagger 文档(/docs)。CORS、统一错误响应(SERVICE_ERROR_CODE)、JWT 鉴权(@elysiajs/jwt + @elysiajs/bearer)、日志插件均挂载在应用层。

五条主数据流

1. 模板配置流(管理员/设计师)

1
2
3
4
5
6
pf-editor 拖拽出版
→ 序列化为 sceneJson(Leafer 节点树)
→ POST /pf-editor/templates
→ templates 表落库
→ 关联 album_templates / category / tags
→ 上架后用户端 pf-app 可见

2. 素材库流(管理员/运营)

1
2
3
4
5
6
pf-manage 上传背景 / 字体
→ POST /shared/oss/upload ← S3 预签名 / 直传 MinIO
→ POST /shared/background-library
→ POST /shared/font-library
→ 全局 (userId=0) 或个人素材入库
→ 用户端 / 编辑器侧均可拉取

3. 用户传图与提交流(终端用户)

1
2
3
4
5
6
7
8
pf-app 选相册/模板
→ 分片上传素材到 MinIO(前端裁剪/旋转后再传)
→ 可选:POST /shared/face/detect 获取人脸框 → 自动避脸裁剪
→ 用户编辑文字、调整位置
→ POST /pf-app/order-template-submissions
body: { orderNo, items: [{ templateId, userUploads, userTexts, sceneJson }] }
→ 入 order_template_submissions / order_template_submission_items
→ 状态 = pending

4. 自动合成流(后台 Job)

1
2
3
4
5
6
7
8
9
10
11
orderTemplateCompositionJob (每 N 秒轮询)
→ 取一条 status=pending 的 submission
→ 标记 processing
→ 遍历明细 item:
合并 sceneJson + userUploads + userTexts
→ 启动/复用 Puppeteer 实例
→ 把场景渲染到 headless Chrome 页面
→ 截图 / 导出 PNG/PDF
→ 上传 MinIO 得到 composedImageUrls
→ 全部成功 → submission.status = completed
→ 任一失败 → item.failReason 记录 / submission.status = failed

5. 审核与交付流(管理员)

1
2
3
4
pf-manage 列表查看 status=completed 的 submission
→ 预览 composedImageUrls
→ 审核通过:标记可下载 / 推送至打印队列
→ 审核驳回:回写状态 → 通知用户端 → 用户重新提交

端口与协议矩阵

目标 协议 端口 用途
用户浏览器 pf-app HTTP 8000 H5 静态资源
管理员浏览器 pf-editor HTTP 9000 编辑器静态资源
管理员浏览器 pf-manage HTTP 9527 后台静态资源
三端前端 pf-service HTTP/JWT 3000 业务 API
任意 pf-service HTTP 3000 /docs Swagger、/health
pf-service pf-face-service HTTP 3010 POST /api/face/detect
pf-service MySQL TCP 3306 Drizzle ORM
pf-service Redis TCP 6379 缓存 / 状态
pf-service / 前端 MinIO HTTP 1280 素材 / 成品对象存储

生产环境请将 pf-service 与 pf-face-service 置于内网;前端三端通过反向代理(Nginx / Caddy)统一对外,参见 部署

前端分层(pf-app / pf-editor)

两个 Quasar 应用源码结构高度对称,便于复用:

1
2
3
4
5
6
7
8
9
10
src/
├── pages/ # 路由页面(IndexPage、AlbumTemplatesPage、TemplateListPage...)
├── layouts/ # 布局
├── components/ # 业务组件(ImageCropper, ImagePreview, TemplateCard, workshop/*)
├── composables/ # 组合式函数(编辑器 useXxx, fontPreload)
├── stores/ # Pinia(pinia-plugin-persistedstate 持久化)
├── api/ # 与 pf-service 的 HTTP 调用
├── boot/ # Quasar boot 文件(i18n、axios、router 守卫)
├── i18n/ # 国际化
└── utils/ # 通用工具

渲染引擎是 Leafer 2.x——同一份 sceneJsonpf-editor 用于交互编辑,在 pf-app 用于预览,在 pf-service 用 Puppeteer 启动一个最小的 Leafer 页面用于合成。

资源解耦

各组件之间无强耦合,可按需替换:

想换什么 怎么做
对象存储 MinIO 换 S3 / OSS / COS,改 apps/pf-service/config.jsons3
数据库 Drizzle 抽象层支持 MySQL / Postgres / SQLite,改 db/mysql.tsdrizzle.config.ts
人脸模型 替换 apps/pf-face-service/models/*.onnx,或换实现(保留 /api/face/detect 协议)
合成引擎 utils/jobProcessor.ts 的 Puppeteer 路径换成 sharp / canvas / 远程 GPU 服务
渲染引擎 Leafer 可换 Konva / Fabric,只要前后端共用同一份 sceneJson 序列化协议
UI 框架 移除 Quasar,复用 packages/* 共享层接入任意 Vue 应用

下一步