定位
pf-service 是图模工坊的单体后端中台——所有前端(pf-app / pf-editor / pf-manage)的业务请求都经过它;它也负责后台合成 Job 的调度、对外暴露 OpenAPI 文档。
- 运行时:Bun 1.x(启动比 Node 快、对 TS 零编译、原生 Web API)
- 框架:Elysia 1.4(路由 + 校验 + 中间件,TypeBox 驱动)
- ORM:Drizzle 0.45(类型安全 SQL builder)
- 数据库:MySQL 8.0
- 缓存:Redis 6.x
- 对象存储:MinIO(S3 协议)
- 鉴权:JWT (
@elysiajs/jwt) + Bearer Token (@elysiajs/bearer) - 端口:开发环境
3000
源码结构
1 | apps/pf-service/ |
启动流程
src/index.ts 装配应用:
1 | new Elysia() |
启动日志:
1 | ======================================== |
路由分区
后端路由按”调用方”分四区,避免不同前端的字段约定、鉴权策略串味:
| 前缀 | 调用方 | 文件位置 |
|---|---|---|
/pf-app/* |
终端用户 H5 | routes/projects/pf-app/ |
/pf-editor/* |
模板编辑器 | routes/projects/pf-editor/ |
/pf-manage/* |
管理后台 | routes/projects/pf-manage/ |
/shared/* |
跨端共用(OSS / 字体 / 背景 / face) | routes/projects/shared/ |
/docs |
Swagger UI | 框架自带 |
/health |
探活 | 入口 |
鉴权
JWT 中间件 plugins/auth.ts:
- 登录接口下发
accessToken(短 TTL)+refreshToken(长 TTL) - Bearer Token 解析(
@elysiajs/bearer) - 角色 / 权限校验在路由层
derive,不通过则返回 401/403 - 用户/角色/菜单表与 soybean-admin 标准结构对齐,方便后台直接接入
统一响应
utils/serviceResponse.ts 暴露:
1 | export const SERVICE_SUCCESS_CODE = "0000"; |
全局 onError 把抛出的 Error 转成结构化响应;NOT_FOUND 是约定常量。所有响应都附 timestamp。
数据访问
1 | import { db } from "../db/mysql"; |
- 软删强约束:所有查询带
isNull(deletedAt) - 事务:
await db.transaction(async (tx) => { ... }) - 迁移:
bun db:generate生成 SQL,bun db:push同步(dev)/ 手动审过迁移(prod)
后台 Job:自动合成
jobs/orderTemplateCompositionJob.ts 定时轮询 order_template_submissions:
- 每
intervalMs(默认 10s,可在config.server.jobs.composition.intervalMs调整)跑一轮 - 通过内部锁
isRunning防止并发重入 - 单条 submission 进入处理后整体加事务,明细级失败标记
failReason - 实际渲染走
utils/jobProcessor.ts→ Puppeteer
完整机制见 自动合成流水线。
对外集成
- MinIO:
db/s3.ts暴露上传/预签名/读取;前端直传走预签名 URL,后端合成产物走服务端 PutObject - Redis:缓存热点配置(字体/分类)、Job 状态、Puppeteer 进程信号
- face-service:
routes/projects/shared/faceRoutes.ts是个 thin proxy,统一前端入口;后端合成时也内部调用同一服务 - OpenAPI:
@elysiajs/swagger自动从 Elysia 路由 schema 生成/docs
开发指令
1 | cd apps/pf-service |
性能与限制
| 维度 | 现状 | 后续 |
|---|---|---|
| 启动 | < 1s(Bun + 单进程) | — |
| 单机并发 | 取决于 MySQL / Puppeteer 实例数 | 多进程 + 任务队列 |
| 合成吞吐 | ~ Puppeteer 实例数 × 张/秒 | 进程池 + GPU 渲染 |
| 文件上传 | 走 MinIO 预签名直传,绕开 Node 单点 | — |
| 鉴权 | JWT 单点,refresh token 续期 | 引入 SSO |