赞
踩
瑞吉外卖项目
Java项目实战《瑞吉外卖》,轻松掌握springboot + mybatis plus开发核心技术的真java实战项目
包含自己学习时候的理解 有可能不对欢迎指正,我会查看并且进行正确的修改,也有老师讲的时候的讲解
创建项目,数据库 我就不多讲解了 (项目这里导入了backend 和front包 都是前端代码)
注意:这里导入了backend 和 front 包的时候,springboot并不知道这些是静态资源,所以要编写一个配置类,配置mvc静态资源的映射 如果不编写输入时url时不出现页面
以下是springboot默认的静态资源路径
spring.resources.static-locations=
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/
编写配置类
告诉springboot 要访问backend 就是 resource下面那个backend 你直接输入
localhost:8080/backend/… 就去resource/backend/ 下面去找
另外:reources包下在建一个resources包装静态资源不能直接放在第一个reources下
这时直接输入网站就可以访问了
导入依赖如果有问题可以私信我帮助解决一下 建议配置本地maven 能解决大部分问题
然后实现相应的Empoyee 类或接口 让他们继承对应mybatis plus 提供的类
这个类里有很多我们会经常用的实现方法,所以尽管表面看起来没有神什么代码但其实已经有很多实现方法了,感兴趣的同学们可以ctrl+b 看一下
注意R类里的success和error和add方法经常用到
弱弱的说一句不知道为什么他要把方法写在controller类里,应该是service类里吧,我们这里尊重作者
@Slf4j
@RequestMapping("/employee")
// @ResponseBody 注解是将返回的数据结构转换为 Json 格式
@RestController
// @RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解
//使用了 @RestController 注解即可将返回的数据结构转换成 Json 格式,
// Spring Boot 中默认使用的 Json 解析技术框架是 jackson。
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping("/login")
//@PostMapping是@RequestMapping(method = RequestMethod.POST)缩写的组合注解,
// 用于将 HTTP 的post 请求映射到特定处理程序的方法注解。
//RequestBody 前端点登录发送的请求会带来账号密码参数 但是是json形式 接受的时候要用这个注解
//要与实体类里的username 一样
//request 是因为要存一份在session里 request可以get一个session
//这个session是服务器端共享,每个游览器(客户端)独享的。我们可以在session存储数据,实现数据共享。
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {
String pwd = employee.getPassword();
//md5解密
pwd = DigestUtils.md5DigestAsHex(pwd.getBytes());
//mybatis-plus 的方法
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
//和数据库进行判断
queryWrapper.eq(Employee::getUsername, employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
System.out.println(emp);
if (emp == null) {
return R.error("登陆失败");
}
if (!emp.getPassword().equals(pwd)) {
return R.error("密码不对");
}
if (emp.getStatus() == 0) {
return R.error("账号被锁");
}
System.out.println("登录成功");
//这里设定employee的id的key为employee
System.out.println("----这个是empoyee---" + employee);
System.out.println("----这个是emp---" + emp);
request.getSession().setAttribute("employee", emp.getId());
return R.success(emp);
}
==注意:这里我直接加上了session 原视频开始是没有的,是后来完善的,另外 request.getSession().setAttribute(“employee”, emp.getId()); 这句话传递的是emp。getId()而不是employee.id(),employee这里id是空的 所以我在发现写错了之前session一直出错… ==
我们这里用的是过滤器
@Slf4j
@WebFilter(filterName = "LoginCheckfilter", urlPatterns = "/*")
// /* 所有的请求都拦截
// 过滤器要有这个注解
//添加过滤器和拦截器 防止直接输入网站 从而跳过 登陆界面
public class LoginCheckfilter implements Filter {
/*
有些路径后面带html,有些不带html
用路径匹配器,支持通配符
*/
static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
log.info("拦截到请求{}", request.getRequestURI());
/*
filterchain.dofilter() 方法用于执行过滤器链中的所有过滤器。它返回过滤器链处理后的结果。
*/
String requestURI = request.getRequestURI();
//不需要处理的URI路径
//backen和front 包里有些带.html后缀 有些不带 用路径通配符
String[] URIS = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
boolean check = check(requestURI, URIS);
//如果不需要处理就直接放行
if (check) {
log.info("本次请求{}不需要处理",requestURI);
filterChain.doFilter(request, response);
return;
}
//如果session 有对象 说明就已经登陆了
if (request.getSession().getAttribute("employee") != null) {
//取得属性的名称之后,我们就可以用getAttribute()方法将它的属性值拿出来了。
System.out.println("----session--- 不为空");
log.info("用户登录,用户id为{}",request.getSession().getAttribute("employee"));
filterChain.doFilter(request, response);
return;
}
// 如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
//通过JSON.toJSONSting方法 将R对象转换成josn数据 然后调用write写回去
//"NOTLOGIN" 对应 request.js 里的”NOTLOGIN“
log.info("用户未登录");
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
/**
* 路径匹配看一下是否要放行
*
* @param requestURI 传进来的uri
* @return
*/
public static boolean check(String requestURI, String[] UIRS) {
for (String url : UIRS) {
//通配符
boolean match = PATH_MATCHER.match(url, requestURI);
if (match) {
return true;
}
}
return false;
}
}
另外我想起了一个面试题 拦截器和过滤器有什么不同?
有兴趣大家可以研究一下
/**
* 员工退出
*
* @param request
* @return
*/
@PostMapping("/logout")
//@RequestMapping(method = RequestMethod.POST)的快捷方式
public R<String> logout(HttpServletRequest request) {
//清理session 传进去的叫employee 移除的也叫employee
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
注意这里密码用MD5加密
/**
* 新增员工
*
* @param employee
* @return
*/
@PostMapping
public R<String> save(@RequestBody Employee employee, HttpServletRequest request) {
log.info("新增员工,输出员工信息:{}", employee.toString());
//设定初始密码时123456 并且加密
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
//获得当前用户id
Long empId = (long) request.getSession().getAttribute("employee");
employee.setCreateUser(empId);
employee.setUpdateUser(empId);
//mybatis plus 里的方法
employeeService.save(employee);
return R.success("新增员工成功");
}
注意我们这里先不先不设置MetaObjectHandler类 我们在 公共字段填充代码里写,可以看目录。
aop编程
/**
* 全局异常处理
*/
/**
* @ControllerAdvice
* annotation: 注解
* 只要类上加了 RestController, Controller 这两个注解 就会被拦截到
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class})
/**
* 因为要返回json数据所以用ResponsBody
*/
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
//@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常,
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
log.error(ex.getMessage());
if (ex.getMessage().contains("Duplicate entry")) {
//用空格分开
String[] split = ex.getMessage().split(" ");
//split【2】得到重复的用户名 加已存在
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("未知错误");
}
}
/**
* 员工分页查询
* @param page 页码
* @param pageSize 一页的大小 10
* @param name 搜索时输入的值
*/
@GetMapping("/page")//因为传过来的是get
//在request.js 里 将json数据解析 然后拼接成url 并转换成key value形式 所以这里的参数不用加 @RequestBody
//这里 参数要和 前端url 里的key 一样 前段是?page= 后端参数名字就要叫page
public R<Page> page(int page, int pageSize, String name) {
log.info("page = {},pageSize = {},name = {}", page, pageSize, name);
//构造分页构造器
//Page也是Spring Data库中的一个接口,主要用于存储JPA查询数据库的结果集。
Page pageInfo = new Page(page, pageSize);
//构造条件构造器
//mybatis puls 提供的 和数据库进行比较
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件
//StringUtils已经提供了一个判断如果name为空的情况 就不需要自己判断了
//如果空就不执行下面这句话 ,如果不为空再执行这句话
queryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);
//添加排序条件 根据更新时间排序
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo, queryWrapper);
return R.success(pageInfo);
}
注意这里是因为mybatis-puls 不考虑null 的值 前端只传过来 status还有id
其他的值都是空所以他只更新不为空的值 如果想让他更新null的值要改配置
在MyBatis-Plus配置文件中修改field-strategy字段验证的值为0即可
/**
* 根据id修改员工信息
* @param employee
* @return
*/
@PutMapping
public R<String> update(@RequestBody Employee employee,HttpServletRequest request){
log.info(employee.toString());
employee.setUpdateUser((long)request.getSession().getAttribute("employee"));
employee.setUpdateTime(LocalDateTime.now());
//代码简单是因为我们用的mybatis puls updateById 是继承Iservice这个接口
employeeService.updateById(employee);
return R.success("员工信息修改成功");
}
注意这里update会失败因为json解析long型的数据时会改变精度 所以要进行修改
注意 update次数是0
/**
* 对象映射器:基于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);
}
注意这里要控制下标把咱们的转换器放在0位置也就是先用咱们这个转换器而不是先用默认的转换器
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
MappingJackson2HttpMessageConverter是springboot中默认的Json消息转换器。
/**
* 根据id查询数字
*/
@GetMapping("/{id}")
public R<Employee>getById(@PathVariable long id){
Employee employee =employeeService.getById(id);
return null;
}
注意这里和之前员工分页不同!
这个是员工分页
这个是key-value 让括号里声明的参数和key值一样就可以
这个是根据id查询员工
这个是直接获取url上的值所以要用@PathVariable 注解
另外 编辑员工用的也是上一个update 方法所以根据id查完之后就可以直接编辑
我们现在可以把操作员工那里的重复加入的代码(比如设置更新人是谁等等)删去了
我们这里先不得到 updateUser 因为我们在MyMetaObjectHandler类里是获取不到session的
注意:每个线程都只能看到自己线程的值 ,每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题
代码如下:
我们在设置婉工具类后就可以在BaseContxt 类里调用get方法获取session 里的id了
因为filter 先运行filter 里的 BaseContext.setCurrentId(empId); 然后运行MyMetaObjectHandler进行赋值
先获取带字段里带Duplicate entry 说明这个是异常 然后根据空格分开
这里我们后端只需要写一个方法就可以新增套餐分类,新增菜品分类,两个窗口的逻辑,因为请求的url路径和穿的json 参数都一样的
@GetMapping("/page")
public R<Page> page(int page, int pageSize) {
log.info("page = {},pageSize = {}", page, pageSize);
//构造分页构造器
//Page也是Spring Data库中的一个接口,主要用于存储JPA查询数据库的结果集。
Page pageInfo = new Page(page, pageSize);
//构造条件构造器
//mybatis puls 提供的 和数据库进行比较
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper();
//添加排序条件 根据更新时间排序
queryWrapper.orderByAsc(Category::getSort);
//执行查询
categoryService.page(pageInfo, queryWrapper);
return R.success(pageInfo);
}
接收ids
注意这里老师传的参数是ids 我开始写的id 报了id为空的异常。。。 大家要细心一点
/**
* 根据id删除分类,删除之前要判断
*
* @param id
*/
@Override
public void remove(Long id) {
//查询是否关联了菜品,如果已经关联 ,抛出一个异常
//LambdaQueryWrapper.eq 方法是 MyBatis Plus 中用于构建等于(equal)条件的方法。
// 它接收两个参数,第一个参数是数据库表中的列名,第二个参数是要匹配的值。
// 该方法返回一个 LambdaQueryWrapper 对象,可以配合其它条件继续构建复杂的查询语句。
LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<Dish>();
dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);
int count1 = dishService.count(dishLambdaQueryWrapper);
//查询是否关联了菜品,如果已经关联 ,抛出一个异常
if (count1 > 0) {
throw new CustomException("当下分类关联了菜品,不能删啊0.0");
}
LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);
int count2 = dishService.count(dishLambdaQueryWrapper);
//查询是否关联了套餐,如果已经关联 ,抛出一个异常
if (count2 > 0) {
throw new CustomException("当下分类关联了套餐,不能删啊0.0");
}
//正常删除分类
super.removeById(id);
}
设置全局异常处理
前端来看修改和新增用的是一个窗口
@RestController
@Slf4j
@RequestMapping("/common")
public class CommonController {
/**
* 那么系统中如何使用这些配置信息呢,spring中提供了@Value注解来解决这个问题。
* 通常我们会将配置信息以key=value的形式存储在properties配置文件中。
*/
@Value("${takeout.path}")
private String basePath;
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
//file 是一个临时文件,要转存到一个位置不然本次请求完成后就会消失
log.info(file.toString());
String originalFilename = file.getOriginalFilename();
String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = UUID.randomUUID().toString()+substring ;
File dir = new File(basePath);
//如果目录不存在 那么创建
if(!dir.exists()){
dir.mkdirs();
}
try {
//把临时文件转存到指定位置
file.transferTo(new File(basePath+fileName));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(fileName);
}
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
log.info(name);
try {
//输入流,通过输入流读取文件内容
FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
//输出流,通过输出流将文件写回浏览器,在浏览器展示图片了
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("image/jpeg");
int len=0;
byte[]bytes=new byte[1024];
while ((len=fileInputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
outputStream.flush();
}
outputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
配置spring.yml 配置文件
用到了输入输出流 javase学的忘记了的学生可以搜一下学一下
@GetMapping("/list")
//点击新建菜品 传参为type=1 可以用String type 来接收 但是我们这里采用封装成category的方法
public R<List<Category>> list(Category category){
log.info("进入新建菜品页面");
//条件构造器
LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
//添加条件
categoryLambdaQueryWrapper.eq(category.getType()!=null,Category::getType,category.getType());
//添加排序条件 优先用sort来排序 如果sort相同那么采用更新时间进行排序
categoryLambdaQueryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);
List<Category> list = categoryService.list(categoryLambdaQueryWrapper);
return R.success(list);
}
这个表里有两个id 第一个id是表本身自带的 一个是另一个表的菜品id这这里叫dish_id
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
@Autowired
private DishFlavorservice dishFlavorservice;
@Transactional//保证数据一致性
@Override
public void saveWithFalver(DishDto dishDto) {
//保存菜品的基本信息到菜品dish
this.save(dishDto);
Long id = dishDto.getId();
//菜品口味
List<DishFlavor> flavors = dishDto.getFlavors();
//lambda表达式 Java8 新特性
//这里的id 是根据appliciation.yml 里的 id-type: ASSIGN_ID 注入的
flavors=flavors.stream().map((dishFlavor) -> {
dishFlavor.setDishId(id);
return dishFlavor;
}).collect(Collectors.toList());//变为一个链表并且把dish_id 赋值
// //保存菜品口味数据到菜品口味表dish——flavor
dishFlavorservice.saveBatch(flavors);
}
}
lambda 表达式这里大家可以搜索学习一下是新特性当然也可以用foreach
/*
菜品信息分页查询
*/
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
//构造分页构造器对象
Page<Dish> dishPage = new Page<>(page, pageSize);
Page<DishDto> dishDtoPage = new Page<>();
//条件构造器
LambdaQueryWrapper<Dish> QueryWrapper = new LambdaQueryWrapper<>();
//添加过滤条件 用名字
LambdaQueryWrapper<Dish> like = QueryWrapper.like(name != null, Dish::getName, name);
//用更新时间进行排序
QueryWrapper.orderByDesc(Dish::getUpdateTime);
//执行分页 查询
dishService.page(dishPage, QueryWrapper);
//对象拷贝
BeanUtils.copyProperties(dishPage, dishDtoPage, "records");
List<Dish> records = dishPage.getRecords();
List<DishDto> list = records.stream().map((item) -> {
DishDto dishDto = new DishDto();
//遍历出来的属性拷贝到dishDto
BeanUtils.copyProperties(item, dishDto);
Long categoryId = item.getCategoryId();//分类
//根据id查询分类对象
Category category = categoryService.getById(categoryId);
if(category!=null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
return dishDto;
}).collect(Collectors.toList());
dishDtoPage.setRecords(list);
return R.success(dishDtoPage);
}
/**
* 修改菜品
* @param dishDto
*/
@Override
@Transactional
public void updateWithFalvor(DishDto dishDto) {
//更新dish表信息
this.updateById(dishDto);
//清理当前菜品对应口味数据--dish_falvor表的delete操作
LambdaQueryWrapper<DishFlavor> QueryWrapper = new LambdaQueryWrapper();
QueryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
dishFlavorservice.remove(QueryWrapper);
//添加当前提交过来的口味数据,dish_flavor表的insert操作
List<DishFlavor> flavors = dishDto.getFlavors();
flavors=flavors.stream().map((item)->{
log.info("item.tostring()={}",item.toString());
log.info("dishDto.getId().toString()={}",dishDto.getId().toString());
item.setDishId(dishDto.getId());
return item;
}).collect(Collectors.toList());
}
发现他传了一个菜品id key-value形式 我们这里可以用Long categoryid来接收
/**
* 根据条件来查询对应的菜品数据
* @param dish
* @return
*/
@GetMapping("/list")
public R<List<Dish>> list(Dish dish){
//构造查询条件
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());
//添加条件,查询状态为1(起售状态)的菜品
queryWrapper.eq(Dish::getStatus,1);
//添加排序条件
queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper);
return R.success(list);
}
/**
* 新增套餐 同时保存套餐与菜品的关联关系
*
*/
@Override
@Transactional
public void saveWithDish(SetmealDto setmealDto) {
//保存套餐的基本信息,操作setmeal,执行insert操作
this.save(setmealDto);
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
setmealDishes.stream().map((item) -> {
item.setSetmealId(setmealDto.getId());
return item;
}).collect(Collectors.toList());
//保存套餐和菜品的关联信息,操作setmeal_dish,执行insert操作
setmealDishService.saveBatch(setmealDishes);
}
/**
* 套餐分页查询
* @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<>();
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);
//分类id
Long 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);
}
删除一个和删除多个是一样的 无非是id传过来的个数不一样
/**
* 删除套餐,同时需要删除套餐和菜品的关联数据
* @param ids
*/
@Transactional
@Override
public void removeWithDish(List<Long> ids) {
//select count(*) from setmeal where id in (1,2,3) and status = 1
//查询套餐状态,确定是否可用删除
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper();
queryWrapper.in(Setmeal::getId,ids);
System.out.println("-------------------------------------------------------------");
queryWrapper.eq(Setmeal::getStatus,1);
int count = this.count(queryWrapper);
if(count > 0){
//如果不能删除,抛出一个业务异常
throw new CustomException("套餐正在售卖中,不能删除");
}
//如果可以删除,先删除套餐表中的数据---setmeal
this.removeByIds(ids);
//delete from setmeal_dish where setmeal_id in (1,2,3)
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
//删除关系表中的数据----setmeal_dish
setmealDishService.remove(lambdaQueryWrapper);
}
/**
* 移动端用户登录
* @param map
* @param session
* @return
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session){
log.info(map.toString());
log.info("进入方法--------------------");
//获取手机号
String phone = map.get("phone").toString();
//获取验证码
String code = map.get("code").toString();
//从Session中获取保存的验证码
Object codeInSession = session.getAttribute(phone);
//进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
if(codeInSession != null && codeInSession.equals(code)){
//如果能够比对成功,说明登录成功
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(queryWrapper);
if(user == null){
//判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
user = new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("user",user.getId());
return R.success(user);
}
return R.error("登录失败");
}
不要忘记加过滤器
我在做这里的时候遇到了找不到sendMsgApi的问题 我把target文件删掉然后重新启动项目就好了
front文件用的day6
Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {
@Autowired
private AddressBookService addressBookService;
/**
* 新增
*/
@PostMapping
public R<AddressBook> save(@RequestBody AddressBook addressBook) {
addressBook.setUserId(BaseContext.getCurrentId());
log.info("addressBook:{}", addressBook);
addressBookService.save(addressBook);
return R.success(addressBook);
}
/**
* 设置默认地址
*/
@PutMapping("default")
public R<AddressBook> setDefault(@RequestBody AddressBook addressBook) {
log.info("addressBook:{}", addressBook);
LambdaUpdateWrapper<AddressBook> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
wrapper.set(AddressBook::getIsDefault, 0);
//SQL:update address_book set is_default = 0 where user_id = ?
addressBookService.update(wrapper);
addressBook.setIsDefault(1);
//SQL:update address_book set is_default = 1 where id = ?
addressBookService.updateById(addressBook);
return R.success(addressBook);
}
/**
* 根据id查询地址
*/
@GetMapping("/{id}")
public R get(@PathVariable Long id) {
AddressBook addressBook = addressBookService.getById(id);
if (addressBook != null) {
return R.success(addressBook);
} else {
return R.error("没有找到该对象");
}
}
/**
* 查询默认地址
*/
@GetMapping("default")
public R<AddressBook> getDefault() {
LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
queryWrapper.eq(AddressBook::getIsDefault, 1);
//SQL:select * from address_book where user_id = ? and is_default = 1
AddressBook addressBook = addressBookService.getOne(queryWrapper);
if (null == addressBook) {
return R.error("没有找到该对象");
} else {
return R.success(addressBook);
}
}
/**
* 查询指定用户的全部地址
*/
@GetMapping("/list")
public R<List<AddressBook>> list(AddressBook addressBook) {
addressBook.setUserId(BaseContext.getCurrentId());
log.info("addressBook:{}", addressBook);
//条件构造器
LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(null != addressBook.getUserId(), AddressBook::getUserId, addressBook.getUserId());
queryWrapper.orderByDesc(AddressBook::getUpdateTime);
//SQL:select * from address_book where user_id = ? order by update_time desc
return R.success(addressBookService.list(queryWrapper));
}
}
/**
* 根据条件来查询对应的菜品数据
* @param dish
* @return
*/
@GetMapping("/list")
public R<List<DishDto>> list(Dish dish){
//构造查询条件
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());
//添加条件,查询状态为1(起售状态)的菜品
queryWrapper.eq(Dish::getStatus,1);
//添加排序条件
queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper);
List<DishDto> dishDtoList = list.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item,dishDto);
Long categoryId = item.getCategoryId();//分类id
//根据id查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
//当前菜品的id
Long dishId = item.getId();
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
//SQL:select * from dish_flavor where dish_id = ?
List<DishFlavor> dishFlavorList = dishFlavorservice.list(lambdaQueryWrapper);
dishDto.setFlavors(dishFlavorList);
return dishDto;
}).collect(Collectors.toList());
return R.success(dishDtoList);
}
/**
* 添加购物车
* @param shoppingCart
* @return
*/
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
log.info("购物车数据:{}",shoppingCart);
//设置用户id,指定当前是哪个用户的购物车数据
Long currentId = BaseContext.getCurrentId();
shoppingCart.setUserId(currentId);
Long dishId = shoppingCart.getDishId();
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,currentId);
if(dishId != null){
//添加到购物车的是菜品
queryWrapper.eq(ShoppingCart::getDishId,dishId);
}else{
//添加到购物车的是套餐
queryWrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
//查询当前菜品或者套餐是否在购物车中
//SQL:select * from shopping_cart where user_id = ? and dish_id/setmeal_id = ?
ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper);
if(cartServiceOne != null){
//如果已经存在,就在原来数量基础上加一
Integer number = cartServiceOne.getNumber();
cartServiceOne.setNumber(number + 1);
shoppingCartService.updateById(cartServiceOne);
}else{
//如果不存在,则添加到购物车,数量默认就是一
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartService.save(shoppingCart);
cartServiceOne = shoppingCart;
}
return R.success(cartServiceOne);
}
@GetMapping("/list")
public R<List<ShoppingCart>> list(){
log.info("查看购物车...");
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
queryWrapper.orderByAsc(ShoppingCart::getCreateTime);
List<ShoppingCart> list = shoppingCartService.list(queryWrapper);
return R.success(list);
}
/**
* 清空购物车
* @return
*/
@DeleteMapping("/clean")
public R<String> clean(){
//SQL:delete from shopping_cart where user_id = ?
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
shoppingCartService.remove(queryWrapper);
return R.success("清空购物车成功");
}
@Autowired
private ShoppingCartService shoppingCartService;
@Autowired
private UserService userService;
@Autowired
private AddressBookService addressBookService;
@Autowired
private OrderDetailService orderDetailService;
/**
* 用户下单
* @param orders
*/
@Transactional
public void submit(Orders orders) {
//获得当前用户id
Long userId = BaseContext.getCurrentId();
//查询当前用户的购物车数据
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
List<ShoppingCart> shoppingCarts = shoppingCartService.list(wrapper);
if(shoppingCarts == null || shoppingCarts.size() == 0){
throw new CustomException("购物车为空,不能下单");
}
//查询用户数据
User user = userService.getById(userId);
//查询地址数据
Long addressBookId = orders.getAddressBookId();
AddressBook addressBook = addressBookService.getById(addressBookId);
if(addressBook == null){
throw new CustomException("用户地址信息有误,不能下单");
}
long orderId = IdWorker.getId();//订单号
AtomicInteger amount = new AtomicInteger(0);
List<OrderDetail> orderDetails = shoppingCarts.stream().map((item) -> {
OrderDetail orderDetail = new OrderDetail();
orderDetail.setOrderId(orderId);
orderDetail.setNumber(item.getNumber());
orderDetail.setDishFlavor(item.getDishFlavor());
orderDetail.setDishId(item.getDishId());
orderDetail.setSetmealId(item.getSetmealId());
orderDetail.setName(item.getName());
orderDetail.setImage(item.getImage());
orderDetail.setAmount(item.getAmount());
amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
return orderDetail;
}).collect(Collectors.toList());
orders.setId(orderId);
orders.setOrderTime(LocalDateTime.now());
orders.setCheckoutTime(LocalDateTime.now());
orders.setStatus(2);
orders.setAmount(new BigDecimal(amount.get()));//总金额
orders.setUserId(userId);
orders.setNumber(String.valueOf(orderId));
orders.setUserName(user.getName());
orders.setConsignee(addressBook.getConsignee());
orders.setPhone(addressBook.getPhone());
orders.setAddress((addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
+ (addressBook.getCityName() == null ? "" : addressBook.getCityName())
+ (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
+ (addressBook.getDetail() == null ? "" : addressBook.getDetail()));
//向订单表插入数据,一条数据
this.save(orders);
//向订单明细表插入数据,多条数据
orderDetailService.saveBatch(orderDetails);
//清空购物车数据
shoppingCartService.remove(wrapper);
}
基础篇完结散花 欢迎指正
我会在另一篇更新后面的内容
去玩星穹铁道了!
比心
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。