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

92 lines
7.7 KiB
Markdown
Raw Normal View History

---
name: 食谱百科食物图AI生成与OSS更新
overview: 对 v2_foods 表中 image 非阿里云 OSS 的记录,根据 name 调用 KieAI 生成图片(压缩至不超过 100KB上传到 OSS 并回写 v2_foods.image复用现有 DishImageServiceImpl 的 KieAI + 压缩 + OSS 能力,扩展“食物”场景并增加更新数据库的入口与触发方式。
todos: []
isProject: 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或只打日志不更新视产品要求而定。
-`[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`(若做按需或批量触发) |
## 流程概览
```mermaid
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
```