项目实战——苍穹外卖(Day4)

目录

1. 新增套餐

1.1 需求分析和设计

1.2 代码实现

1.2.1 DishController

1.2.2 DishService

1.2.3 DishServiceImpl

1.2.4 DishMapper

1.2.5 DishMapper.xml

1.2.6 SetmealController

1.2.7 SetmealService

1.2.8 SetmealServiceImpl

1.2.9 SetmealMapper

1.2.10 SetmealMapper.xml

1.2.11 SetmealDishMapper

1.2.12 SetmealDishMapper.xml

2. 套餐分页查询

2.1 需求分析和设计

2.2 代码实现

2.2.1 SetmealController

2.2.2 SetmealService

2.2.3 SetmealServiceImpl

2.2.4 SetmealMapper

2.2.5 SetmealMapper.xml

3. 删除套餐

3.1 需求分析和设计

3.2 代码实现

3.2.1 SetmealController

3.2.2 SetmealService

3.2.3 SetmealServiceImpl

3.2.4 SetmealMapper

3.2.5 SetmealDishMapper

4. 修改套餐

4.1 需求分析和设计

4.2 代码实现

4.2.1 SetmealController

4.2.2 SetmealService

4.2.3 SetmealServiceImpl

4.2.4 SetmealDishMapper

5. 起售停售套餐

5.1 需求分析和设计

5.2 代码实现

5.2.1 SetmealController

5.2.2 SetmealService

5.2.3 SetmealServiceImpl

5.2.4 DishMapper


完成套餐管理模块所有业务功能,包括:

  • 新增套餐

  • 套餐分页查询

  • 删除套餐

  • 修改套餐

  • 起售停售套餐

要求:

  1. 根据产品原型进行需求分析,分析出业务规则

  2. 设计接口

  3. 梳理表之间的关系(分类表、菜品表、套餐表、口味表、套餐菜品关系表)

  4. 根据接口设计进行代码实现

  5. 分别通过swagger接口文档和前后端联调进行功能测试

1. 新增套餐

1.1 需求分析和设计

项目实战——苍穹外卖(Day4)_第1张图片

项目实战——苍穹外卖(Day4)_第2张图片

业务规则:

  • 套餐名称唯一

  • 套餐必须属于某个分类

  • 套餐必须包含菜品

  • 名称、分类、价格、图片为必填项

  • 添加菜品窗口需要根据分类类型来展示菜品

  • 新增的套餐默认为停售状态

接口设计(共涉及到4个接口):

  • 根据类型查询分类(已完成)

  • 根据分类id查询菜品

  • 图片上传(已完成)

  • 新增套餐

项目实战——苍穹外卖(Day4)_第3张图片

项目实战——苍穹外卖(Day4)_第4张图片

 

项目实战——苍穹外卖(Day4)_第5张图片

数据库设计:

setmeal表为套餐表,用于存储套餐的信息。具体表结构如下:

字段名 数据类型 说明 备注
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 菜品份数

1.2 代码实现

1.2.1 DishController
/**
     * 根据分类id查询菜品
     * @param categoryId
     * @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result> list(Long categoryId){
    List list = dishService.list(categoryId);
    return Result.success(list);
}
1.2.2 DishService
/**
     * 根据分类id查询菜品
     * @param categoryId
     * @return
*/
List list(Long categoryId);
1.2.3 DishServiceImpl
/**
     * 根据分类id查询菜品
     * @param categoryId
     * @return
*/
public List list(Long categoryId) {
    Dish dish = Dish.builder()
        .categoryId(categoryId)
        .status(StatusConstant.ENABLE)
        .build();
    return dishMapper.list(dish);
}
1.2.4 DishMapper
/**
     * 动态条件查询菜品
     * @param dish
     * @return
*/
List list(Dish dish);
1.2.5 DishMapper.xml
1.2.6 SetmealController
/**
 * 套餐管理
 */
@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) {
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }
}
1.2.7 SetmealService
public interface SetmealService {

    /**
     * 新增套餐,同时需要保存套餐和菜品的关联关系
     * @param setmealDTO
     */
    void saveWithDish(SetmealDTO setmealDTO);
}
1.2.8 SetmealServiceImpl
/**
 * 套餐业务实现
 */
@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 setmealDishes = setmealDTO.getSetmealDishes();
        setmealDishes.forEach(setmealDish -> {
            setmealDish.setSetmealId(setmealId);
        });

        //保存套餐和菜品的关联关系
        setmealDishMapper.insertBatch(setmealDishes);
    }
}
1.2.9 SetmealMapper
/**
     * 新增套餐
     * @param setmeal
*/
@AutoFill(OperationType.INSERT)
void insert(Setmeal setmeal);
1.2.10 SetmealMapper.xml

    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})
1.2.11 SetmealDishMapper
/**
     * 批量保存套餐和菜品的关联关系
     * @param setmealDishes
*/
void insertBatch(List setmealDishes);
1.2.12 SetmealDishMapper.xml

    insert into setmeal_dish
    (setmeal_id,dish_id,name,price,copies)
    values
    
        (#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})
    

 

2. 套餐分页查询

2.1 需求分析和设计

产品原型:

项目实战——苍穹外卖(Day4)_第6张图片

业务规则:

  • 根据页码进行分页展示

  • 每页展示10条数据

  • 可以根据需要,按照套餐名称、分类、售卖状态进行查询

接口设计:

项目实战——苍穹外卖(Day4)_第7张图片

2.2 代码实现

2.2.1 SetmealController
/**
     * 分页查询
     * @param setmealPageQueryDTO
     * @return
*/
@GetMapping("/page")
@ApiOperation("分页查询")
public Result page(SetmealPageQueryDTO setmealPageQueryDTO) {
    PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
    return Result.success(pageResult);
}
2.2.2 SetmealService
/**
     * 分页查询
     * @param setmealPageQueryDTO
     * @return
*/
PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
2.2.3 SetmealServiceImpl
/**
     * 分页查询
     * @param setmealPageQueryDTO
     * @return
*/
public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
    int pageNum = setmealPageQueryDTO.getPage();
    int pageSize = setmealPageQueryDTO.getPageSize();

    PageHelper.startPage(pageNum, pageSize);
    Page page = setmealMapper.pageQuery(setmealPageQueryDTO);
    return new PageResult(page.getTotal(), page.getResult());
}
2.2.4 SetmealMapper
/**
     * 分页查询
     * @param setmealPageQueryDTO
     * @return
*/
Page pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
2.2.5 SetmealMapper.xml

 

3. 删除套餐

3.1 需求分析和设计

项目实战——苍穹外卖(Day4)_第8张图片

业务规则:

  • 可以一次删除一个套餐,也可以批量删除套餐

  • 起售中的套餐不能删除

接口设计:

项目实战——苍穹外卖(Day4)_第9张图片

3.2 代码实现

3.2.1 SetmealController
/**
     * 批量删除套餐
     * @param ids
     * @return
*/
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List ids){
    setmealService.deleteBatch(ids);
    return Result.success();
}
3.2.2 SetmealService
/**
     * 批量删除套餐
     * @param ids
*/
void deleteBatch(List ids);
3.2.3 SetmealServiceImpl

 

/**
     * 批量删除套餐
     * @param ids
*/
@Transactional
public void deleteBatch(List 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);
    });
}
3.2.4 SetmealMapper
/**
     * 根据id查询套餐
     * @param id
     * @return
*/
@Select("select * from setmeal where id = #{id}")
Setmeal getById(Long id);

/**
     * 根据id删除套餐
     * @param setmealId
*/
@Delete("delete from setmeal where id = #{id}")
void deleteById(Long setmealId);
3.2.5 SetmealDishMapper
/**
     * 根据套餐id删除套餐和菜品的关联关系
     * @param setmealId
*/
@Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")
void deleteBySetmealId(Long setmealId);

 

4. 修改套餐

4.1 需求分析和设计

产品原型:

项目实战——苍穹外卖(Day4)_第10张图片

接口设计(共涉及到5个接口):

  • 根据id查询套餐

  • 根据类型查询分类(已完成)

  • 根据分类id查询菜品(已完成)

  • 图片上传(已完成)

  • 修改套餐

项目实战——苍穹外卖(Day4)_第11张图片

 

项目实战——苍穹外卖(Day4)_第12张图片

4.2 代码实现

4.2.1 SetmealController
/**
     * 根据id查询套餐,用于修改页面回显数据
     *
     * @param id
     * @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result getById(@PathVariable Long id) {
    SetmealVO setmealVO = setmealService.getByIdWithDish(id);
    return Result.success(setmealVO);
}

/**
     * 修改套餐
     *
     * @param setmealDTO
     * @return
*/
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO) {
    setmealService.update(setmealDTO);
    return Result.success();
}
4.2.2 SetmealService
/**
     * 根据id查询套餐和关联的菜品数据
     * @param id
     * @return
*/
SetmealVO getByIdWithDish(Long id);

/**
     * 修改套餐
     * @param setmealDTO
*/
void update(SetmealDTO setmealDTO);
4.2.3 SetmealServiceImpl
/**
     * 根据id查询套餐和套餐菜品关系
     *
     * @param id
     * @return
*/
public SetmealVO getByIdWithDish(Long id) {
    Setmeal setmeal = setmealMapper.getById(id);
    List setmealDishes = setmealDishMapper.getBySetmealId(id);

    SetmealVO setmealVO = new SetmealVO();
    BeanUtils.copyProperties(setmeal, setmealVO);
    setmealVO.setSetmealDishes(setmealDishes);
    
    return setmealVO;
}

/**
     * 修改套餐
     *
     * @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);

    List setmealDishes = setmealDTO.getSetmealDishes();
    setmealDishes.forEach(setmealDish -> {
        setmealDish.setSetmealId(setmealId);
    });
    //3、重新插入套餐和菜品的关联关系,操作setmeal_dish表,执行insert
    setmealDishMapper.insertBatch(setmealDishes);
}
4.2.4 SetmealDishMapper
/**
     * 根据套餐id查询套餐和菜品的关联关系
     * @param setmealId
     * @return
     */
    @Select("select * from setmeal_dish where setmeal_id = #{setmealId}")
    List getBySetmealId(Long setmealId);

        update setmeal
        
            name=#{name},
            category_id=#{categoryId},
            price=#{price},
            image=#{image},
            description=#{description},
            status=#{status},
            update_time=#{updateTime},
            update_user=#{updateUser},
        
        where id=#{id}
    

5. 起售停售套餐

5.1 需求分析和设计

项目实战——苍穹外卖(Day4)_第13张图片

业务规则:

  • 可以对状态为起售的套餐进行停售操作,可以对状态为停售的套餐进行起售操作

  • 起售的套餐可以展示在用户端,停售的套餐不能展示在用户端

  • 起售套餐时,如果套餐内包含停售的菜品,则不能起售

接口设计:

项目实战——苍穹外卖(Day4)_第14张图片 

5.2 代码实现

5.2.1 SetmealController
/**
     * 套餐起售停售
     * @param status
     * @param id
     * @return
*/
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
public Result startOrStop(@PathVariable Integer status, Long id) {
    setmealService.startOrStop(status, id);
    return Result.success();
}
5.2.2 SetmealService
/**
     * 套餐起售、停售
     * @param status
     * @param id
*/
void startOrStop(Integer status, Long id);
5.2.3 SetmealServiceImpl
/**
     * 套餐起售、停售
     * @param status
     * @param id
*/
public void startOrStop(Integer status, Long id) {
    //起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
    if(status == StatusConstant.ENABLE){
        //select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?
        List dishList = dishMapper.getBySetmealId(id);
        if(dishList != null && dishList.size() > 0){
            dishList.forEach(dish -> {
                if(StatusConstant.DISABLE == dish.getStatus()){
                    throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                }
            });
        }
    }

    Setmeal setmeal = Setmeal.builder()
        .id(id)
        .status(status)
        .build();
    setmealMapper.update(setmeal);
}
5.2.4 DishMapper
/**
     * 根据套餐id查询菜品
     * @param setmealId
     * @return
*/
@Select("select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #{setmealId}")
List getBySetmealId(Long setmealId);

你可能感兴趣的:(苍穹外卖,学习,java,spring,boot,mybatis)