Files
msh-system/.cursor/plans/食谱百科食物图ai生成与oss更新_b5228ab9.plan.md

7.7 KiB
Raw Blame History

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/listtool/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 文生图 → 下载图片 → 压缩至 100KBMAX_IMAGE_BYTES = 100*1024compressImageToMaxBytes)→ 上传 OSSrecipes/)→ 写 V2DishImageCache。菜品 prompt 为“一道精美的中式菜品照片:{name}…”。

目标

  • v2_foodsimage 非阿里云 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或只打日志不更新视产品要求而定。
  • [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_foodsimage 为空或非 OSS 的记录,循环调用 ensureFoodImageAndUpdateDb(foodId),可限制单次条数(如 20并返回处理数量避免一次性跑全表。

4. 技术细节摘要

  • 图片大小:沿用 DishImageServiceImplcompressImageToMaxBytes(bytes, 100*1024L),保证上传前 ≤100KB。
  • OSS 路径:使用 foods/food-{sanitizedName}-{timestamp}.jpg,与菜品 recipes/dish-... 区分,便于运维与排查。
  • KieAI 配置:与现有菜品一致,使用 KieAIConfigToolKieAIService;未配置 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},而后端 getFoodDetailLong 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.javamsh_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