167 lines
7.1 KiB
Markdown
167 lines
7.1 KiB
Markdown
|
|
---
|
|||
|
|
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` | 食谱卡片展示适配 |
|
|||
|
|
|
|||
|
|
|