7.7 KiB
7.7 KiB
name, overview, todos, isProject
| name | overview | todos | isProject |
|---|---|---|---|
| 食谱百科食物图AI生成与OSS更新 | 对 v2_foods 表中 image 非阿里云 OSS 的记录,根据 name 调用 KieAI 生成图片(压缩至不超过 100KB),上传到 OSS 并回写 v2_foods.image;复用现有 DishImageServiceImpl 的 KieAI + 压缩 + OSS 能力,扩展“食物”场景并增加更新数据库的入口与触发方式。 | false |
食谱百科食物图 AI 生成并更新 v2_foods.image
现状
- 食谱百科前端:
[msh_single_uniapp/pages/tool/food-encyclopedia.vue](msh_single_uniapp/pages/tool/food-encyclopedia.vue)通过getFoodList/searchFood([api/tool.js](msh_single_uniapp/api/tool.js))请求tool/food/list、tool/food/search,列表项中的image直接来自后端。 - 后端数据:
[ToolFoodServiceImpl](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolFoodServiceImpl.java)从v2_foods查数据,返回的 map 包含image([V2Food](msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2Food.java)的image字段)。当前部分记录的image为 Figma/非 OSS 等非阿里云地址。 - 可复用能力:
[DishImageServiceImpl](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/DishImageServiceImpl.java)已实现:KieAI 文生图 → 下载图片 → 压缩至 100KB(MAX_IMAGE_BYTES = 100*1024、compressImageToMaxBytes)→ 上传 OSS(recipes/)→ 写V2DishImageCache。菜品 prompt 为“一道精美的中式菜品照片:{name}…”。
目标
- 对 v2_foods 中 image 非阿里云 OSS 的记录:根据 name 调用 AI 生成图片,生成图 不超过 100KB,上传到 OSS 后 更新 v2_foods.image 为 OSS 地址。
- 判断“非 OSS”:
image为空或image不包含当前项目使用的 OSS 域名(如配置中的alUploadUrl或固定包含aliyuncs.com)。
实现方案
1. 判断“是否为 OSS 地址”
- 在公共工具或 Service 内封装:若
image为空/blank,视为非 OSS;若不为空,则判断是否包含 OSS 域名(可从SystemConfigService.getValueByKey(SysConfigConstants.CONFIG_AL_UPLOAD_URL)取域名,或简单用image.contains("aliyuncs.com"))。满足则为 OSS,否则需要补图。
2. 后端:扩展“食物图片”能力并写回 v2_foods
方案 A(推荐):在现有 DishImageServiceImpl 上扩展
- 在
[DishImageServiceImpl](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/DishImageServiceImpl.java)中:- 增加 食物 用 prompt 方法,例如:
buildFoodImagePrompt(String foodName),内容为“一份新鲜的食物/食材照片:{name},高清静物摄影,白色背景或餐盘,自然光,细节清晰”(与菜品区分开)。 - 增加 食物 的 OSS 路径常量,如
OSS_FOODS_PATH = "foods/",上传时使用foods/food-{sanitizedName}-{timestamp}.jpg,与recipes/区分。 - 新增 公共流程:
generateImageAndUploadToOss(String name, String prompt, String ossPathPrefix)(或拆成:用现有 KieAI + 下载 +compressImageToMaxBytes(imageBytes, 100*1024)+ 上传,仅参数为 name/prompt/路径前缀),返回 OSS 完整 URL。 - 注入
V2FoodDao,新增方法:ensureFoodImageAndUpdateDb(Long foodId):- 根据 foodId 查
V2Food; - 若
image已是 OSS(用上述判断),直接返回当前 image; - 否则用
buildFoodImagePrompt(food.getName())调用上述“生成并上传”流程,得到 ossUrl; - 更新
food.setImage(ossUrl)并v2FoodDao.updateById(food); - 失败时可按现有逻辑降级为占位图并同样写回 DB,或只打日志不更新,视产品要求而定。
- 根据 foodId 查
- 增加 食物 用 prompt 方法,例如:
- 在
[DishImageService](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/tool/DishImageService.java)接口中声明ensureFoodImageAndUpdateDb(Long foodId)(或返回 String 的ensureFoodImageUrl(Long foodId)由调用方更新 DB,二选一即可)。
方案 B:新建 FoodImageService
- 新建
FoodImageService+FoodImageServiceImpl,内部注入DishImageService或直接复用 KieAI、Oss、压缩逻辑(若将 DishImageServiceImpl 中下载/压缩/上传抽成 package 内可复用方法,或抽到公共工具类)。 FoodImageServiceImpl.ensureFoodImageAndUpdateDb(Long foodId)查 v2_foods → 判断 image 非 OSS → 调 KieAI 生图 → 压缩 ≤100KB → 上传 OSS(路径foods/)→ 更新v2_foods.image。- 与方案 A 二选一即可;方案 A 复用更集中,改动面小。
3. 触发方式(可选其一或组合)
- 按需(列表/详情):在
[ToolFoodServiceImpl](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolFoodServiceImpl.java)的getList/search返回列表前,对每条记录的image判断;若非 OSS,可异步调用ensureFoodImageAndUpdateDb(food.getFoodId()),本次仍返回原 image,下次请求得到 OSS 地址;或同步调用(会拉长接口耗时,需权衡)。详情getDetail同理,对当前条若 image 非 OSS 可同步/异步补图并更新。 - 批量/管理端:在
[ToolController](msh_crmeb_22/crmeb-front/src/main/java/com/zbkj/front/controller/ToolController.java)或管理端增加接口,例如POST /api/front/tool/food/refresh-images或后台POST /api/admin/tool/food/refresh-images:查询v2_foods中image为空或非 OSS 的记录,循环调用ensureFoodImageAndUpdateDb(foodId),可限制单次条数(如 20)并返回处理数量,避免一次性跑全表。
4. 技术细节摘要
- 图片大小:沿用
DishImageServiceImpl的compressImageToMaxBytes(bytes, 100*1024L),保证上传前 ≤100KB。 - OSS 路径:使用
foods/food-{sanitizedName}-{timestamp}.jpg,与菜品recipes/dish-...区分,便于运维与排查。 - KieAI 配置:与现有菜品一致,使用
KieAIConfig、ToolKieAIService;未配置 Token 时可按现有逻辑降级(占位图或跳过更新)。 - 前端:无需改;列表/详情接口返回的
image更新为 OSS 后,[food-encyclopedia.vue](msh_single_uniapp/pages/tool/food-encyclopedia.vue)和[food-detail.vue](msh_single_uniapp/pages/tool/food-detail.vue)会自然展示新图。
5. 可选说明(不阻塞本次需求)
- 食谱百科列表跳详情当前为
id=${item.name},而后端getFoodDetail为Long id;若实际数据以 id 为主,建议前端改为传id=${item.id}并在详情用 id 请求,避免按 name 查的兼容逻辑。
涉及文件(建议)
| 类型 | 路径 |
|---|---|
| 接口 | msh_crmeb_22/crmeb-service/.../tool/DishImageService.java |
| 实现 | msh_crmeb_22/crmeb-service/.../tool/DishImageServiceImpl.java |
| 食物服务/控制器 | msh_crmeb_22/crmeb-service/.../tool/ToolFoodServiceImpl.java、msh_crmeb_22/crmeb-front/.../ToolController.java(若做按需或批量触发) |
流程概览
flowchart LR
A[v2_foods 记录] --> B{image 是否 OSS?}
B -->|是| C[直接使用]
B -->|否| D[buildFoodImagePrompt]
D --> E[KieAI 文生图]
E --> F[下载图片]
F --> G[压缩至 100KB]
G --> H[上传 OSS foods/]
H --> I[更新 v2_foods.image]
I --> C