Files
msh-system/.cursor/plans/计算器结果保存食谱_f149b3dc.plan.md

167 lines
7.1 KiB
Markdown
Raw Normal View History

---
name: 计算器结果保存食谱
overview: 在用户采纳营养计划时,将食谱计算器的配餐方案提取并保存到 v2_recipes 食谱表,并在首页"精选食谱"中混合展示用户自己的食谱(排在前面)。
todos:
- id: db-migration
content: 新建 SQL 变更文件v2_recipes 表新增 source 和 source_id 字段
status: completed
- id: entity-update
content: V2Recipe.java 实体类新增 source 和 sourceId 属性
status: completed
- id: adopt-save-recipe
content: ToolCalculatorServiceImpl.adopt() 中新增保存食谱到 v2_recipes 的逻辑(含幂等检查)
status: completed
- id: home-mix-recipes
content: ToolHomeServiceImpl.getRecommendedRecipes() 混入当前用户的计算器食谱
status: completed
- id: home-return-fields
content: 推荐食谱接口返回字段补充 description、source、totalProtein
status: completed
- id: frontend-recipe-card
content: 首页 index.vue 食谱卡片展示适配tag、desc 等字段映射)
status: completed
isProject: false
---
# 食谱计算器结果保存到食谱表
## 整体流程
```mermaid
sequenceDiagram
participant User as 用户
participant FE as 前端
participant Ctrl as ToolController
participant CalcSvc as ToolCalculatorServiceImpl
participant RecipeDao as V2RecipeDao
participant HomeSvc as ToolHomeServiceImpl
User->>FE: 点击"采纳计划"
FE->>Ctrl: POST /calculator/adopt
Ctrl->>CalcSvc: adopt(resultId)
CalcSvc->>CalcSvc: 创建营养计划 (已有逻辑)
CalcSvc->>RecipeDao: 保存一条食谱到 v2_recipes
CalcSvc-->>FE: 返回 adoptResponse
Note over FE: 首页刷新时
FE->>Ctrl: GET /home/recipes
Ctrl->>HomeSvc: getRecommendedRecipes()
HomeSvc->>RecipeDao: 查询推荐食谱 + 用户自己的食谱
HomeSvc-->>FE: 返回混合列表
```
## 1. 数据库变更 - v2_recipes 新增字段
需要新增两个字段以追踪食谱来源:
```sql
ALTER TABLE v2_recipes
ADD COLUMN source VARCHAR(20) DEFAULT 'manual' COMMENT '来源manual(手动)/calculator(计算器)/ai(AI生成)' AFTER sort_order,
ADD COLUMN source_id BIGINT DEFAULT NULL COMMENT '来源ID如计算器结果ID' AFTER source;
```
同时新增一个变更 SQL 文件记录此次 DDL。
## 2. 后端 - 实体类更新
**文件**: [V2Recipe.java](msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2Recipe.java)
新增两个字段:
```java
@ApiModelProperty(value = "来源manual/calculator/ai")
private String source;
@ApiModelProperty(value = "来源ID计算器结果ID等")
private Long sourceId;
```
## 3. 后端 - 采纳时保存食谱
**文件**: [ToolCalculatorServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCalculatorServiceImpl.java)
`adopt()` 方法中,步骤 5创建营养计划之后新增步骤"保存食谱到 v2_recipes"
- 注入 `V2RecipeDao`
-`V2CalculatorResult` 中解析 `mealPlanJson` 得到 MealPlan
- 构建 `V2Recipe` 对象:
- `userId` = 当前用户ID
- `name` = "每日营养配餐 - {ckdStage}",如 "每日营养配餐 - CKD 5期"
- `description` = "蛋白质 {proteinIntake}g/天 | 能量 {energyIntake}kcal/天"
- `coverImage` = 取午餐第一道菜的图片 URL最具代表性
- `mealType` = null整日配餐非单餐
- `category` = "营养配餐"
- `tagsJson` = `["AI配餐", "{ckdStage}"]`
- `ingredientsJson` = 聚合三餐所有食材
- `stepsJson` = 存放完整 mealPlanJson早/午/晚餐详情),复用此字段存储配餐详情
- `totalProtein` = result.getProteinIntake()
- `totalEnergy` = result.getEnergyIntake()
- `suitableStagesJson` = `["{ckdStage}"]`
- `suitableDialysis` = result.getHasDialysis()
- `status` = "published"
- `isRecommend` = 0不进入官方推荐通过 source + userId 查询)
- `isOfficial` = 0
- `source` = "calculator"
- `sourceId` = resultId
- 幂等处理:先检查是否已存在 `source='calculator' AND source_id=resultId` 的记录,避免重复
## 4. 后端 - 首页推荐食谱混入用户食谱
**文件**: [ToolHomeServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolHomeServiceImpl.java)
修改 `getRecommendedRecipes()` 方法:
1. 如果用户已登录,先查询该用户的食谱(`user_id = currentUserId AND status = 'published' AND source = 'calculator'`,按 `created_at DESC`,取最新 1 条)
2. 再查询官方推荐食谱(现有逻辑,`is_recommend = 1`
3. 将用户食谱排在前面,官方推荐排在后面,合并后返回
4. 总数仍控制在 `limit` 范围内(如用户有 1 条自己的食谱,则官方推荐取 limit-1 条)
5. 缓存 key 需要区分用户(登录用户的缓存 key 加上 userId
## 5. 后端 - 推荐食谱接口返回字段补充
**文件**: [ToolHomeServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolHomeServiceImpl.java)
在返回数据 map 中增加字段,使前端能够区分并展示更多信息:
```java
map.put("id", recipe.getRecipeId());
map.put("name", recipe.getName());
map.put("coverImage", recipe.getCoverImage());
map.put("totalEnergy", recipe.getTotalEnergy());
map.put("description", recipe.getDescription()); // 新增
map.put("source", recipe.getSource()); // 新增:标记来源
map.put("totalProtein", recipe.getTotalProtein()); // 新增
```
## 6. 前端 - 首页食谱展示适配
**文件**: [index.vue (首页)](msh_single_uniapp/pages/tool_main/index.vue)
调整食谱卡片展示逻辑:
- `item.tag`:如果 `source === 'calculator'` 显示 "我的配餐",否则显示 "推荐"
- `item.tagClass`:根据来源使用不同样式(如用户食谱用不同颜色)
- `item.desc`:使用 `description` 字段(如 "蛋白质 75.6g/天 | 能量 2205kcal/天"
- `item.time`:可不显示或显示 "每日配餐"
- `item.views`:使用 `viewCount` 或隐藏
修改 `loadData` 中对 `recipeList` 的处理逻辑,将后端返回的数据映射为前端需要的格式。
## 7. 前端 - 食谱详情页适配
点击首页食谱卡片跳转到 `recipe-detail` 页面,需要确认该页面能正确显示来自计算器的食谱内容(特别是 `stepsJson` 中存储的 mealPlan 数据)。如果需要特殊处理,在详情页根据 `source` 字段做渲染分支。
## 涉及文件清单
| 层级 | 文件 | 改动 |
| -------- | --------------------------------------------- | ------------------------------ |
| SQL | `docs/sql/v2_recipes_add_source.sql`(新建) | 新增 source、source_id 字段 |
| Entity | `V2Recipe.java` | 新增 source、sourceId 字段 |
| Service | `ToolCalculatorServiceImpl.java` | adopt() 中新增保存食谱逻辑 |
| Service | `ToolHomeServiceImpl.java` | getRecommendedRecipes() 混入用户食谱 |
| Frontend | `msh_single_uniapp/pages/tool_main/index.vue` | 食谱卡片展示适配 |