92 lines
7.7 KiB
Markdown
92 lines
7.7 KiB
Markdown
---
|
||
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
|
||
```
|
||
|
||
|
||
|