新增套餐功能

(14)SetmealController完善

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

完整代码:

package com.sky.controller.admin;

import com.sky.dto.SetmealDTO;
import com.sky.result.Result;
import com.sky.service.SetmealService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 套餐管理
 */
@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐相关接口")
@Slf4j
public class SetmealController {
    @Autowired
    private SetmealService setmealService;
    /**
     * 新增套餐
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    public Result save(@RequestBody SetmealDTO setmealDTO) {
        log.info("新增套餐:{}", setmealDTO);
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }
}

添加的代码:

/**
 * 套餐管理
 */
@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐相关接口")
@Slf4j
public class SetmealController {
    @Autowired
    private SetmealService setmealService;
    /**
     * 新增套餐
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    public Result save(@RequestBody SetmealDTO setmealDTO) {
        log.info("新增套餐:{}", setmealDTO);
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }

示意图:

(15)SetmealService完善

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

完整代码:

package com.sky.service;

import com.sky.dto.SetmealDTO;

public interface SetmealService {
    /**
     * 保存套餐及其菜品
     * @param setmealDTO
     */
    void saveWithDish(SetmealDTO setmealDTO);
}

添加的代码:

/**
 * 保存套餐及其菜品
 * @param setmealDTO
 */
void saveWithDish(SetmealDTO setmealDTO);

示意图:

(16)SetmealServiceImpl完善

位置:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

完整代码:

package com.sky.service.impl;

import com.sky.dto.SetmealDTO;
import com.sky.entity.Setmeal;
import com.sky.entity.SetmealDish;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealDishMapper;
import com.sky.mapper.SetmealMapper;
import com.sky.service.SetmealService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Slf4j
public class SetmealServiceImpl implements SetmealService {

    @Autowired
    private SetmealMapper setmealMapper;
    @Autowired
    private SetmealDishMapper setmealDishMapper;
    @Autowired
    private DishMapper dishMapper;

    /**
     * 新增套餐,同时需要保存套餐和菜品的关联关系
     * @param setmealDTO
     */
    @Transactional
    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);
    }
}

示意图:

(17)SetmealMapper完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealMapper.java

完整代码:

package com.sky.mapper;

import com.sky.annotation.AutoFill;
import com.sky.entity.Setmeal;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface SetmealMapper {

    /**
     * 新增套餐
     * @param setmeal
     */
    @AutoFill(value = OperationType.INSERT)
    void insert(Setmeal setmeal);

     /**
     * 根据分类id查询套餐的数量
     * @param id
     * @return
     */
    @Select("select count(id) from setmeal where category_id = #{categoryId}")
    Integer countByCategoryId(Long id);
}

添加的代码:

/**
 * 新增套餐
 * @param setmeal
 */
@AutoFill(value = OperationType.INSERT)
void insert(Setmeal setmeal);

示意图:

(18)SetmealDishMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealDishMapper.java

完整代码:

package com.sky.mapper;

import com.sky.entity.SetmealDish;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface SetmealDishMapper {
    /**
     * 根据菜品id获取套餐id
     * @param dishIds
     * @return
     */
    List<Long> getSetmealIdsByDishId(List<Long> dishIds);

    /**
     * 批量插入套餐菜品
     * @param setmealDishes
     */
    void insertBatch(List<SetmealDish> setmealDishes);
}

添加的代码:

/**
 * 批量插入套餐菜品
 * @param setmealDishes
 */
void insertBatch(List<SetmealDish> setmealDishes);

示意图:

(19)SetmealDishMapper.xml完善

位置:sky-server/src/main/resources/mapper/SetmealDishMapper.xml

完整代码:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.SetmealDishMapper">
    <select id="getSetmealIdsByDishId" resultType="java.lang.Long">
        SELECT setmeal_id FROM setmeal_dish WHERE dish_id IN
        <foreach collection="dishIds" item="dishId" open="(" separator="," close=")">
            #{dishId}
        </foreach>
    </select>

    <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>

</mapper>

添加的代码:

<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>

示意图:

(20)功能测试

打上断点,调试项目(DuBug)

打开前端网页:添加套餐,添加相关数据,点击添加,回到控制台,查看是否成功接收到前端发送的信息,成功则如下图所示

跳过断点

查看数据库,已成功添加数据“套餐二”

至此,新增套餐功能已完成!

套餐分页查询功能

(21)SetmealController完善

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

添加的代码:

/**
 * 套餐分页查询
 * @param setmealPageQueryDTO
 * @return
 */
@GetMapping("/page")
@ApiOperation("分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO) {
    PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
    return Result.success(pageResult);
}

示意图:

(22)SetmealService完善

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

添加的代码:

/**
 * 分页查询套餐
 * @param setmealPageQueryDTO
 * @return
 */
PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);

示意图:

(24)SetmealServiceImpl完善

位置:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

添加的代码:

/**
 * 分页查询
 * @param setmealPageQueryDTO
 * @return
 */
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());
}

示意图:

(25)SetmealMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealMapper.java

添加的代码:

/**
 *  分页查询
 * @param setmealPageQueryDTO
 * @return
 */
Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);

示意图:

(26)SetmealMapper.xml完善

位置:sky-server/src/main/resources/mapper/SetmealMapper.xml

添加的代码:

<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>

示意图:

(27)功能测试

调试项目,打开前端网页,找到一下位置

出现数据,则说明功能实现成功!

批量删除套餐功能实现

(28)SetmealServiceImpl完善

位置:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

添加的代码:

/**
 * 批量删除套餐
 * 1.删除套餐表数据
 * 2.删除套餐菜品表数据
 * 3.删除菜品表数据
 * @param ids
 */
@Transactional//由于涉及到三个表的操作,需要事务控制
public void delete(List<Long> ids) {
    //判断起售状态是否为0,如果为0,则不能删除
    ids.forEach(id -> {
        Setmeal setmeal = setmealMapper.getByID(id);
        if (setmeal.getStatus() == StatusConstant.ENABLE) {
            throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
        }
    });

    //判断是否有菜品关联了套餐,如果有,则不能删除
    ids.forEach(id -> {
    setmealMapper.deleteByIDs(ids);//删除套餐表数据
    setmealDishMapper.deleteBySetmealIds(ids);//删除套餐菜品表数据
    });
}

示意图:

(29)SetmealController完善

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

添加的代码:

/**
 * 批量删除套餐
 * @param ids
 * @return
 */
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List<Long> ids) {
    log.info("批量删除套餐:{}", ids);
    setmealService.delete(ids);
    return Result.success();
}

示意图:

(30)SetmealService完善

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

添加的代码:

/**
 * 根据id删除套餐
 * @param ids
 */
void delete(List<Long> ids);

示意图:

(31)SetmealDishMapper.xml完善

位置:sky-server/src/main/resources/mapper/SetmealDishMapper.xml

添加的代码:

<delete id="deleteBySetmealIds" parameterType="list">
    DELETE FROM setmeal_dish
    WHERE setmeal_id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

<select id="getDishIdsBySetmealIds" resultType="java.lang.Long">
    SELECT DISTINCT dish_id
    FROM setmeal_dish
    WHERE setmeal_id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

示意图:

(32)SetmealDishMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealDishMapper.java

添加的代码:

/**
 * 根据套餐id删除套餐菜品
 * @param ids
 */
void deleteBySetmealIds(List<Long> ids);

/**
 * 根据套餐id获取菜品id
 * @param ids
 * @return
 */
List<Long> getDishIdsBySetmealIds(List<Long> ids);

示意图:

(33)SetmealMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealMapper.java

添加的代码:

/**
 *  根据id删除套餐
 * @param ids
 */
void deleteByIDs(List<Long> ids);

/**
 *  根据id查询套餐
 * @param id
 * @return
 */
@Select("select * from setmeal where id = #{id}")
Setmeal getByID(Long id);

示意图:

(34)SetmealMapper.xml完善

位置:sky-server/src/main/resources/mapper/SetmealMapper.xml

添加的代码:

<delete id="deleteByIDs">
    delete from setmeal
    where id in
    <foreach collection = "ids" item="id" index="index" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

示意图:

(35)功能测试

直接打开或调试项目,打开前端网页,选择需要删除的套餐,点击批量删除,删除成功则功能完成!

根据id获取套餐功能实现

(36)SetmealController

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

添加的代码:

/**
 * 根据id获取套餐
 * @param id
 * @return
 */
@GetMapping("/{id}")
@ApiOperation("根据id获取套餐")
public Result<SetmealVO> getById(@PathVariable("id") Long id){
    log.info("根据id获取套餐:{}", id);
    SetmealVO setmealVO = setmealService.getById(id);
    return Result.success(setmealVO);
}

示意图:

(37)SetmealService

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

添加的代码:

/**
 * 根据id获取套餐详情
 * @param id
 * @return
 */
SetmealVO getById(Long id);

示意图:

(38)MessageConstant和SetmealServiceImpl

位置1:sky-common/src/main/java/com/sky/constant/MessageConstant.java

添加代码:

public static final String SETMEAL_NOT_FOUND = "套餐不存在";

示意图:

位置2:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

添加的代码:

/**
 * 根据id查询套餐详情和菜品信息
 * @param id
 * @return
 */
public SetmealVO getById(Long id) {
    //查询套餐信息,如果没有该菜品,则抛出异常
    Setmeal setmeal = setmealMapper.getByID(id);
    if (setmeal == null) {
        throw new SetmealEnableFailedException(MessageConstant.SETMEAL_NOT_FOUND);
    }

    //查询套餐菜品信息
    List<SetmealDish> setmealDishes = setmealDishMapper.getBySetmealId(id);

    //创建套餐vo对象
    SetmealVO setmealVO = new SetmealVO();
    BeanUtils.copyProperties(setmeal, setmealVO);

    //设置套餐菜品信息
    setmealVO.setSetmealDishes(setmealDishes);

    return setmealVO;
}

示意图:

(39)SetmealDishMapper.java

位置:sky-server/src/main/java/com/sky/mapper/SetmealDishMapper.java

添加的代码:

/**
 * 根据id获取套餐及菜品关联关系
 * @param id
 * @return
 */
@Select("SELECT * FROM setmeal_dish WHERE id = #{id}")
List<SetmealDish> getBySetmealId(Long id);

示意图:

(40)功能测试

打开Swagger测试文档:苍穹外卖项目接口文档

查看数据库,找到套餐对应的id号,并输入到参数值中,如“32”,查看相应内容是否有对应的套餐信息,有则成功!

最后输入一个,数据库中没有的套餐id,如“1”,借此判断是否能正确抛出异常,

更新套餐功能

(41)SetmealController完善

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

添加的代码:

/**
 * 更新套餐
 * @param setmealDTO
 * @return
 */
@PutMapping
@ApiOperation("更新套餐")
public Result Update(@RequestBody SetmealDTO setmealDTO) {
    log.info("更新套餐:{}", setmealDTO);
    setmealService.update(setmealDTO);
    return Result.success();
}

示意图:

(42)SetmealService完善

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

添加的代码:

/**
 * 更新套餐
 * @param setmealDTO
 */
void update(SetmealDTO setmealDTO);

示意图:

(43)SetmealServiceImpl完善

位置:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

添加的代码:

/**
 * 修改套餐
 * @param setmealDTO
 */
@Transactional
public void update(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);
 
    //获取套餐菜品dto列表
    List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
 
    //执行循环,设置套餐id,然后保存到数据库,作用为保存套餐菜品的关联关系
    setmealDishes.forEach(setmealDish -> {
        setmealDish.setSetmealId(setmealId);
    });
 
    //3、重新插入套餐和菜品的关联关系,操作setmeal_dish表,执行insert
    setmealDishMapper.insertBatch(setmealDishes);
}

示意图:

(44)SetmealDishMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/SetmealDishMapper.java

添加的代码:

/**
 * 根据套餐id删除套餐菜品
 * @param setmealId
 */
@Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")
void deleteBySetmealId(Long setmealId);

示意图:

(45)功能测试

调试或者直接启动项目,打开前端网页,找到以下位置,任意选择一个套餐,点击“修改”,任意输入修改的数据,点击保存,跳出修改成功的信息,则说明功能完成!

查看数据库,数据库已发生变化

套餐停售启售功能

(1)SetmealController完善

位置:sky-server/src/main/java/com/sky/controller/admin/SetmealController.java

添加的代码:

/**
 * 套餐起售停售
 * @param status
 * @param id
 * @return
 */
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
public Result startOrStop(@PathVariable Integer status, Long id) {
    setmealService.startOrStop(status, id);
    return Result.success();
}

示意图:

(2)SetmealService完善

位置:sky-server/src/main/java/com/sky/service/SetmealService.java

添加的代码:

/**
 * 套餐起售、停售
 * @param status
 * @param id
 */
void startOrStop(Integer status, Long id);

示意图:

(3)SetmealServiceImpl完善

位置:sky-server/src/main/java/com/sky/service/impl/SetmealServiceImpl.java

添加的代码:

/**
 * 套餐起售、停售
 * @param status
 * @param id
 */
public void startOrStop(Integer status, Long id) {
    //起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
    if(status == StatusConstant.ENABLE){
        //select a.* from dish a, setmeal_dish b where a.id = b.dish_id and b.setmeal_id = #{id}
        //如果有停售菜品,则抛出异常
        List<Dish> dishList = dishMapper.getBySetmealId(id);
 
        //判断是否有菜品关联了套餐,如果有,则不能删除
        //套餐菜品为空,就不需要判断菜品状态了
        if(dishList != null && dishList.size() > 0){
            dishList.forEach(dish -> {
                //判断菜品状态是否为0,如果为0,则不能启用
                if(StatusConstant.DISABLE == dish.getStatus()){
                    //抛出异常
                    throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                }
            });
        }
    }
 
    //修改套餐状态
    Setmeal setmeal = Setmeal.builder()
            .id(id)
            .status(status)
            .build();
    setmealMapper.update(setmeal);
}
部分代码说明:(AI)

//修改套餐状态
Setmeal setmeal = Setmeal.builder()
        .id(id)
        .status(status)
        .build();
setmealMapper.update(setmeal);

在这段代码中,只设置 .id(id).status(status) 而不设置其他属性,主要基于以下原因:

  1. 更新需求的精准性
    这段代码的核心目的是仅更新菜品的状态(起售 / 停售),不需要修改菜品的其他信息(如名称、价格、分类等)。
    MyBatis 的 update 方法通常会根据实体对象中的非空字段生成 SQL 更新语句。如果设置了其他属性(如 name),即使没有修改其值,也可能被误更新(例如如果实体类中该字段默认值为 null,可能导致数据库字段被置空)。
  2. 避免意外数据覆盖
    假设实体类 Dish 中还有 nameprice 等属性,且这些属性在创建 dish 对象时未被赋值(默认可能为 null):
    • 如果 dishMapper.update(dish) 方法的 SQL 是 UPDATE dish SET name=?, price=?, status=? WHERE id=?,那么未设置的 nameprice 会被更新为 null,这显然是错误的。
    • 只设置 id(用于定位要更新的记录)和 status(要修改的字段),可以确保 SQL 只更新 status 字段,避免其他字段被意外修改。
  3. 符合 SQL 最佳实践
    数据库更新操作应遵循最小修改原则:只更新需要变更的字段,这样可以:
    • 减少 SQL 语句的执行开销
    • 避免触发不必要的索引更新或触发器
    • 降低并发场景下的数据冲突风险

简单来说,这段代码通过 “只设置必要字段” 的方式,精准控制了更新范围,确保操作的安全性和高效性。

(4)DishMapper.java完善

位置:sky-server/src/main/java/com/sky/mapper/DishMapper.java

添加的代码:

    /**
     * 根据套餐id查询菜品
     * sql语句的意思为根据传进来的id找到对应的套餐,
     * 然后再根据该套餐表的dish_id找到对应菜品,最后找到套餐中菜品
     * @param id
     * @return
     */
    @Select("select a.* from dish a, setmeal_dish b where a.id = b.dish_id and b.setmeal_id = #{id}")
    List<Dish> getBySetmealId(Long id);

示意图:

SQL语句说明:根据指定的套餐 ID(setmeal_id),查询出该套餐所包含的所有菜品的完整信息

(5)功能测试

点击启售,成功后,再点击停售,若都无问题,则功能完成!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐