文章目录
- 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()
- 返回当前线程所对应的线程局部变量的值
- public void set(T value)
-
解决步骤:
- 我们可以在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));}