跳到主要内容SpringBoot 减肥食谱管理系统(中):食谱与菜单配置实现 | 极客日志Java大前端java
SpringBoot 减肥食谱管理系统(中):食谱与菜单配置实现
SpringBoot 结合 PostgreSQL 构建减肥食谱管理系统,本文聚焦于食谱与菜单配置的数据库设计、后端业务逻辑实现以及前端父子表单交互。通过 MyBatis Plus 处理数据持久化,利用 Thymeleaf 完成页面渲染,重点演示了一对多关系的增删改查流程,为类似场景提供技术参考。
极客零度1 浏览 SpringBoot 减肥食谱管理系统(中):食谱与菜单配置实现
前言
健康意识的提升让科学饮食成为关注焦点。一个合理的减肥食谱系统需要兼顾热量控制与营养均衡,同时适应不同地区的饮食习惯。本文作为系列项目的中篇,将重点介绍如何基于 SpringBoot 和 PostgreSQL 搭建食谱区域、餐别及菜单详情的配置功能,并通过实际案例展示父子表单在后台管理中的实现方式。
一、项目需求简介
1. 需求背景
在前序内容中,我们已完成区域和省份的基础配置。这部分是系统的地理维度基础,决定了食谱能否覆盖特定区域的饮食偏好。例如沿海地区侧重海鲜,内陆地区侧重肉菜搭配。本阶段将继续完善食谱核心数据模型,包括四季分类、餐别定义及具体菜品配方。
2. 本次实现范围
本次开发主要围绕以下数据库表展开:
- 四季食谱信息表 (
biz_four_seasons_recipe)
- 食谱餐别信息表 (
biz_recipe_meal_type)
- 菜单详情信息表 (
biz_recipe_details)
这些表构成了后续功能的数据基石,需确保结构清晰且易于扩展。
二、SpringBoot 后端实现
本节以 Ruoyi 框架为例,讲解 Model 层与业务层的实现细节。
1. Model 层设计
Model 类需与数据库表严格对应。这里涉及三个核心实体:
四季食谱信息
按季节维度组织数据,用于定义不同季节的膳食方向。
package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@TableName(value = "biz_four_seasons_recipe")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class FourSeasonsRecipe implements Serializable {
private static -;
Long pkId;
Long foodId;
String foodName;
Integer totalEnergy;
Integer season;
Integer orderNum;
String remark;
}
final
long
serialVersionUID
=
6072349554277340610L
@TableId(value = "pk_id")
private
@TableField(value = "food_id")
private
@TableField(value = "food_name")
private
@TableField(value = "total_energy")
private
@TableField(value = "season")
private
@TableField(value = "order_num")
private
private
餐别信息
定义一日三餐或加餐的类型,支持一对多关联到菜单详情。
package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@TableName(value = "biz_recipe_meal_type")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class RecipeMealType implements Serializable {
private static final long serialVersionUID = 4399855059597304978L;
@TableId(value = "pk_id")
private Long pkId;
@TableField(value = "recipe_id")
private Long recipeId;
@TableField(value = "meal_type_name")
private String mealTypeName;
@TableField(value = "order_num")
private Integer orderNum;
@TableField(exist = false)
private List<RecipeDetails> recipeDetailsList;
}
package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@TableName(value = "biz_recipe_details")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class RecipeDetails implements Serializable {
private static final long serialVersionUID = -4003180539390925788L;
@TableId(value = "pk_id")
private Long pkId;
@TableField(value = "meal_type_id")
private Long mealTypeId;
private String name;
private String formula;
@TableField(value = "order_num")
private Integer orderNum;
}
Mapper 层遵循标准 MyBatis Plus 规范,此处不再赘述。
2. 业务层实现
业务层负责校验规则与数据关联,以下是几个关键场景的处理逻辑。
关于返回数量限制
在使用 MyBatis Plus 查询时,若需限制返回结果集,可直接追加 SQL 子句:
private FourSeasonsRecipe getFourSeasonsRecipe(FourSeasonsRecipe fourSeasonsRecipe) {
QueryWrapper<FourSeasonsRecipe> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("food_id", fourSeasonsRecipe.getFoodId());
if (StringUtils.isNotBlank(fourSeasonsRecipe.getFoodName())) {
queryWrapper.like("food_name", fourSeasonsRecipe.getFoodName());
}
queryWrapper.last(" limit 1 ");
return this.baseMapper.selectOne(queryWrapper);
}
父子表单的新增实现
餐别与菜单详情是一对多关系,适合使用父子表单管理。新增时先保存父表获取主键,再批量插入子表并关联 ID。
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int insertEntity(RecipeMealType recipeMealType) {
int result = this.baseMapper.insert(recipeMealType);
Long pkId = recipeMealType.getPkId();
List<RecipeDetails> recipeDetailsList = recipeMealType.getRecipeDetailsList();
for (RecipeDetails details : recipeDetailsList) {
details.setMealTypeId(pkId);
}
recipeDetailsService.saveBatch(recipeDetailsList);
return result;
}
父子表单的编辑实现
编辑逻辑相对灵活,这里采用'先删后增'的策略简化事务处理。即删除原有子表记录,再批量插入新提交的数据。这种方式在实际业务中可根据需求调整为增量更新。
三、Thymeleaf 前端实现
后端完成后,使用 Thymeleaf 构建前端界面。采用单体架构集成,便于快速部署。
1. 食谱管理实现
列表页需支持按地区 Tab 切换,并记住当前选择状态。通过 JavaScript 动态打开标签页:
function recipemealtypeConfig(recipeId, foodName) {
var url = ctx + "/eq/foodarea/recipemealtype/" + recipeId;
$.modal.openTab("[" + foodName + "] 餐别配置", url);
}
2. 餐别及详情实现
完成四季食谱定义后,进入餐别配置环节,包括早餐、中餐、晚餐及加餐的管理。这是饮食控制的核心部分。
3. 父子表单提交
父子表单与普通表单不同,需特殊处理数据绑定。HTML 中定义表格容器,JS 中初始化表格实例。
<h4>菜单详情</h4>
<div>
<div>
<button type="button" onclick="addRow()"><i>增加</i></button>
<button type="button" onclick="sub.delRow()"><i>删除</i></button>
<table></table>
</div>
</div>
$(function() {
var data = [{
pkId: "",
name: "",
formula: "",
orderNum: "1"
}];
var options = {
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
sidePagination: "client",
columns: [{
checkbox: true
}, {
field: 'index',
align: 'center',
title: "序号",
formatter: function(value, row, index) {
var columnIndex = $.common.sprintf("<input type='hidden' name='index' value='%s'>", $.table.serialNumber(index));
return columnIndex + $.table.serialNumber(index);
}
}, {
field: 'name',
align: 'center',
title: '菜单名称',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input type='text' name='recipeDetailsList[%s].name' value='%s'>", index, value);
return html;
}
}, {
field: 'formula',
align: 'center',
title: '配方信息',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input type='text' name='recipeDetailsList[%s].formula' value='%s'>", index, value);
return html;
}
}, {
field: 'orderNum',
align: 'center',
title: '排序号',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input type='text' name='recipeDetailsList[%s].orderNum' value='%s'>", index, value);
return html;
}
}, {
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var value = $.common.isNotEmpty(row.index) ? row.index : $.table.serialNumber(index);
return '<a href="javascript:void(0)" onclick="sub.delRowByIndex(\'' + value + '\')"><i></i>删除</a>';
}
}]
};
$.table.init(options);
});
function addRow() {
var count = $("#" + table.options.id).bootstrapTable('getData').length;
var row = {
index: $.table.serialNumber(count),
name: "",
formula: "",
orderNum: ""
};
sub.addRow(row);
}
以上配置即可实现父子表单的前端交互,实际效果符合预期。
四、总结
本文详细介绍了基于 SpringBoot 和 PostgreSQL 的减肥食谱管理系统中,食谱与菜单配置的完整实现流程。从数据库模型设计到后端业务逻辑,再到前端父子表单的交互,涵盖了全栈开发的关键环节。特别是针对一对多关系的增删改查,提供了可复用的代码模式,可为类似场景的开发提供参考。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online