7.1 KiB
7.1 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 计算器结果保存食谱 | 在用户采纳营养计划时,将食谱计算器的配餐方案提取并保存到 v2_recipes 食谱表,并在首页"精选食谱"中混合展示用户自己的食谱(排在前面)。 |
|
false |
食谱计算器结果保存到食谱表
整体流程
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 新增字段
需要新增两个字段以追踪食谱来源:
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
新增两个字段:
@ApiModelProperty(value = "来源:manual/calculator/ai")
private String source;
@ApiModelProperty(value = "来源ID(计算器结果ID等)")
private Long sourceId;
3. 后端 - 采纳时保存食谱
文件: ToolCalculatorServiceImpl.java
在 adopt() 方法中,步骤 5(创建营养计划)之后,新增步骤"保存食谱到 v2_recipes":
- 注入
V2RecipeDao - 从
V2CalculatorResult中解析mealPlanJson得到 MealPlan - 构建
V2Recipe对象:userId= 当前用户IDname= "每日营养配餐 - {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= 0source= "calculator"sourceId= resultId
- 幂等处理:先检查是否已存在
source='calculator' AND source_id=resultId的记录,避免重复
4. 后端 - 首页推荐食谱混入用户食谱
修改 getRecommendedRecipes() 方法:
- 如果用户已登录,先查询该用户的食谱(
user_id = currentUserId AND status = 'published' AND source = 'calculator',按created_at DESC,取最新 1 条) - 再查询官方推荐食谱(现有逻辑,
is_recommend = 1) - 将用户食谱排在前面,官方推荐排在后面,合并后返回
- 总数仍控制在
limit范围内(如用户有 1 条自己的食谱,则官方推荐取 limit-1 条) - 缓存 key 需要区分用户(登录用户的缓存 key 加上 userId)
5. 后端 - 推荐食谱接口返回字段补充
在返回数据 map 中增加字段,使前端能够区分并展示更多信息:
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 (首页)
调整食谱卡片展示逻辑:
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 |
食谱卡片展示适配 |