开工第一天,我没急着写接口,也没去调大模型API,而是先把脑图摊开,把“指尖魔镜”这个产品从用户点开App到最终生成一张可用美甲效果图的完整链路盘了三遍。这个项目核心做两件事,一是帮用户把手部照片和美甲款式合成到一起,实时看效果;二是用AI按用户偏好生成新的美甲款式。听起来技术难点在图像合成和生成模型上,但真正决定后续开发顺不顺的,其实是Day1就要落地的业务数据模型。因为款式要管理、要审核、要上下架;AI生图这种耗时操作又不能同步等结果,必须走异步任务调度。如果这两块地基没打好,后面每接一个模块都是在豆腐上盖房。
这篇文章写给正在做AI应用、尤其是涉及“异步推理任务 + 业务资源管理”的后端开发者,也写给那些刚起步、面对一张空荡荡的数据库还不知道从哪下手的同学。我会以指尖魔镜的Day1为例,把业务数据模型的拆分过程、核心表的字段设计、状态机与任务调度之间的咬合关系,以及我实际踩过的坑全部摊开讲。
1. 开工之前,先把“指尖魔镜”的业务闭环盘清楚
1.1 产品闭环的三种触发链路
我习惯在写任何字段之前,先把产品会出现的“业务流”全部列出来,不列清楚就建表,后面一定返工。指尖魔镜当前能想到的闭环有三条。
第一条是用户主动选择款式做效果预览。用户上传一张手部照片,系统调用手部关键点检测模型,识别指甲位置和朝向;用户再从款式库里挑选某个美甲样式,后台将款式图像经过透视变换贴合到对应指甲上,生成一张合成图返回给前端。这条链路涉及“用户照片、款式、合成任务、合成结果”。
第二条是AI生成新款。用户用自然语言描述想要的风格,比如“奶白色底胶、贝壳碎片、无名指跳色”,后台把这些条件组装成提示词,交给图像生成模型处理,得到初稿后再进入人工或模型质量筛选,合格后入库成为新款式。这条链路比第一条更重,因为模型推理时间不可控,少则几秒,多则半分钟,不可能让HTTP请求一直挂着等返回,所以必须拆成任务异步处理。
第三条是运营端维护款式内容。美甲款式的视觉信息非常依赖图片素材,一个成品款式往往包含缩略图、官方展示大图、试戴效果示例图、AI设计过程稿等。运营需要上传、编辑、提交审核、上下架。素材不是简单的一张图,每个文件都有存储路径、尺寸、格式、MD5这些技术属性。
看完这三条链路,数据模型的方向就很明显了,系统核心不是订单、不是支付,而是“款式”和“任务”两个域。款式域负责整个视觉资源库的管理;任务域负责把所有耗时且可异步化的操作统一调度起来。
1.2 建模范围的边界:什么数据今天不该碰
我给自己画了一条边界,今天绝对不碰的至少有三块:用户账号体系只做到最基本的user_id关联,不做会员积分和用户偏好画像;支付、订单、售后这些电商能力完全留给后续独立服务;款式库存这种概念也不存在,美甲款式本身就是数字内容,不存在售罄问题。
没有边界意识,很容易一步到位建出二十张表,看着很完整,实际上每张表都充满猜测。AI项目迭代速度极快,你猜的订单字段大概率上线第三周就会改。建模应该是“用最小的核心空间装下确定性的业务”,不确定的用任务流和冗余字段兜住,等它长出来再说。
1.3 两个核心域的职责定义
款式管理域和任务调度域,边界一定要切干净。
款式管理域的职责是回答三个问题:一个款式是什么;它有哪些可视资源;它当前处于什么可见状态。它是纯业务资源的“内容库”,不关心这个款式是通过人工上传还是AI生成来的,也不需要知道自己是否正被某个合成任务引用。
任务调度域的职责是回答另外三个问题:系统里有哪些需要异步执行的动作;这些动作当前跑到哪一步了;如果失败,下一次重试在什么时候。它是系统的“流水线”,不应该关心任务结果具体是美甲图还是人脸图,而是把任何带“异步执行”属性的动作统一抽象成任务。
我见过最糟的设计,是把任务字段直接插到业务表里,比如给style表加一个generate_status、generate_task_id,某天想做视频生成再塞一堆video_xxx字段,表结构最终膨胀成不可维护的怪物。正确的做法是让任务域保持独立,通过业务类型和业务ID把你的款式表、请求记录、结果数据关联起来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据模型拆解:款式域与任务域的核心实体
2.1 style主表:一个款式到底存什么字段
第一版style主表我这样设计,字段刻意收敛,凡是可通过素材子表表达的都不放在主表里。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增,后续拆库再迁移为雪花生成 |
| style_code | varchar(32) | 款式的业务唯一编号,用于前端追踪 |
| name | varchar(128) | 款式名称 |
| description | varchar(1024) | 描述文案 |
| category_id | bigint | 分类ID,例如纯色、法式、猫眼、手绘 |
| cover_asset_id | bigint | 封面素材ID,冗余来源素材子表 |
| source_type | varchar(32) | 款式来源:运营上传、AI生成、合作方导入 |
| status | varchar(32) | 款式状态:草稿、待审核、已上架、已下架 |
| designer_id | bigint | 创建人/设计人ID,运营或AI标识 |
| deleted | tinyint | 逻辑删除标记 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 最后更新时间 |
| version | int | 乐观锁版本号 |
说说几个设计考虑。
style_code一定不能省。虽然id已经是唯一主键,但对外展示、埋
