文章目录
一、session注册时
刚开始使用的是该代码
httpServletRequest.setAttribute("employee",emp.getId());
导致我点击登录后,还是一直在登录页面,进不去主页面。
原因是:
我使用的那行代码并没有保存到session里,只是保存到当前请求中。当请求别的页面时,就会失效。
正确代码:
httpServletRequest.getSession().setAttribute("employee", emp.getId());
该代码是保存到当前会话域。
移除的代码:
httpServletRequest.getSession().removeAttribute("employee");
获取代码:
httpServletRequest.getSession().getAttribute("employee")
当时还比较迷惑一个地方,就是 已经已经排除掉以下页面,就是以下页面直接放行。但是当我请求/backend/index.html 时并不会被直接放行,还是需要先登录,经过检查,
String[] urls=new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
原因是:
index.html页面 会直接默认请求一个页面,
请求该页面就会被过滤器拦截。
二、用户增加时
ControllerAdvice
注解给忘记了,通过该注解可以配置一个全局的信息,其中有一个属性是annotations,: 允许你指定一个或多个类(类型数组),只有继承或实现了这些类的控制器才会受到此 advice 影响。
用途:专门用于处理控制器层的全局异常处理、数据绑定和数据预处理。
应用范围:仅限于 Spring MVC 的控制器层。
原始代码: 使用的是ControllerAdvice
package com.cky.exceptions;
import com.cky.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @ClassName MyExceptions
* @Description TODO
* @Author lukcy
* @Date 2024/6/21 15:51
* @Version 1.0
*/
@Slf4j
@ResponseBody
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class MyExceptions {
/**
* 异常处理方法
* @param ex
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> EmpExceptions(SQLIntegrityConstraintViolationException ex) {
if (ex.getMessage().contains("")) {
String[] split = ex.getMessage().split("");
String msg = split[2] + "已存在";
return R.error(msg);
}return R.error("未知错误");
}}
修改后
我没有使用该全局异常,而是在保存时先判断是否存在
public interface EmployeeService extends IService<Employee> {
boolean isUsernameExist(String username);
}
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
/**
* 检查数据库中是否存在指定的用户名
*
* @param username 要检查的用户名
* @return 如果存在返回true,否则返回false
*/
public boolean isUsernameExist(String username) {
QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
int count = employeeMapper.selectCount(queryWrapper);
return count > 0;
}
}
@PostMapping
public R<String> saveemp(HttpServletRequest httpServletRequest,@RequestBody Employee employee){
boolean usernameExist = employeeService.isUsernameExist(employee.getUsername());
if (usernameExist){
return R.error(String.format("%s 用户名已经存在", employee.getUsername()));
}
//设置默认初始密码
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
//其他字段
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
Long id = (Long) httpServletRequest.getSession().getAttribute("employee");
employee.setCreateUser(id);
employee.setUpdateUser(id);
//直接调用接口的save方法
employeeService.save(employee);
return R.success("员工添加成功");
}
能够实现同样的功能
更正
到后边,比如新增菜品或者套餐时,发现还是需要判断名字是否唯一,所以还是需要全局异常类更好些。
package com.cky.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @ClassName GlobalExceptiobHandler
* @Description TODO
* @Author lukcy
* @Date 2024/6/25 9:56
* @Version 1.0
*/
@Slf4j
@ResponseBody
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptiobHandler {
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
log.error(ex.getMessage());
if (ex.getMessage().contains("Duplicate entry")) {
String[] split = ex.getMessage().split(" ");
String msg = split[2] + " 已存在";
return R.error(msg);
}
return R.error("未知错误");
}
}
三、RequestBody
在修改用户状态时:
3.1 Long问题
js 在对Long数据传送时,只会保证前16位有效,做法:增加一个自己的消息转换器+对象转换器,把Long数据转换为String。
package com.cky.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于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);
}
}
package com.cky.config;
import com.cky.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
/**
* @ClassName WebmvcConfig
* @Description TODO
* @Author lukcy
* @Date 2024/5/29 9:40
* @Version 1.0
*/
//默认能访问类路径下的static和template文件夹 这里我们没有放在这些文件夹下 所以访问不到,需要自己配置静态资源处理器
@Configuration
@Slf4j
public class WebmvcConfig extends WebMvcConfigurationSupport {
/**
* 静态资源映射
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
/**
* 增加消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("配置的消息转换器....");
MappingJackson2HttpMessageConverter mappingJackson2CborHttpMessageConverter=new MappingJackson2HttpMessageConverter();
mappingJackson2CborHttpMessageConverter.setObjectMapper(new JacksonObjectMapper());
converters.add(0,mappingJackson2CborHttpMessageConverter);
}
}
3.2 RequestBody
刚开始在控制器上没有加上@RequestBody注解,导致传入的参数并不能赋给Employee类。
RequestBody和RequestParam区别
:
@RequestBody会将请求体中的数据,转换成对象
@RequestParam会从http请求查询参数中提取数据
@RequestParam和@RequestBody是Spring Framework中用于处理HTTP请求的注解,它们有以下区别:
1.数据来源:
@RequestParam: 从HTTP请求的查询参数中提取数据,即从URL中的?key=value形式的参数中获取数据。
@RequestBody: 从HTTP请求的请求体(body)中提取数据,通常用于接收JSON、XML等格式的数据。
2.用法:
@RequestParam: 通常用于处理GET请求或POST请求中的表单数据,例如?name=John&age=30这样的查询参数。
@RequestBody: 通常用于处理POST请求中的非表单数据,例如JSON格式的数据,或者XML格式的数据。
3.数据格式:
@RequestParam: 提取的数据一般是简单类型,如字符串、整数等。
@RequestBody: 提取的数据可以是复杂类型,如自定义的Java对象、Map、List等,通常是用于反序列化JSON或XML数据为Java对象。
3.3 UpdataById
由于Mybatis plus默认的更新策略是NOT_NULL:非 NULL;即通过接口更新数据时数据为NULL值时将不更新进数据库。所以Mybatis plus通过updateById(XXX)更新数据,当用户有更新字段为 空字符串 或者 null 的需求时,需要对 FieldStrategy 策略进行调整。
FieldStrategy 有三种策略:
IGNORED:0 忽略
NOT_NULL:1 非 NULL,默认策略
NOT_EMPTY:2 非空
四、公共填充字段
比如employee中 创建人,修改人,创建时间,修改时间都属于公共字段,我们没必要写到controller中,可以通过mybatispuls的公共填充机制。
①实体类中
还有一个问题就是我们自己编写的配置类没有办法获取http的请求,所以id如何获得呢?
解决:
使用ThreadLocal
每次http请求都对应一个线程。
package com.cky.common;
/**
* @ClassName MythreadLocal
* @Description TODO
* @Author lukcy
* @Date 2024/6/25 9:21
* @Version 1.0
*/
/**
* 线程工具类
*/
public class MythreadLocal {
//因为存放的是id 所以是lONG
private static ThreadLocal<Long> threadLocal=new ThreadLocal<>();
public static void setCurrendId(Long id){
threadLocal.set(id);
}
public static Long getCurrendId(){
return threadLocal.get();
}
}
在filter中已经登录后在threadLocal中保存id,在我们编写的元数据处理配置类中获取id。
package com.cky.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @ClassName MyMetaObjectHandler
* @Description TODO
* @Author lukcy
* @Date 2024/6/25 9:12
* @Version 1.0
*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", MythreadLocal.getCurrendId());
metaObject.setValue("updateUser",MythreadLocal.getCurrendId() );
}
@Override
public void updateFill(MetaObject metaObject) {
long id = Thread.currentThread().getId();
log.info("当前线程id{}",id);
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser",MythreadLocal.getCurrendId());
}
}
控制器中这些就可以省略了。
五、文件上传与下载
5.1 拦截器与过滤器
请求处理顺序总结
前端请求拦截器:在请求发出之前处理请求。
后端过滤器:在请求到达控制器之前进行预处理。
后端拦截器:在请求到达控制器之前进行进一步的处理。
控制器:处理请求并生成响应。
后端拦截器(响应阶段):在响应返回之前处理响应。
后端过滤器(响应阶段):在响应返回之前进行进一步的处理。
前端响应拦截器:在响应返回到客户端之前处理响应。
AJAX请求:前端拦截器(如axios拦截器)会处理请求和响应,然后请求会经过后端过滤器和拦截器,最后到达控制器。
非AJAX请求:不会经过前端拦截器,而是直接发送到服务器。请求会先经过后端过滤器和拦截器,最后到达控制器。
因此,对于非AJAX请求,前端的axios拦截器(request.use 和 response.use)将不会被触发。这些请求只会经过后端的过滤器和拦截器进行处理。
写上述是因为把upload.html 加入之后,返回的是200,相应内容
我在想,为什么这里也没有登录并且common/page 并没有在过滤器里配置,怎么没有返回到login页面。
原因:
因为emlment-upload 组件 是一个普通的http请求,该请求会经过我们的后端过滤器,所以返回了NOTLOGIN(我们在过滤器里面配置的),由于他不是一个ajax请求,所以并不会经过我们的前端拦截器的请求和相应。所以不会跳转到登录页面,我们可以看到登录页面是在前端拦截器的响应中写的。
六、菜品分类页面
①发送ajax请求,获取内容
②发送下载图片的请求(前端写好了,直接调用之前的下载图片的代码就行
首先想到的是最基本的代码:
@GetMapping("/page")
public R<Page> list(int page,int pageSize,String name){
Page<Dish> dishPage=new Page<>(page,pageSize);
LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(name!=null,Dish::getName,name);
lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
dishService.page(dishPage,lambdaQueryWrapper);
return R.success(dishPage);
}
但是我们打开页面时会发现 菜品名分类并没有显示。
理由
我们并没有categoryName这一列。
所以就需要用到之前的Dishdto
由于dishtao并没有实际对应的表,所以我们需要首先通过Dish来查到基本数据,再去填充CategoryName这一列
@GetMapping("/page")
public R<Page> list(int page,int pageSize,String name){
Page<Dish> dishPage=new Page<>(page,pageSize);
Page<DishDto> dishdtoPage=new Page<>();
LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(name!=null,Dish::getName,name);
lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
dishService.page(dishPage,lambdaQueryWrapper);
BeanUtils.copyProperties(dishPage,dishdtoPage,"records");
List<Dish> records = dishPage.getRecords();
List<DishDto> collect = records.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
//通过id获取菜品分类名称
Long categoryId = item.getCategoryId();
Category category = categoryService.getById(categoryId);
String name1 = category.getName();
dishDto.setCategoryName(name1);
return dishDto;
}).collect(Collectors.toList());
dishdtoPage.setRecords(collect);
return R.success(dishdtoPage);
}
虽然DishDto没有直接对应的数据库表,但你可以使用Dish的Mapper进行数据库操作,并在Service层或Mapper中进行数据转换和填充。这样可以保持代码的简洁和结构的清晰,同时充分利用MyBatis-Plus的功能。
七、自己实现
7.1 批量删除
批量删除记得删除菜品的同时把菜品对应的口味给删除了
DishController.java
/**
* 批量删除菜品 同时要把对应菜品的口味也对应删除
* @param ids
* @return
*/
@DeleteMapping
public R<String> delete(@RequestParam("ids") String ids){
List<Long> idList = Arrays.stream(ids.split(","))
.map(Long::valueOf)
.collect(Collectors.toList());
dishService.deletewithfalvor(idList);
return R.success("删除成功");
}
DishService.java
/**
* 批量删除菜品 同时要把对应菜品的口味也对应删除
* @param ids
* @return
*/
@DeleteMapping
public R<String> delete(@RequestParam("ids") List<Long> ids){
// List<Long> idList = Arrays.stream(ids.split(","))
// .map(Long::valueOf)
// .collect(Collectors.toList());
dishService.deletewithfalvor(ids);
return R.success("删除成功");
}
DishServiceImpl.java
/**
* 删除菜品和口味
* @param ids
*/
@Override
public void deletewithfalvor(List<Long> ids) {
//删除菜品时首先要判断 是否有套餐包含该菜品 以及该菜品是否在售
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(SetmealDish::getDishId,ids);
int count = setmealDishService.count(lambdaQueryWrapper);
//如果有套餐包含 则抛出异常
if(count>0){
throw new MyException("有套餐包含该菜品 不能删除");
}
//如果在售 则抛出异常
LambdaQueryWrapper<Dish> lambdaQueryWrapper1=new LambdaQueryWrapper<>();
lambdaQueryWrapper1.in(Dish::getId,ids);
lambdaQueryWrapper1.eq(Dish::getStatus,1);
int count1 = this.count(lambdaQueryWrapper1);
if(count1>0){
throw new MyException("有菜品正在售 不能删除");
}
//其他正常删除 菜品 以及菜品对应的口味表
this.removeByIds(ids);
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper2=new LambdaQueryWrapper<>();
lambdaQueryWrapper2.in(DishFlavor::getDishId,ids);
dishFlavorService.remove(lambdaQueryWrapper2);
}
有套餐包含菜品 就不能删除
7.2 批量起售
/**
* 批量起售
* @param ids
* @return
*/
@PostMapping("/status/1")
public R<String> start(@RequestParam("ids") List<Long> ids){
LambdaUpdateWrapper<Dish> lambdaUpdateWrapper=new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.in(Dish::getId,ids);
lambdaUpdateWrapper.set(Dish::getStatus,1);
boolean update = dishService.update(lambdaUpdateWrapper);
if (update) {
return R.success("批量起售成功");
} else {
return R.error("批量起售成功");
}
}
7.3 批量停售
如果有套餐包含 该菜品 并且菜品正在售卖 则不能停售菜品
/**
* 批量停售 需要判断 是否有套餐包含该菜品 如果该套餐在售 则不能停售
* @param ids
* @return
*/
@PostMapping("/status/0")
public R<String> stop(@RequestParam("ids") List<Long> ids) {
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(SetmealDish::getDishId, ids); // 先把包含菜品的给选出来
// 接着把该菜品对应的套餐选出来
List<SetmealDish> list = setmealDishService.list(lambdaQueryWrapper);
Set<Long> setmealIds = list.stream()
.map(SetmealDish::getSetmealId)
.collect(Collectors.toSet()); // 去重复
if (!setmealIds.isEmpty()) {
LambdaQueryWrapper<Setmeal> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
lambdaQueryWrapper1.in(Setmeal::getId, setmealIds);
lambdaQueryWrapper1.eq(Setmeal::getStatus, 1); // 需要确保套餐在售,套餐在售则菜品不能停售,否则可以
int count = setmealService.count(lambdaQueryWrapper1);
if (count > 0) {
throw new MyException("有套餐包含该菜品,正在售");
}
}
LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(Dish::getId, ids);
updateWrapper.set(Dish::getStatus, 0); // 修改状态为停售
boolean update = dishService.update(updateWrapper);
if (update) {
return R.success("状态修改成功");
} else {
return R.error("状态修改失败");
}
}
八 、套餐
8,1 保存套餐和其对应的菜品信息
/**
* 保存套餐和其对应的菜品信息
* @param setmealDto
* @return
*/
@PostMapping
public R<String> save(@RequestBody SetmealDto setmealDto){
//保存套餐和其对应的菜品信息
setmealService.savewithdishes(setmealDto);
return R.success("套餐保存成功");
}
//保存套餐和其对应的菜品信息
@Override
public void savewithdishes(SetmealDto setmealDto) {
this.save(setmealDto);//保存套餐信息
//获得套餐对应的菜品信息 并为其赋值(套餐id
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
List<SetmealDish> collect = setmealDishes.stream().map((item) -> {
item.setSetmealId(setmealDto.getId());
return item;
}).collect(Collectors.toList());
setmealDishService.saveBatch(collect);//批量保存
}
8.2 套餐分页展示
/**
* 对套餐信息进行分页展示
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
public R<Page> list(Integer page,Integer pageSize,String name){
log.info("{},{},{}",page,pageSize,name);
//首先先获取套餐的内容
Page<Setmeal> setmealPage=new Page<>();
LambdaQueryWrapper<Setmeal> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(name!=null,Setmeal::getName,name);
lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);
setmealService.page(setmealPage,lambdaQueryWrapper);
//由于我们页面展示的还有套餐所属套餐分类的名称,所以需要dto
Page<SetmealDto> setmealDtoPage=new Page<>();
//属性赋值 除了records这一列
BeanUtils.copyProperties(setmealPage,setmealDtoPage,"records");
//获取原始套餐的信息
List<Setmeal> records = setmealPage.getRecords();
//遍历原始套餐的信息并为其赋上套餐分类的名称
List<SetmealDto> collect = records.stream().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(item, setmealDto);
Long categoryId = item.getCategoryId();
Category category = categoryService.getById(categoryId);
setmealDto.setCategoryName(category.getName());
return setmealDto;
}).collect(Collectors.toList());
setmealDtoPage.setRecords(collect); //这一步一定要写 不然没有内容
return R.success(setmealDtoPage);
}
8.3 删除套餐
套餐如果在售 则不能删除
/**
* 套餐删除
* @param ids
* @return
*/
@DeleteMapping
public R<String> delete(@RequestParam List<Long> ids){
log.info("{}",ids);
setmealService.deletewithdish(ids);
return R.success("删除 成功");
}
/**
* // 删除套餐和其对应的套餐菜品表
* @param ids
*/
@Transactional
@Override
public void deletewithdish(List<Long> ids) {
//查询要删除的套餐是否有在售的
LambdaQueryWrapper<Setmeal> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(Setmeal::getId,ids);
lambdaQueryWrapper.eq(Setmeal::getStatus,1);
int count = this.count(lambdaQueryWrapper);
//如果有抛出一个异常
if(count>0){
throw new MyException("选择的套餐有在售的 不能删除");
}
//如果没有 则先删除套餐
this.removeByIds(ids);
//接着删除setmeal——dish表
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper1=new LambdaQueryWrapper<>();
lambdaQueryWrapper1.in(SetmealDish::getSetmealId,ids);
setmealDishService.remove(lambdaQueryWrapper1);
}
8.4 根据菜品类型获得所有菜品
/**
* 根据菜品的分类id获得所有菜品
* @param dish
* @return
*/
@GetMapping("/list")
public R<List<Dish>> listR(Dish dish)
{
Long id = dish.getCategoryId();
LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper();
lambdaQueryWrapper.eq(id!=null,Dish::getCategoryId,id);
lambdaQueryWrapper.eq(Dish::getStatus,1);
lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(lambdaQueryWrapper);
return R.success(list);
}
8.5 修改套餐 (自己实现
①首先发送了ajax请求,对所选择的套餐进行回显。
②点击保存,发送ajax请求,对套餐内容进行保存。
/**
*回显套餐内容
* @param id
* @return
*/
@GetMapping("{id}")
public R<SetmealDto> showandupdate(@PathVariable Long id){
//回显套餐内容进行修改
SetmealDto updatewithdishes = setmealService.get(id);
return R.success(updatewithdishes);
}
@PutMapping
public R<String> update(@RequestBody SetmealDto setmealDto){
setmealService.updatewithdiehes(setmealDto);
return R.success("修改套餐信息成功");
}
package com.cky.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cky.dto.SetmealDto;
import com.cky.pojo.Setmeal;
import java.util.List;
/**
* @ClassName SetmealService
* @Description TODO
* @Author lukcy
* @Date 2024/6/25 20:02
* @Version 1.0
*/
public interface SetmealService extends IService<Setmeal> {
// //保存套餐和其对应的菜品信息
void savewithdishes(SetmealDto setmealDto);
// 删除套餐和其对应的套餐菜品表
void deletewithdish(List<Long> ids);
SetmealDto get(Long id);
void updatewithdiehes(SetmealDto setmealDto);
}
/**
* 回显套餐内容
* @param id
*/
@Override
public SetmealDto get(Long id) {
SetmealDto setmealDto=new SetmealDto();
Setmeal setmeal1 = setmealService.getById(id);
BeanUtils.copyProperties(setmeal1,setmealDto);
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(SetmealDish::getSetmealId,id);//获得套餐对应的菜品
List<SetmealDish> list = setmealDishService.list(lambdaQueryWrapper);
setmealDto.setSetmealDishes(list);//将菜品信息也放入setmealdto
return setmealDto;
}
/**
* 修改套餐信息
* @param setmealDto
*/
@Override
public void updatewithdiehes(SetmealDto setmealDto) {
//修改套餐的基本信息
this.updateById(setmealDto);
//清除原来套餐的菜品
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(SetmealDish::getSetmealId,setmealDto.getId());
setmealDishService.remove(lambdaQueryWrapper);
//更新新的菜品
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
setmealDishes.stream().map((item->{
item.setSetmealId(setmealDto.getId());
return item;
})).collect(Collectors.toList());
setmealDishService.saveBatch(setmealDishes);
}
九、菜品
9.1 菜品展示
首先登录进入之后,共同发送了两个ajax请求,①获得所有分类并展示,②获取第一个分类的所有菜品。
由于我们在添加购物车时,需要选择相应的规格即口味信息,所以我们要对list方法进行一个重新编写,返回菜品的同时能够返回口味信息。
/**
* 根据菜品的分类id获得所有菜品
* @param dish
* @return
*/
@GetMapping("/list")
public R<List<DishDto>> listR(Dish dish)
{
Long id = dish.getCategoryId();
LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper();
lambdaQueryWrapper.eq(id!=null,Dish::getCategoryId,id);
lambdaQueryWrapper.eq(Dish::getStatus,1);
lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(lambdaQueryWrapper);//获得所有的菜品 但是为了让前端能够展示规格 我们需要将口味也返回
List<DishDto> listdto=null;
List<DishDto> collect = list.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
//通过id获取菜品分类名称
Long categoryId = item.getCategoryId();
Category category = categoryService.getById(categoryId);
String name1 = category.getName();
dishDto.setCategoryName(name1);
//根据菜品id获得 口味信息
Long dishId = item.getId();
LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper=new LambdaQueryWrapper<>();
dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
List<DishFlavor> list1 = dishFlavorService.list(dishFlavorLambdaQueryWrapper);
dishDto.setFlavors(list1);
return dishDto;
}).collect(Collectors.toList());
return R.success(collect);
}
这并不会影响我们之前后端的程序,因为我们只是多添加了一些属性,但是原来的dish信息都还在。
9.2 添加购物车
/**
* 添加购物车 前端就没有给可以多个口味的选择 只要选择了一个口味 就只能点击加号 选择同样的口味了
* 如果可以添加不同的口味 那么number就要变了 因为不能仅仅通过菜品id来number加1 如果口味不同 那就属于不同的
* @param shoppingCart
* @return
*/
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
//设置userid
shoppingCart.setUserId(MythreadLocal.getCurrendId());
//查看相同菜品或者套餐是否在数据库中 如果有直接分数加1
LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ShoppingCart::getUserId,MythreadLocal.getCurrendId());
lambdaQueryWrapper.eq(shoppingCart.getDishId()!=null,ShoppingCart::getDishId, shoppingCart.getDishId());
lambdaQueryWrapper.eq(shoppingCart.getSetmealId()!=null,ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
ShoppingCart serviceOne = shoppingCartService.getOne(lambdaQueryWrapper);
if (serviceOne!=null){
Integer number = serviceOne.getNumber();
serviceOne.setNumber(number+1);
serviceOne.setCreateTime(LocalDateTime.now());
shoppingCartService.updateById(serviceOne);
}
else {
//没有查到
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartService.save(shoppingCart);
serviceOne=shoppingCart;
}
return R.success(serviceOne);
}
9.3 减少菜品或套餐数量
/**
* 减少套餐或者菜品
* @param shoppingCart
* @return
*/
@PostMapping("/sub")
public R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart){
LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ShoppingCart::getUserId,MythreadLocal.getCurrendId());
lambdaQueryWrapper.eq(shoppingCart.getSetmealId()!=null,ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
lambdaQueryWrapper.eq(shoppingCart.getDishId()!=null,ShoppingCart::getDishId,shoppingCart.getDishId());
ShoppingCart serviceOne = shoppingCartService.getOne(lambdaQueryWrapper);
Integer number = serviceOne.getNumber();
if(number>=2){
serviceOne.setNumber(number-1);
shoppingCartService.updateById(serviceOne);
}
else
shoppingCartService.removeById(serviceOne);
return R.success(serviceOne);
}
9.4 查看购物车
/**
* 查看购物车
* @return
*/
@GetMapping("/list")
public R<List<ShoppingCart>> list(){
log.info("查看购物车");
Long currendId = MythreadLocal.getCurrendId();
LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ShoppingCart::getUserId,currendId);
lambdaQueryWrapper.orderByDesc(ShoppingCart::getCreateTime);
List<ShoppingCart> list = shoppingCartService.list(lambdaQueryWrapper);
return R.success(list);
}
9.5 清空购物车
** /**
* 清空购物车
* @return
*/
@DeleteMapping("/clean")
public R<String> clean(){
Long currendId = MythreadLocal.getCurrendId();
LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ShoppingCart::getUserId,currendId);//
shoppingCartService.remove(lambdaQueryWrapper);
return R.success("清空购物车成功");
}**