课程内容
- 本日任务为作业 自主完成
- 新增套餐
- 删除套餐
- 修改套餐
- 起售停售套餐
其他要求
- 根据产品原型进行需求分析 分析出业务规则
- 设计接口
- 梳理表之间的关系
- 根据接口设计进行代码实现
- 通过swagger和前后端联调进行功能测试
新增套餐
问题分析
- 套餐必定包含几个菜品
- 套餐名称唯一
- 新增套餐默认为停售状态
- 根据分页分类类型展示菜品
接口设计
数据库设计
字段名 |
数据类型 |
说明 |
备注 |
id |
bigint |
主键 |
自增 |
name |
varchar(32) |
套餐名称 |
唯一 |
category_id |
bigint |
分类id |
逻辑外键 |
price |
decimal(10,2) |
套餐价格 |
|
image |
varchar(255) |
图片路径 |
|
description |
varchar(255) |
套餐描述 |
|
status |
int |
售卖状态 |
1起售 0停售 |
create_time |
datetime |
创建时间 |
|
update_time |
datetime |
最后修改时间 |
|
create_user |
bigint |
创建人id |
|
update_user |
bigint |
最后修改人id |
|
setmeal_dish表为套餐菜品关系表,用于存储套餐和菜品的关联关系。具体表结构如下:
字段名 |
数据类型 |
说明 |
备注 |
id |
bigint |
主键 |
自增 |
setmeal_id |
bigint |
套餐id |
逻辑外键 |
dish_id |
bigint |
菜品id |
逻辑外键 |
name |
varchar(32) |
菜品名称 |
冗余字段 |
price |
decimal(10,2) |
菜品单价 |
冗余字段 |
copies |
int |
菜品份数 |
|
代码开发
@Data
public class SetmealDTO implements Serializable {
private Long id;
//分类id
private Long categoryId;
//套餐名称
private String name;
//套餐价格
private BigDecimal price;
//状态 0:停用 1:启用
private Integer status;
//描述信息
private String description;
//图片
private String image;
//套餐菜品关系
private List<SetmealDish> setmealDishes = new ArrayList<>();
}
Contreller层
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
public class DishController {
@Autowired
private DishService dishService;
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<Dish>> list(Long categoryId) {
List<Dish> list = dishService.list(categoryId);
return Result.success(list);
}
}
- 创建SetmealController处理套餐相关请求
```@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
/**
* 新增套餐
* @param setmealDTO
* @return
*/
@PostMapping
@ApiOperation("新增套餐")
public Result save(@RequestBody SetmealDTO setmealDTO) {
setmealService.saveWithDish(setmealDTO);
return Result.success();
}
}
Server层
```@Service
public class DishServiceImpl implements DishService {
@Autowired
private DishMapper dishMapper;
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
@Override
public List<Dish> list(Long categoryId) {
Dish dish = Dish.builder()
.categoryId(categoryId)
.status(StatusConstant.ENABLE)
.build();
return dishMapper.list(dish);
}
}
@Service
public class SetmealServiceImpl implements SetmealService {
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
/**
* 新增套餐
* 需要保存套餐与菜品的关系
*/
@Transactional
@Override
public void saveWithDish(SetmealDTO setmealDTO) {
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO, setmeal);
//向套餐表插入数据
setmealMapper.insert(setmeal);
//获取生成的套餐id
Long setmealId = setmeal.getId();
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
setmealDish.setSetmealId(setmealId);
});
//保存套餐和菜品的关联关系
setmealDishMapper.insertBatch(setmealDishes);
}
}
Mapper层
public interface DishMapper {
/**
* 动态条件查询菜品
* @param dish
* @return
*/
List<Dish> list(Dish dish);
}
```<select id="list" resultType="Dish" parameterType="Dish">
select * from dish
<where>
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
<if test="categoryId != null">
and category_id = #{categoryId}
</if>
<if test="status != null">
and status = #{status}
</if>
</where>
order by create_time desc
</select>
public interface SetmealMapper {
/**
* 新增套餐
* @param setmeal
*/
void insert(Setmeal setmeal);
}
<insert id="insert" parameterType="Setmeal" useGeneratedKeys="true" keyProperty="id">
insert into setmeal
(category_id, name, price, status, description, image, create_time, update_time, create_user, update_user)
values (#{categoryId}, #{name}, #{price}, #{status}, #{description}, #{image}, #{createTime}, #{updateTime},
#{createUser}, #{updateUser})
</insert>
public interface SetmealDishMapper {
/**
* 批量保存套餐和菜品的关联关系
* @param setmealDishes
*/
void insertBatch(List<SetmealDish> setmealDishes);
}
<insert id="insertBatch" parameterType="list">
insert into setmeal_dish
(setmeal_id,dish_id,name,price,copies)
values
<foreach collection="setmealDishes" item="sd" separator=",">
(#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})
</foreach>
</insert>
分页查询
需求分析
- 根据页码进行分页展示
- 每页展示10条数据
- 按照套餐名称 分类进行查询
代码开发
Controller层
- 在SetmealController层中创建分页查询方法
@GetMapping("/page")
@ApiOperation("套餐分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO) {
PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
return Result.success(pageResult);
}
Service层
PageResult pageQuery(SetmealPageQueryDto setmealPageQueryDto)
@Override
public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
int pageNum = setmealPageQueryDTO.getPage();
int pageSize = setmealPageQueryDTO.getPageSize();
PageHelper.startPage(pageNum, pageSize);
Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());
}
Mapper层
Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO)
<select id="pageQuery" resultType="com.sky.vo.SetmealVO">
select
s.*,c.name categoryName
from
setmeal s
left join
category c
on
s.category_id = c.id
<where>
<if test="name != null">
and s.name like concat('%',#{name},'%')
</if>
<if test="status != null">
and s.status = #{status}
</if>
<if test="categoryId != null">
and s.category_id = #{categoryId}
</if>
</where>
order by s.create_time desc
</select>
删除套餐实现
需求分析
代码开发
Controller层
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List<Long> ids){
setmealService.deleteBatch(ids);
return Result.success();
}
Service层
void deleteBatch(List<Long> ids);
@Transactional
@Override
public void deleteBatch(List<Long> ids) {
//查询套餐是否在售卖
ids.forEach(id -> {
Setmeal setmeal = setmealMapper.getById(id);
if(StatusConstant.ENABLE == setmeal.getStatus()){
//起售中的套餐不能删除
throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
}
});
ids.forEach(setmealId -> {
//删除套餐表中的数据
setmealMapper.deleteById(setmealId);
//删除套餐菜品关系表中的数据
setmealDishMapper.deleteBySetmealId(setmealId);
});
}
Mapper层
- 在setmealMapper层中添加getById方法和deleteById方法
- 简单语句用注解实现
@Select("select * from setmeal where id = #{id}")
Setmeal getById(Long id);
@Delete("delete from setmeal where id = #{id}")
void deleteById(Long setmealId);
- 根据套餐id删除套餐和菜品的关联关系
- 在SetmealDishMapper中创建方法
@Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")
void deleteBySetmealId(Long setmealId);
套餐修改
需求分析
代码实现
Controller层
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result<SetmealVO> getById(@PathVariable Long id){
SetmealVO setmealVO = setmealService.getByIdWithDish(id);
return Result.success(setmealVO);
}
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO){
setmealService.updateWithDish(setmealDTO);
return Result.success();
}
Service层
SetmealVO getByIdWithDish(Long id);
void updateWithDish(SetmealDTO setmealDTO);
@Override
//根据id查询套餐
public SetmealVO getByIdWithDish(Long id) {
Setmeal setmeal = setmealMapper.getById(id);
List<SetmealDish> setmealDishes = setmealDishMapper.getBySetmealId(id);
SetmealVO setmealVO = new SetmealVO();
BeanUtils.copyProperties(setmeal, setmealVO);
setmealVO.setSetmealDishes(setmealDishes);
return setmealVO;
}
@Override
@Transactional
//修改套餐
public void updateWithDish(SetmealDTO setmealDTO) {
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO, setmeal);
//1、修改套餐表,执行update
setmealMapper.update(setmeal);
//套餐id
Long setmealId = setmealDTO.getId();
//2、删除套餐和菜品的关联关系,操作setmeal_dish表,执行delete
setmealDishMapper.deleteBySetmealId(setmealId);
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
setmealDish.setSetmealId(setmealId);
});
//3、重新插入套餐和菜品的关联关系,操作setmeal_dish表,执行insert
setmealDishMapper.insertBatch(setmealDishes);
}
Mapper层
- 在setmealMapper层中创建update方法
void update(Setmeal setmeal)
- 在SetmealDIshMapper层中创建getBySetmealId方法
@Select("select * from setmeal_dish where setmeal_id = #{setmealId}")
List<SetmealDish> getBySetmealId(Long setmealId);
- 在SetmealMapper.xml文件中实现动态sql
<update id="update" parameterType="com.sky.entity.Setmeal">
update setmeal
<set>
<if test="name != null">name = #{name},</if>
<if test="categoryId != null">category_id = #{categoryId},</if>
<if test="price != null">price = #{price},</if>
<if test="status != null">status = #{status},</if>
<if test="description != null">description = #{description},</if>
<if test="image != null">image = #{image},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser},</if>
</set>
where id = #{id}
</update>
起售停售套餐
需求分析
代码开发
Controller层
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
public Result startOfstop(@PathVariable Integer status , long id){
setmealService.startOfstop(status,id);
return Result.success();
}
Service层
void startOfstop(Integer status, long id);
@Override
public void startOfstop(Integer status, long id) {
//先判断套餐内是否包括停售的菜品
if (status == StatusConstant.ENABLE) {
List<Dish> dishList = dishMapper.getBySetmealId(id);
if (dishList.size() > 0) {
dishList.forEach(dish -> {
if (dish.getStatus() == StatusConstant.DISABLE) {
//如果套餐内有停售的菜品,不能起售
throw new DeletionNotAllowedException(MessageConstant.SETMEAL_CONTAIN_DISABLE_DISH);
}
});
Setmeal setmeal = Setmeal.builder()
.id(id)
.status(status)
.build();
setmealMapper.update(setmeal);
}
}
}
Mapper层
- 在setmealMapper中创建方法
- 简单语句直接用注解实现
@Select("select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #{setmealId}")
List<Dish> getBySetmealId(Long setmealId);
- debug日志
- 联调时发现起售停售无法正常实现
- 检查发现菜品的起售停售都没有对应接口
- ?我忘记写了
- 在dishController层和service层添加对应方法
- 调用现有的update方法成功解决
- debug日志2
- 前后端联调的时候点击停售不显示异常
- 实际上套餐状态没有更新
- 在SetmealServiceImpl的startOfstop方法中
- 只有起售(status == ENABLE)会更新状态
- 停售时不更新
- 将更新套餐状态部分代码从判断中提出解决