瑞吉外卖学习笔记6

本章内容介绍

目录

  1. 新增套餐
  2. 套餐信息分页查询
  3. 删除套餐

业务开发新增套餐_需求分析&数据模型

需求分析

  • 套餐就是菜品的集合
  • 后台系统中可以管理套餐信息,通过新增套餐功能来添加一个新的套餐
  • 在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品,并且需要上传套餐对应的图片
  • 在移动端会按照套餐分类来展示对应的套餐。

数据模型

  1. 新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal_dish表插入套餐和菜品关联数据
  2. 所以在新增套餐时,涉及到两个表:

setmeal----套餐表
setmeal_dish----套餐菜品关系表

setmeal
在这里插入图片描述
在这里插入图片描述

setmeal_dish
在这里插入图片描述
在这里插入图片描述

新增套餐_代码开发_准备工作&梳理交互过程

代码开发-准备工作

在开发业务功能前,先将需要用到的类和接口基本结构创建好:

1、实体类SetmealDish
2、DTO SetmealDto
3、Mapper接口SetmealDishMapper
4、业务层接口SetmealDishService
5、业务层实现类SetmealDishservicelmpl
6、控制层SetmealController

SetmealDish—实体类

package com.itzq.reggie.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 套餐菜品关系
 */
@Data
public class SetmealDish implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //套餐id
    private Long setmealId;


    //菜品id
    private Long dishId;


    //菜品名称 (冗余字段)
    private String name;

    //菜品原价
    private BigDecimal price;

    //份数
    private Integer copies;


    //排序
    private Integer sort;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //是否删除
    private Integer isDeleted;
}


DTO SetmealDto—数据传输对象

package com.itzq.reggie.dto;


import com.itzq.reggie.entity.Setmeal;
import com.itzq.reggie.entity.SetmealDish;
import lombok.Data;
import java.util.List;

@Data
public class SetmealDto extends Setmeal {

    private List<SetmealDish> setmealDishes;

    private String categoryName;
}


SetmealDishMapper接口

package com.itzq.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itzq.reggie.entity.SetmealDish;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SetmealDishMapper extends BaseMapper<SetmealDish> {
}

SetmealDishService接口

package com.itzq.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itzq.reggie.entity.SetmealDish;

public interface SetmealDishService extends IService<SetmealDish> {
}

SetmealDishservicelmpl实现类

package com.itzq.reggie.service.Impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itzq.reggie.entity.SetmealDish;
import com.itzq.reggie.mapper.SetmealDishMapper;
import com.itzq.reggie.service.SetmealDishService;
import org.springframework.stereotype.Service;

@Service
public class SetmealDishServiceImpl extends ServiceImpl<SetmealDishMapper,SetmealDish> implements SetmealDishService {
}


SetmealController控制层

package com.itzq.reggie.controller;

import com.itzq.reggie.service.SetmealDishService;
import com.itzq.reggie.service.SetmealService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/setmeal")
@Slf4j
public class SetmealController {
    @Autowired
    private SetmealService setmealService;
    
    @Autowired
    private SetmealDishService setmealDishService;
    
}

代码开发-梳理交互过程

在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程:

1、页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
2、页面发送ajax请求,请求服务端,获取菜品分类数据并展示到添加菜品窗口中
3、页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
4、页面发送请求进行图片上传,请求服务端将图片保存到服务器
5、页面发送请求进行图片下载,将上传的图片进行回显
6、点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端

开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可

新增套餐_代码开发_根据分类查询菜品

前端分析

启动项目,进入套餐管理,点击新建套餐,会发现页面发送的请求未被服务端接收
在这里插入图片描述

爆系统接口异常,服务端未定义查询菜品的方法
在这里插入图片描述

相关代码

在DishController类中,添加list方法
注意:需要添加额外的查询条件,只查询status为1的数据,表示该菜品为起售状态,才能被加入套餐中,供用户选择

    @GetMapping("/list")
    public R<List<Dish>> list(Dish dish){

        //构造查询条件
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        //添加条件,查询状态为1(1为起售,0为停售)的菜品
        queryWrapper.eq(Dish::getStatus,1);

        List<Dish> list = dishService.list(queryWrapper);
        //添加排序条件
        return R.success(list);
    }

重启项目,发现查询数据成功,并回显到前端页面

在这里插入图片描述

新增套餐_代码开发_服务端接收页面提交的数据

前端分析

启动项目,来到添加套餐页面,输入数据,点击保存
在这里插入图片描述
查看前端页面发送的请求,请求方式

在这里插入图片描述
前端页面传输json数据给服务端

在这里插入图片描述

相关代码(测试版)

根据前端传过来的数据我们可以在后端确定我们需要在后端使用什么来接受前端的参数;

在SetmealController类中添加save方法。编写controller:上面的dishList,我们数据库并不需要这个数据,所以接收数据的实体类没有dishList这个属性也没有关系,前端传过来的数据都是自动映射到接收数据的实体类的属性上的,没有对应起来就不会映射。

    /**
     * 新增套餐
     * 涉及两张表的操作:套餐表和菜品表;
     * @param setmealDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody SetmealDto setmealDto){
 
        setmealService.saveWithDish(setmealDto);
 
        return R.success("新增套餐成功");
    }

SetmealService中添加自定义的方法:

/**
 * 新增套餐,同时需要保存套餐和菜品的关联关系
 * @param setmealDto
 */
void saveWithDish(SetmealDto setmealDto);
@Autowired
SetmealDishService setmealDishService;
 
/**
 * 新增套餐,同时需要保存套餐和菜品的关联关系
 * @param setmealDto
 */
@Transactional
@Override
public void saveWithDish(SetmealDto setmealDto) {
    //保存套餐的基本信息,操作setmeal,执行insert
    this.save(setmealDto);
    log.info(setmealDto.toString()); //查看一下这个套餐的基本信息是什么
 
    //保存套餐和菜品的关联信息,操作setmeal_dish ,执行insert操作
    List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
    //注意上面拿到的setmealDishes是没有setmeanlId这个的值的,通过debug可以发现
    setmealDishes.stream().map((item)->{
        item.setSetmealId(setmealDto.getId());
        return item; //这里返回的就是集合的泛型
    }).collect(Collectors.toList());
 
    setmealDishService.saveBatch(setmealDishes); //批量保存
}

功能测试,自己测试;

套餐信息分页查询_需求分析&梳理交互过程

需求分析

  • 系统中的套餐数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看
  • 一般的系统中都会以分页的方式来展示列表数据

梳理交互过程

在开发代码之前,需要梳理一下套餐分页查询时前端页面和服务端的交互过程:

1、页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据。
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示。

开发套餐信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。

套餐信息分页查询_代码开发&功能测试

前端分析

点击套餐管理,前端页面发送ajax请求,请求方式:get

在这里插入图片描述

代码开发

SetmealController类中,添加list方法

/**
 * 套餐分页查询
 * @param page
 * @param pageSize
 * @param name
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name){
 
    //分页构造器对象
    Page<Setmeal> pageInfo = new Page<>(page,pageSize);
    //构造条件查询对象
    LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
    //添加查询条件,根据name进行like模糊查询
    queryWrapper.like(name != null,Setmeal::getName,name);
    //添加排序条件,根据更新时间降序排列
    queryWrapper.orderByDesc(Setmeal::getUpdateTime);
    setmealService.page(pageInfo,queryWrapper);
 
    /**
     * 注意如果这里直接返回R.success(pageInfo),
     * 虽然不会报错但是分页的数据的套餐分类的名字是显示不了的;
     * 因为这个分页的泛型是Setmeal,Setmeal只封装了f分类的Id categoryId,没有分类的名称 name
     * 所以又需要进行name的获取和设值
     */
    return R.success(pageInfo);
}

bug修复

/**
 * 套餐分页查询
 * @param page
 * @param pageSize
 * @param name
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name){
 
    //分页构造器对象
    Page<Setmeal> pageInfo = new Page<>(page,pageSize);
    Page<SetmealDto> dtoPage = new Page<>(page,pageSize);
 
    //构造条件查询对象
    LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
    //添加查询条件,根据name进行like模糊查询
    queryWrapper.like(name != null,Setmeal::getName,name);
    //添加排序条件,根据更新时间降序排列
    queryWrapper.orderByDesc(Setmeal::getUpdateTime);
    setmealService.page(pageInfo,queryWrapper);
 
    //对象的拷贝  注意这里要把分页数据的全集合records给忽略掉
    BeanUtils.copyProperties(pageInfo,dtoPage,"records");
    List<Setmeal> records = pageInfo.getRecords();
 
    //对records对象进行处理然后封装好赋值给list
    List<SetmealDto> list = records.stream().map((item)->{
        SetmealDto setmealDto = new SetmealDto();
 
        //对setmealDto进行除categoryName的属性进行拷贝(因为item里面没有categoryName)
        BeanUtils.copyProperties(item,setmealDto);
 
        //获取分类id  通过分类id获取分类对象  然后再通过分类对象获取分类名
        Long categoryId = item.getCategoryId();
 
        //根据分类id获取分类对象  判断是否为null
        Category category = categoryService.getById(categoryId);
 
        if (category != null){
            String categoryName = category.getName();
            setmealDto.setCategoryName(categoryName);
        }
        return setmealDto;
    }).collect(Collectors.toList());
    dtoPage.setRecords(list);
 
    return R.success(dtoPage);
}

注意
在套餐管理界面,套餐分类字段显示的是categoryId对应的中文,但在数据库里查询到的是categoryId,因此需要利用categoryId查询到categoryName,并赋值给数据传输对象SetmealDto

删除套餐

需求分析

1、在套餐管理列表页面点击删除按钮,可以删除对应的套餐信息
2、也可以通过复选框选择多个套餐,点击批量删除按钮一次删除多个套餐
3、注意,对于状态为售卖中的套餐不能删除,需要先停售,然后才能删除。

梳理交互过程

在开发代码之前,需要梳理一下删除套餐时前端页面和服务端的交互过程:

1、删除单个套餐时,页面发送ajax请求,根据套餐id删除对应套餐
在这里插入图片描述
2、删除多个套餐时,页面发送ajax请求,根据提交的多个套餐id删除对应套餐开发删除套餐功能

在这里插入图片描述
注意
1、观察删除单个套餐和批量删除套餐的请求信息可以发现,两种请求的地址和请求方式都是相同的
2、不同的则是传递的id个数,所以在服务端可以提供一个方法来统一处理。

删除套餐_代码开发&功能测试

controller层开发

在SetmealService中添加自定义的方法:

/**
 * 删除套餐,同时需要删除套餐和菜品的关联数据
 * @param ids
 */
void removeWithDish(List<Long> ids);

实现该方法:

/**
 * 删除套餐,同时需要删除套餐和菜品的关联数据
 * @param ids
 */
@Override
@Transactional
public void removeWithDish(List<Long> ids) {
    //sql语句应该是这样的:select count(*) setmeal where id in () and status = 1;
    //查询套餐的状态,看是否可以删除
    LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper();
    queryWrapper.in(Setmeal::getId,ids);
    queryWrapper.eq(Setmeal::getStatus,1);
    int count = this.count(queryWrapper);
    //如果不能删除,抛出一个业务异常
    if (count > 0){
        throw new CustomException("套餐正在售卖中,不能删除");
    }
    //如果可以删除,先删除套餐表中的数据--setmeal 
    this.removeByIds(ids);
    //删除关系表中的数据--setmeal_dish
    //delete from setmeal_dish where setmeal_id in (1,2,3)
    LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper();
    lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
    setmealDishService.remove(lambdaQueryWrapper);
}

功能测试;

套餐管理的启售,停售

前端发来的请求:

在这里插入图片描述

根据前面菜品模块自己实现的功能,我们可以知道,我们只需要写一个批量处理的方法就可以完成单个或者是批量套餐的启售,停售;

SetmealController中的controller层代码:

/**
 * 对菜品批量或者是单个 进行停售或者是起售
 * @return
 */
@PostMapping("/status/{status}")
//这个参数这里一定记得加注解才能获取到参数,否则这里非常容易出问题
public R<String> status(@PathVariable("status") Integer status,@RequestParam List<Long> ids){
    setmealService.updateSetmealStatusById(status,ids);
    return R.success("售卖状态修改成功");
}

SetmealService中添加下面方法:

/**
 * 根据套餐id修改售卖状态
 * @param status
 * @param ids
 */
void updateSetmealStatusById(Integer status,List<Long> ids);

该方法的实现:

/**
 * 根据套餐id修改售卖状态
 * @param status
 * @param ids
 */
@Override
public void updateSetmealStatusById(Integer status,  List<Long> ids) {
    LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper();
    queryWrapper.in(ids !=null,Setmeal::getId,ids);
    List<Setmeal> list = this.list(queryWrapper);
 
    for (Setmeal setmeal : list) {
        if (setmeal != null){
            setmeal.setStatus(status);
            this.updateById(setmeal);
        }
    }
}

套餐管理的修改

1、分为两步:数据回显示,和提交修改数据到数据库
2、前端点击套餐修改,前端发过来的请求:
3、请求方式是:get
4、携带的参数是:stemealId
在这里插入图片描述
然后我们发现在弹出编辑窗口是没有数据的:并且报了一个404,那就是说是在数据回显的时候报错了,没有找到具体的回显接口来处理这个请求;

在这里插入图片描述

SetmealController 中添加下面的代码:

/**
 * 回显套餐数据:根据套餐id查询套餐
 * @return
 */
@GetMapping("/{id}")
public R<SetmealDto> getData(@PathVariable Long id){
    SetmealDto setmealDto = setmealService.getDate(id);
 
    return R.success(setmealDto);
}

SetmealService添加下面的代码:

/**
 * 回显套餐数据:根据套餐id查询套餐
 * @return
 */
SetmealDto getDate(Long id);

该方法的实现:

/**
 * 回显套餐数据:根据套餐id查询套餐
 * @return
 */
@Override
public SetmealDto getDate(Long id) {
    Setmeal setmeal = this.getById(id);
    SetmealDto setmealDto = new SetmealDto();
    LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper();
    //在关联表中查询,setmealdish
    queryWrapper.eq(id!=null,SetmealDish::getSetmealId,id);
 
    if (setmeal != null){
        BeanUtils.copyProperties(setmeal,setmealDto);
        List<SetmealDish> list = setmealDishService.list(queryWrapper);
        setmealDto.setSetmealDishes(list);
        return setmealDto;
    }
    return null;
}

测试:数据回显成功:

但是这样我们再点击添加菜品会发现,右边只展示菜品的价格并没有展示菜品对应的名称:

已选菜品中的菜品并没有展示对应的菜品名;
在这里插入图片描述
修改后的运行情况展示:
在这里插入图片描述
修改具体的前端代码:把backend/combo/add.html中的335行修改为下面的代码;
在这里插入图片描述
因为这里的item是代表dish对象,dish实体类是使用name作为菜品名称的;
在这里插入图片描述

修改完成后,点击保存,我们发现前端发出一个put请求:

在这里插入图片描述
携带的参数为:
在这里插入图片描述
根据前端传过来的数据和需要的返回值,我们就可以知道controller层方法的返回值和用什么参数来接收前端传给我们的数据;注意这个套餐里面的菜品也要保存修改:需要把setealDish保存到seteal_dish表中;
在这里插入图片描述

点击修改后的保存,后端会接收到下面的数据:发现setmealId == null,所以这里需要自己单独填充;
在这里插入图片描述
controller层代码:

为了不把问题复杂化,我是先把相关的setmealDish内容移除然后再重新添加,这样就可以不用考虑dish重复的问题和哪些修改哪些没修改;

@PutMapping
public R<String> edit(@RequestBody SetmealDto setmealDto){
 
    if (setmealDto==null){
            return R.error("请求异常");
        }
 
        if (setmealDto.getSetmealDishes()==null){
            return R.error("套餐没有菜品,请添加套餐");
        }
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
        Long setmealId = setmealDto.getId();
 
        LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SetmealDish::getSetmealId,setmealId);
        setmealDishService.remove(queryWrapper);
 
        //为setmeal_dish表填充相关的属性
        for (SetmealDish setmealDish : setmealDishes) {
            setmealDish.setSetmealId(setmealId);
        }
        //批量把setmealDish保存到setmeal_dish表
        setmealDishService.saveBatch(setmealDishes);
        setmealService.updateById(setmealDto);
 
        return R.success("套餐修改成功");
}

参考

https://blog.csdn.net/eadzsdad/article/details/124298918
https://blog.csdn.net/weixin_53142722/article/details/124371940
https://blog.csdn.net/weixin_53142722/article/details/124356412

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peihj2021

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值