[自用,更新自day5]瑞吉外卖代码及笔记

ops/2024/11/2 20:31:43/

文章目录

    • day1
      • 数据库环境搭建
      • 配置WebMvcConfig
      • 返回结果类
      • 登录/退出登录
    • day2
      • 完善登录功能
      • 新增员工
      • 分页查询员工信息
      • 启用/禁用员工信息
      • 编辑员工信息
    • day3
      • 公共字段自动填充
      • 新增分类
      • 分类信息分页查询
      • 删除分类
      • 修改分类
    • day4
      • 文件上传下载
      • 新增菜品
      • 菜品信息分页查询
      • 修改菜品
    • day5
      • 新增套餐
      • 分页查询
      • 删除套餐

day1

数据库环境搭建

导入表结构,命令行中: (注意目录中不要有中文字符)

可以直接拖动文件进去

mysql> source D:\db\xxx

配置WebMvcConfig

如果不放到static文件夹下,是无法访问到我们的静态资源的。

此时可以通过这个配置类进行静态资源映射。

注意点:

@Configuration 声明是配置类 且要放在config文件夹下

config文件夹与启动类同级

@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport{/*** 配置静态资源访问*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("配置静态资源访问");registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");}
}

弹幕:

Dao是ssm基于jdbc的,需要在impl中实现具体函数。Mapper是Mybatis的,只需要接口映射xml就可以

//controller
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;
}//service
public interface EmployeeService extends IService<Employee> {}//serviceImpl
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {}
/*
1. 实现了 EmployeeService 接口。这意味着该类需要提供接口中定义的所有方法的具体实现
2. 通过继承 ServiceImpl ,以使用 MyBatis-Plus 提供的通用服务方法
3. ServiceImpl 是 MyBatis-Plus 提供的一个基础实现类,包含了对数据库的基本 CRUD 操作
*///mapper
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {}
/*
1. Mapper注解 允许 MyBatis 自动生成实现类来执行 SQL 操作
2. BaseMapper 是 MyBatis-Plus 提供的一个通用 Mapper 接口,获得了一系列的 CRUD 方法(如 insert, delete, update, selectById 等),这些方法与 Employee 实体类相对应。
*/

返回结果类

@Data
public class R<T> {private Integer code; //编码:1成功,0和其它数字为失败private String msg; //错误信息private T data; //数据private Map map = new HashMap(); //动态数据public static <T> R<T> success(T object) {R<T> r = new R<T>();r.data = object;r.code = 1;return r;}public static <T> R<T> error(String msg) {R r = new R();r.msg = msg;r.code = 0;return r;}public R<T> add(String key, Object value) {this.map.put(key, value);return this;}
}

登录/退出登录

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;@PostMapping("/login")public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {String password = employee.getPassword();password = DigestUtils.md5DigestAsHex(password.getBytes());LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Employee::getUsername, employee.getUsername());Employee emp = employeeService.getOne(queryWrapper);if (emp == null) {return R.error("用户名不存在");}if (!emp.getPassword().equals(password)) {return R.error("密码错误");}if(emp.getStatus()==0){return R.error("账号已被禁用");}request.getSession().setAttribute("employee", emp);return R.success(emp);}@PostMapping("/logout")public R<String>logout(HttpServletRequest request){request.getSession().removeAttribute("employee");return R.success("退出成功");}
}

day2

完善登录功能

实现步骤:

1.自定义过滤器

2.在启动类上加入注解@ServletComponentScan

3.完善过滤器

package com.itheima.reggie.filter;
import java.io.IOException;import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import jakarta.servlet.ServletException;
import org.springframework.util.AntPathMatcher;@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {//路径匹配器,支持通配符public static final AntPathMatcher pathMatcher = new AntPathMatcher();@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//向下转型//浏览器传进来的参数是HttpServletRequest类型,而ServletRequest是HttpServletRequest的父类//我们用这个父类接收,所以要向下转型,才能使用子类的方法HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse  response= (HttpServletResponse) servletResponse;String requestURI = request.getRequestURI();log.info("requestURI:{}",requestURI);//2.定义不需要处理的路径String[] urls = {"/employee/login","/employee/logout","/backend/**","/frontend/**",};//3.如果不需要处理,则直接放行if(checkUrl(requestURI, urls)){filterChain.doFilter(request, response);return;}//4.如果需要处理,则判断是否登录Object employee = request.getSession().getAttribute("employee");if(employee!=null){log.info("已登录,用户信息:{}",employee);filterChain.doFilter(request, response);return;}//5.如果没有登录,则通过输出流向客户端页面响应数据log.info("未登录,拦截请求:{}",requestURI);response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));return;}public boolean checkUrl(String requestURI, String[] urls) {for (String url : urls) {if (pathMatcher.match(url, requestURI)) {return true;}}return false;}
}

新增员工

//GlobalExceptionHandler.java
//全局异常处理器@ControllerAdvice(annotations = {RestController.class, Controller.class})//无论是Controller还是RestController都会被拦截
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(SQLIntegrityConstraintViolationException.class)public R<String>exceptionHandler(SQLIntegrityConstraintViolationException e){log.error(e.getMessage());if(e.getMessage().contains("Duplicate entry")){String[] split = e.getMessage().split(" ");String msg =split[2]+"已存在";return R.error(msg);}return R.error("未知错误");}
}
//EmployeeController.java
@PostMappingpublic R<String> save(HttpServletRequest request, @RequestBody Employee employee) {log.info("employee:{}", employee.toString());employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());//获得当前登录用户idObject emp = request.getSession().getAttribute("employee");Long empId = ((Employee) emp).getId();employee.setCreateUser(empId);employee.setUpdateUser(empId);employeeService.save(employee);return R.success("保存成功");}

分页查询员工信息

//EmployeeController.java
@GetMapping("/page")public R<Page> page(int page, int pageSize, String name){log.info("page:{},pageSize:{},name:{}",page,pageSize,name);//构建分页构造器Page pageInfo = new Page(page,pageSize);//条件构造器LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();//过滤条件queryWrapper.like(name!=null,Employee::getName,name);//添加排序条件queryWrapper.orderByDesc(Employee::getUpdateTime);employeeService.page(pageInfo,queryWrapper);//执行查询return R.success(pageInfo);}
//MybatisPlusConfig
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}
}

启用/禁用员工信息

问题:前端js处理数字最多存16位,导致前段返回的Id与数据库不一致

解决方法:服务端用消息转换器

具体实现步骤:
1)提供对象转换器JacksonObjectMapper,基于jackson进行Java对象到json数据的转换

//一个工具类
/*** 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]*/
public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";public JacksonObjectMapper() {super();//收到未知属性时不报异常this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时,属性不存在的兼容处理this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);SimpleModule simpleModule = new SimpleModule().addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))).addSerializer(BigInteger.class, ToStringSerializer.instance).addSerializer(Long.class, ToStringSerializer.instance).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//注册功能模块 例如,可以添加自定义序列化器和反序列化器this.registerModule(simpleModule);}
}

2)在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行java对象到json数据的转换

//JaksonObjectMapper.java/*** 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]*/
@Component
public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";public JacksonObjectMapper() {super();//收到未知属性时不报异常this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时,属性不存在的兼容处理this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);SimpleModule simpleModule = new SimpleModule().addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))).addSerializer(BigInteger.class, ToStringSerializer.instance).addSerializer(Long.class, ToStringSerializer.instance).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//注册功能模块 例如,可以添加自定义序列化器和反序列化器this.registerModule(simpleModule);}
}
//WebMvcConfig.java
/*** 扩展mvc框架的消息转换器*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展mvc框架的消息转换器");//创建消息转换器对象MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();//设置对象转换器,底层使用Jackson将Java对象转换为jsonmessageConverter.setObjectMapper(new JacksonObjectMapper());//将上面的消息转换器添加到mvc框架的消息转换器集合中converters.add(0,messageConverter);//index=0说明是优先使用}

弹幕里面的其他方法:

1.在实体类中的id上添加注解:@JsonSerialize(using=ToStringSerializer.class)

2.在实体类中的id上添加注解:

@JsonFormat(shape=JsonFormat.Shape.STRING)

编辑员工信息

编辑和新增共用一个页面 只要新增一个返回员工信息的接口就可以了

//EmploeeController.java
@GetMapping("/{id}")public R<Employee> findById(@PathVariable Long id){log.info("根据Id查询员工信息,id:{}",id);Employee employee = employeeService.getById(id);if(employee==null){return R.error("员工不存在");}return R.success(employee);}

day3

公共字段自动填充

创建人、创建时间、修改时间、修改人是公共字段

Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。
实现步骤:
1、在实体类的属性上加入@TableField注解,指定自动填充的策略

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

2、按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

问题:怎么在这个元数据对象处理器接口获取当前用户id

ThreadLocal类

在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:
1、LoginCheckFilter的doFilter方法
2、Employeecontroller的update方法
3、MyMetaObjectHandler的updaeFill方法
可以在上面的三个方法中分别加入下面代码(获取当前线程id):

long id = Thread.currentThread().getId():
log.info("线程id:{}.id);

执行编辑员工功能进行验证,通过观察控制台输出可以发现,一次请求对应的线程id是相同的:

  • 什么是ThreadLocal?

  • ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本

  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

  • ThreadLocal常用方法:

    • public void set(T value)
      • 设置当前线程的线程局部变量的值
    • public T get()
      • 返回当前线程所对应的线程局部变量的值
  • 解决步骤:

    • 我们可以在LoginCheckfilter的doFilter方法中获取当前登录用户id
    • 并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id)
    • 然后在MyMeta0bjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。

实现步骤:

实现步骤:
1、编写BaseContext工具类,基于ThreadLocal封装的工具类

package com.itheima.reggie.common;public class BaseContext {private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}
}

2、在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id

        Employee employee = (Employee) request.getSession().getAttribute("employee");if(employee!=null){BaseContext.setCurrentId(employee.getId());log.info("已登录,用户信息:{}",employee);filterChain.doFilter(request, response);return;}

3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("公共字段自动填充[insertFill]");metaObject.setValue("createTime", LocalDateTime.now()) ;metaObject.setValue("updateTime", LocalDateTime.now()) ;metaObject.setValue("createUser", BaseContext.getCurrentId()) ;metaObject.setValue("updateUser", BaseContext.getCurrentId()) ;}@Overridepublic void updateFill(MetaObject metaObject) {log.info("公共字段自动填充[updateFill]");metaObject.setValue("updateTime", LocalDateTime.now()) ;metaObject.setValue("updateUser", BaseContext.getCurrentId()) ;}
}

新增分类

 @PostMappingpublic R<String> save(@RequestBody Category category) {log.info("保存分类信息:{}", category);categoryService.save(category);return R.success("保存成功");}

分类信息分页查询

@GetMapping("/page")public R page(int page,int pageSize) {Page<Category> pageInfo = new Page<>(page, pageSize);LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.orderByAsc(Category::getSort);categoryService.page(pageInfo, queryWrapper);return R.success(pageInfo);}

删除分类

//Contoller
@DeleteMappingpublic R<String> delete(Long ids) {log.info("删除分类信息:{}", ids);categoryService.removeById(ids);return R.success("删除成功");
}
//Service
public interface CategoryService extends IService<Category> {public void remove(Long id);
}
//ServiceImpl
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService{@Autowiredprivate DishService dishService;@Autowiredprivate SetmealService setmealService;@Overridepublic void remove(Long id) {LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);int count1 = (int) dishService.count(dishLambdaQueryWrapper);if (count1 > 0) {throw new RuntimeException("该分类下有菜品,不能删除");}LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);int  count2 = (int) setmealService.count(setmealLambdaQueryWrapper);if(count2 > 0){throw new RuntimeException("该分类下有套餐,不能删除");}}
}

修改分类

  @PutMappingpublic R<String> update(@RequestBody Category category) {log.info("更新分类信息:{}", category);categoryService.updateById(category);return R.success("更新成功");}

day4

文件上传下载

文件上传

  • 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
    • commons-fileupload
    • commons-io
  • Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明-个MultipartFile类型的参数即可接收上传的文件,例如:

文件下载

  • 文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程

  • 通过浏览器进行文件下载,通常有两种表现形式:

    • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
    • 直接在浏览器中打开

    通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。

import org.springframework.beans.factory.annotation.Value;
@Value("${reggie.path}")
//注意导的是这个包
//CommonController
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {//file是一个临时文件,需要转存到指定位置,否则本次请求完成后,文件会被删除@Value("${reggie.path}")private String basePath;//上传文件@PostMapping("/upload")public R<String> upload(MultipartFile file) {log.info("上传文件:{}", file.getOriginalFilename());String originalFilename = file.getOriginalFilename();String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));//使用UUID生成文件名String fileName = UUID.randomUUID().toString() + suffix;File dir = new File(basePath);if(!dir.exists()) {dir.mkdirs();}try {//将文件保存到指定位置file.transferTo(new File(basePath + fileName));}catch (Exception e) {log.error("上传文件失败", e.getMessage());}return R.success(fileName);}//下载文件@GetMapping("/download")public void download(String name, HttpServletResponse response) {//输入流 读取文件try{FileInputStream fis = new FileInputStream(basePath + name);ServletOutputStream os = response.getOutputStream();response.setContentType("image/jpeg");byte[] bytes = new byte[1024];int len = 0;while((len = fis.read(bytes)) != -1) {os.write(bytes, 0, len);os.flush();}//关闭资源fis.close();os.close();}catch (Exception e) {log.error("下载文件失败", e.getMessage());}}}

新增菜品

DTO,全称为Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输。

//DishDto
@Data
public class DishDto extends Dish {private List<DishFlavor> flavors = new ArrayList<>();private String categoryName;private Integer copies;
}
//DishController
@RequestMapping("/dish")
@RestController
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;@PostMappingpublic R<String> save(@RequestBody DishDto dishDto){log.info("dishDto:{}",dishDto.toString());dishService.saveWishFlavour(dishDto);return R.success("新增菜品成功");}
}
//DishService
public interface DishService extends IService<Dish> {//新增菜品 同时插入菜品对应的口味数据 要操作两张表:dish、dish_flavorpublic void saveWishFlavour(DishDto dishDto);
}

//DishServiceImpl
@Service
public class DishServiceImpl  extends ServiceImpl<DishMapper, Dish> implements DishService {@Autowiredprivate DishFlavorService dishFlavorService;@Override@Transactionalpublic void saveWishFlavour(DishDto dishDto) {//新增菜品 同时插入菜品对应的口味数据 要操作两张表:dish、dish_flavorthis.save(dishDto);//调用 ServiceImpl 类中的 save 方法Long dishId = dishDto.getId();List<DishFlavor> flavors = dishDto.getFlavors();flavors.stream().map((item)->{item.setDishId(dishId);return item;}).collect(Collectors.toList());dishFlavorService.saveBatch(flavors);}}

菜品信息分页查询

因为在分页查询的Dish的records(菜品记录中),只有这个菜品所属的categoryId,但是我们需要分页的时候展示的是菜品名字。

又因为DishDto里面有分类名称,所以改成返回的是DishDto

 @GetMapping("/page")public R<Page> page(int page,int pageSize,String name){Page<Dish> pageInfo = new Page<>(page,pageSize);//存储原始菜品Dish的分页信息Page<DishDto> dishDtoPage = new Page<>(page,pageSize);//存储转换后的菜品分页信息LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.like(name!=null,Dish::getName,name);//如,则查询果 name 不为空,则查询queryWrapper.orderByDesc(Dish::getCreateTime);dishService.page(pageInfo,queryWrapper);//对象拷贝BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");//将pageInfo中的属性 除了records复制到dishDtoPage中 List<Dish> records = pageInfo.getRecords();//获取菜品记录列表//将每个Dish转换为DishDto/*1.创建一个新的DishDto示例2.复制Dish的属性到DishDto3.根据categoryId来获取相应的Category对象,并设置名称到DishDto中*/List<DishDto> list =  records.stream().map((item)->{DishDto dishDto = new DishDto();// 1BeanUtils.copyProperties(item,dishDto);// 2Long categoryId = item.getCategoryId(); //3Category category = categoryService.getById(categoryId);String categoryName = category.getName();dishDto.setCategoryName(categoryName);return dishDto;}).collect(Collectors.toList());dishDtoPage.setRecords(list);//将转换后的列表设置到dishDtoPage的记录中return R.success(dishDtoPage);}

修改菜品

  • 单个菜品信息回显
    • 查询dish
    • 把dish转换为dishDto
    • 通过菜品id 查询dishFlavor
    • 把口味加到dishDto上
//controller@GetMapping("/{id}")public R<DishDto> get(@PathVariable Long id){DishDto dishDto = dishService.getByIdWithFlavor(id);return R.success(dishDto);}//servicepublic DishDto getByIdWithFlavor(Long id);//serviceImpl
public DishDto getByIdWithFlavor(Long id) {Dish dish = this.getById(id);DishDto dishDto = new DishDto();BeanUtils.copyProperties(dish,dishDto);LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(DishFlavor::getDishId,dish.getId());List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);dishDto.setFlavors(flavors);return dishDto;}
  • 修改
    • 首先直接更新dish表的基本信息
    • 然后把当前口味表直接全部删除 再添加回去
//controller@PutMappingpublic R<String> update(@RequestBody DishDto dishDto){log.info("dishDto:{}",dishDto.toString());dishService.updateWithFalvor(dishDto);return R.success("修改菜品成功");}//Service
public void updateWithFalvor(DishDto dishDto);//ServiceImpl@Overridepublic void updateWithFalvor(DishDto dishDto) {//更新dish表基本信息this.updateById(dishDto);//清理当前菜品对应口味数据 dish_flavor表的delete操作dishFlavorService.remove(new LambdaQueryWrapper<DishFlavor>().eq(DishFlavor::getDishId,dishDto.getId()));//添加当前提交的口味数据 dish_flavor表的insert操作List<DishFlavor> flavors = dishDto.getFlavors();flavors = flavors.stream().map((item)->{item.setDishId(dishDto.getId());return item;}).collect(Collectors.toList());dishFlavorService.saveBatch(flavors);}

day5

新增套餐

将新增页面录入的套餐信息插入到setmeal表,还要向setmeal_dish表插入套餐和菜品关联数据,所以新增的时候涉及到两个表。

1.展示菜品分类

  @GetMapping("/list")public R<List<Dish>> list(Dish dish){LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Dish::getStatus,1);queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);List<Dish> list = dishService.list(queryWrapper);return R.success(list);}
@Override
public void saveWithDish(SetmealDto setmealDto) {//保存套餐信息 操作setmeal表 执行insert操作this.save(setmealDto);List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();setmealDishes.forEach(item -> item.setSetmealId(setmealDto.getId()));//保存套餐和菜品的关系 操作setmeal_dish表 执行insert操作setmealDishService.saveBatch(setmealDishes);
}

分页查询

 @GetMapping("/page")public R<Page> page(int page,int pageSize,String name){//分页构造器对象Page<Setmeal> pageInfo = new Page<>(page,pageSize);Page<SetmealDto> dtoPage = new Page<>();LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();//添加查询条件,根据name进行like模糊查询queryWrapper.like(name != null,Setmeal::getName,name);//添加排序条件,根据更新时间降序排列queryWrapper.orderByDesc(Setmeal::getUpdateTime);setmealService.page(pageInfo,queryWrapper);//对象拷贝BeanUtils.copyProperties(pageInfo,dtoPage,"records");List<Setmeal> records = pageInfo.getRecords();List<SetmealDto> list = records.stream().map((item) -> {SetmealDto setmealDto = new SetmealDto();//对象拷贝BeanUtils.copyProperties(item,setmealDto);//分类idLong categoryId = item.getCategoryId();//根据分类id查询分类对象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);}

删除套餐

    @Override@Transactionalpublic void removeWithDish(List<Long> ids) {LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.in(Setmeal::getId, ids);queryWrapper.eq(Setmeal::getStatus, 1);long count = this.count(queryWrapper);if(count>0) {throw new RuntimeException("删除的套餐中存在已上架的套餐,不能删除");}//删除套餐和菜品的关系 操作setmeal_dish表 执行delete操作this.removeByIds(ids);setmealDishService.remove(new LambdaQueryWrapper<SetmealDish>().in(SetmealDish::getSetmealId, ids));}

http://www.ppmy.cn/ops/130510.html

相关文章

jmeter结合ansible分布式压测--准备工作

主要思路&#xff1a;用ansible去修改施压执行机的配置&#xff0c;执行用jmeter分布式方式。准备1个host文件s-host-suzhou20.txt 部署csv&#xff0c;jmter环境&#xff0c;jmx 到所有环境上 1、修改每台施压机的bin文件中的jmeter.properties #server.rmi.ssl.disablefa…

Kaggle入门指南(Kaggle竞赛)

文章目录 Kaggle 入门指南1. Kaggle 的功能概述1.1 竞赛1.2 数据集1.3 学习与教程1.4 社区 2. 注册与设置2.1 创建账户2.2 完善个人资料 3. 探索数据集3.1 查找数据集3.2 下载数据集示例代码&#xff1a;加载数据集 3.3 数据预处理示例代码&#xff1a;数据预处理 4. 参与竞赛4…

智慧园区:机遇、发展与数字化转型

智慧园区的构建是一个系统性工程&#xff0c;它遵循着三个主要阶段的发展路径&#xff0c;以确保园区能够实现高效、智能和可持续的运营。以下是智慧园区构建的三个关键阶段&#xff1a; 第一阶段&#xff1a;全面在线化 智慧园区构建的第一步是实现全面在线化。这一阶段的核…

超好用的视频剪辑软件分享:10款剪辑软件推荐

视频剪辑软件哪个比较好用&#xff1f;无论是短视频创作者、专业剪辑师&#xff0c;还是影视后期制作团队&#xff0c;选择一款合适的视频剪辑软件至关重要。今天&#xff0c;我将为大家分享几款超好用的视频剪辑软件&#xff0c;并介绍视频剪辑的六大核心流程。 1.影忆 特点&a…

再学FreeRTOS---(任务的挂起与恢复)

目录 一.FreeRTOS任务挂起与恢复的API函数 二.任务挂起和恢复的基本操作 1.任务挂起: 2.任务恢复: 三.任务挂起与恢复的实现步骤 任务挂起的实现 任务恢复的实现 在实时操作系统FreeRTOS中&#xff0c;任务的挂起与恢复是一项重要的控制功能。任务挂起相当于暂停任务的执…

如何使用python完成数据统计分析及预测?

在当今数据驱动的时代,数据分析和预测已成为各行各业的重要组成部分。 Python作为一种强大的编程语言,因其简洁的语法和丰富的库而广泛应用于数据科学领域。 本文将深入探讨如何使用Python进行数据统计分析和数据预测,并提供相关的代码示例。 第一部分:数据统计分析 1.…

vscode摸鱼学习插件开发

不知道大家在摸鱼的时候&#xff0c;会不会想要学习&#xff1f; 或者有没有考公人&#xff0c;下班要学习的&#xff1f; 上班时间摸鱼&#xff0c;下班时间不够学习&#xff1f; 为此&#xff0c;我决定开发一个vscode插件&#xff0c;来刷粉笔题 粉笔插件名称&#xff1a;…

启纬科技发布6色无源电子纸手机壳InkaceE6

杭州启纬科技有限公司投稿:无源NFC技术的开创者和领导者,杭州启纬科技有限公司于北京时间2024年10月28号正式发布了面向iOS系统和安卓/鸿蒙系统的6色无源电子纸手机壳---InkaceE6系列产品及配套方案。 图1:手机壳高清图 图2:6色与3色、4色效果对比图,海边美女 启纬…