瑞吉外卖项目
Java项目实战《瑞吉外卖》,轻松掌握springboot + mybatis plus开发核心技术的真java实战项目
包含自己学习时候的理解 有可能不对欢迎指正,我会查看并且进行正确的修改,也有老师讲的时候的讲解
创建项目,数据库 我就不多讲解了 (项目这里导入了backend 和front包 都是前端代码)
注意:这里导入了backend 和 front 包的时候,springboot并不知道这些是静态资源,所以要编写一个配置类,配置mvc静态资源的映射 如果不编写输入时url时不出现页面
以下是springboot默认的静态资源路径
sources.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 {@Autowiredprivate 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 = Password();//md5解密pwd = DigestUtils.Bytes());//mybatis-plus 的方法LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();//和数据库进行判断queryWrapper.eq(Employee::getUsername, Username());Employee emp = One(queryWrapper);System.out.println(emp);if (emp == null) {("登陆失败");}if (!Password().equals(pwd)) {("密码不对");}if (Status() == 0) {("账号被锁");}System.out.println("登录成功");//这里设定employee的id的key为employeeSystem.out.println("----这个是empoyee---" + employee);System.out.println("----这个是emp---" + emp);Session().setAttribute("employee", Id());return R.success(emp);}
==注意:这里我直接加上了session 原视频开始是没有的,是后来完善的,另外 Session().setAttribute(“employee”, Id()); 这句话传递的是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();@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) servletResponse;HttpServletRequest request = (HttpServletRequest) servletRequest;log.info("拦截到请求{}", RequestURI());/*filterchain.dofilter() 方法用于执行过滤器链中的所有过滤器。它返回过滤器链处理后的结果。*/String requestURI = RequestURI();//不需要处理的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 (Session().getAttribute("employee") != null) {//取得属性的名称之后,我们就可以用getAttribute()方法将它的属性值拿出来了。System.out.println("----session--- 不为空");log.info("用户登录,用户id为{}",Session().getAttribute("employee"));filterChain.doFilter(request, response);return;}// 如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据//通过JSONSting方法 将R对象转换成josn数据 然后调用write写回去//"NOTLOGIN" 对应 request.js 里的”NOTLOGIN“log.info("用户未登录");Writer().("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 移除的也叫Session().removeAttribute("employee");return R.success("退出成功");}
注意这里密码用MD5加密
/*** 新增员工** @param employee* @return*/@PostMappingpublic R<String> save(@RequestBody Employee employee, HttpServletRequest request) {log.info("新增员工,输出员工信息:{}", String());//设定初始密码时123456 并且加密employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));employee.w());employee.w());//获得当前用户idLong empId = (long) Session().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) {(ex.getMessage());if (ex.getMessage().contains("Duplicate entry")) {//用空格分开String[] split = ex.getMessage().split(" ");//split【2】得到重复的用户名 加已存在String msg = split[2] + "已存在";(msg);}("未知错误");}
}
/*** 员工分页查询* @param page 页码* @param pageSize 一页的大小 10* @param name 搜索时输入的值*/@GetMapping("/page")//因为传过来的是get//在request.js 里 将json数据解析 然后拼接成url 并转换成key value形式 所以这里的参数不用加 @RequestBody//这里 参数要和 前端url 里的key 一样 前段是?page= 后端参数名字就要叫pagepublic 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);//添加排序条件 根据更新时间排序derByDesc(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*/@PutMappingpublic R<String> update(@RequestBody Employee employee,HttpServletRequest request){log.String());employee.setUpdateUser((Session().getAttribute("employee"));employee.w());//代码简单是因为我们用的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();//收到未知属性时不报异常figure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时,属性不存在的兼容处理DeserializationConfig().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)));//注册功能模块 例如,可以添加自定义序列化器和反序列化器isterModule(simpleModule);}
注意这里要控制下标把咱们的转换器放在0位置也就是先用咱们这个转换器而不是先用默认的转换器
/*** 扩展mvc框架的消息转换器* @param converters*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展消息转换器...");//创建消息转换器对象MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();//设置对象转换器,底层使用Jackson将Java对象转为jsonmessageConverter.setObjectMapper(new JacksonObjectMapper());//将上面的消息转换器对象追加到mvc框架的转换器集合中converters.add(0,messageConverter);}
MappingJackson2HttpMessageConverter是springboot中默认的Json消息转换器。
/*** 根据id查询数字*/@GetMapping("/{id}")public R<Employee>getById(@PathVariable long id){Employee employee ById(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();//添加排序条件 根据更新时间排序derByAsc(Category::getSort);//执行查询categoryService.page(pageInfo, queryWrapper);return R.success(pageInfo);}
接收ids
注意这里老师传的参数是ids 我开始写的id 报了id为空的异常。。。 大家要细心一点
/*** 根据id删除分类,删除之前要判断** @param id*/@Overridepublic void remove(Long id) {//查询是否关联了菜品,如果已经关联 ,抛出一个异常//LambdaQueryWrapper.eq 方法是 MyBatis Plus 中用于构建等于(equal)条件的方法。// 它接收两个参数,第一个参数是数据库表中的列名,第二个参数是要匹配的值。// 该方法返回一个 LambdaQueryWrapper 对象,可以配合其它条件继续构建复杂的查询语句。LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<Dish>();dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);int count1 = unt(dishLambdaQueryWrapper);//查询是否关联了菜品,如果已经关联 ,抛出一个异常if (count1 > 0) {throw new CustomException("当下分类关联了菜品,不能删啊0.0");}LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);int count2 = unt(dishLambdaQueryWrapper);//查询是否关联了套餐,如果已经关联 ,抛出一个异常if (count2 > 0) {throw new CustomException("当下分类关联了套餐,不能删啊0.0");}//正常删除分类veById(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.String());String originalFilename = OriginalFilename();String substring = originalFilename.substring(originalFilename.lastIndexOf("."));String fileName = UUID.randomUUID().toString()+substring ;File dir = new File(basePath);//如果目录不存在 那么创建if(!ists()){dir.mkdirs();}try {//把临时文件转存到指定位置ansferTo(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 = OutputStream();response.setContentType("image/jpeg");int len=0;byte[]bytes=new byte[1024];while ((lenad(bytes))!=-1){outputStream.write(bytes,0,len);outputStream.flush();}outputStream.close();fileInputStream.close();} catch (Exception e) {e.printStackTrace();}}
}
配置l 配置文件
用到了输入输出流 javase学的忘记了的学生可以搜一下学一下
@GetMapping("/list")//点击新建菜品 传参为type=1 可以用String type 来接收 但是我们这里采用封装成category的方法public R<List<Category>> list(Category category){log.info("进入新建菜品页面");//条件构造器LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加条件 categoryLambdaQueryWrapper.Type()!=null,Category::Type());//添加排序条件 优先用sort来排序 如果sort相同那么采用更新时间进行排序derByAsc(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 {@Autowiredprivate DishFlavorservice dishFlavorservice;@Transactional//保证数据一致性@Overridepublic void saveWithFalver(DishDto dishDto) {//保存菜品的基本信息到菜品dishthis.save(dishDto);Long id = Id();//菜品口味List<DishFlavor> flavors = Flavors();//lambda表达式 Java8 新特性//这里的id 是根据l 里的 id-type: ASSIGN_ID 注入的flavors=flavors.stream().map((dishFlavor) -> {dishFlavor.setDishId(id);return dishFlavor;}).List());//变为一个链表并且把dish_id 赋值// //保存菜品口味数据到菜品口味表dish——flavordishFlavorservice.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);//用更新时间进行排序derByDesc(Dish::getUpdateTime);//执行分页 查询dishService.page(dishPage, QueryWrapper);//对象拷贝pyProperties(dishPage, dishDtoPage, "records");List<Dish> records = Records();List<DishDto> list = records.stream().map((item) -> {DishDto dishDto = new DishDto();//遍历出来的属性拷贝到pyProperties(item, dishDto);Long categoryId = CategoryId();//分类//根据id查询分类对象Category category = ById(categoryId);if(category!=null){String categoryName = Name();dishDto.setCategoryName(categoryName);}return dishDto;}).List());dishDtoPage.setRecords(list);return R.success(dishDtoPage);}
/*** 修改菜品* @param dishDto*/@Override@Transactionalpublic void updateWithFalvor(DishDto dishDto) {//更新dish表信息this.updateById(dishDto);//清理当前菜品对应口味数据--dish_falvor表的delete操作LambdaQueryWrapper<DishFlavor> QueryWrapper = new LambdaQueryWrapper();QueryWrapper.eq(DishFlavor::Id());ve(QueryWrapper);//添加当前提交过来的口味数据,dish_flavor表的insert操作List<DishFlavor> flavors = Flavors();flavors=flavors.stream().map((item)->{log.info(string()={}",String());log.info(Id().toString()={}",Id().toString());item.Id());return item;}).List());}
发现他传了一个菜品id key-value形式 我们这里可以用Long categoryid来接收
/*** 根据条件来查询对应的菜品数据* @param dish* @return*/@GetMapping("/list")public R<List<Dish>> list(Dish dish){//构造查询条件LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.CategoryId() != null ,Dish::CategoryId());//添加条件,查询状态为1(起售状态)的菜品queryWrapper.eq(Dish::getStatus,1);//添加排序条件derByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);List<Dish> list = dishService.list(queryWrapper);return R.success(list);}
/*** 新增套餐 同时保存套餐与菜品的关联关系**/@Override@Transactionalpublic void saveWithDish(SetmealDto setmealDto) {//保存套餐的基本信息,操作setmeal,执行insert操作this.save(setmealDto);List<SetmealDish> setmealDishes = SetmealDishes();setmealDishes.stream().map((item) -> {item.Id());return item;}).List());//保存套餐和菜品的关联信息,操作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);//添加排序条件,根据更新时间降序排列derByDesc(Setmeal::getUpdateTime);setmealService.page(pageInfo,queryWrapper);//对象拷贝pyProperties(pageInfo,dtoPage,"records");List<Setmeal> records = Records();List<SetmealDto> list = records.stream().map((item) -> {SetmealDto setmealDto = new SetmealDto();//对象拷贝pyProperties(item,setmealDto);//分类idLong categoryId = CategoryId();//根据分类id查询分类对象Category category = ById(categoryId);if(category != null){//分类名称String categoryName = Name();setmealDto.setCategoryName(categoryName);}return setmealDto;}).List());dtoPage.setRecords(list);return R.success(dtoPage);}
删除一个和删除多个是一样的 无非是id传过来的个数不一样
/*** 删除套餐,同时需要删除套餐和菜品的关联数据* @param ids*/@Transactional@Overridepublic 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 = unt(queryWrapper);if(count > 0){//如果不能删除,抛出一个业务异常throw new CustomException("套餐正在售卖中,不能删除");}//如果可以删除,先删除套餐表中的数据---veByIds(ids);//delete from setmeal_dish where setmeal_id in (1,2,3)LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);//删除关系表中的数据----ve(lambdaQueryWrapper);}
/*** 移动端用户登录* @param map* @param session* @return*/@PostMapping("/login")public R<User> login(@RequestBody Map map, HttpSession session){log.String());log.info("进入方法--------------------");//获取手机号String phone = ("phone").toString();//获取验证码String code = ("code").toString();//从Session中获取保存的验证码Object codeInSession = Attribute(phone);//进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)if(codeInSession != null && codeInSession.equals(code)){//如果能够比对成功,说明登录成功LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getPhone,phone);User user = One(queryWrapper);if(user == null){//判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册user = new User();user.setPhone(phone);user.setStatus(1);userService.save(user);}session.setAttribute("user",Id());return R.success(user);}("登录失败");}
不要忘记加过滤器
我在做这里的时候遇到了找不到sendMsgApi的问题 我把target文件删掉然后重新启动项目就好了
front文件用的day6
Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {@Autowiredprivate AddressBookService addressBookService;/*** 新增*/@PostMappingpublic R<AddressBook> save(@RequestBody AddressBook addressBook) {addressBook.CurrentId());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, CurrentId());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 = ById(id);if (addressBook != null) {return R.success(addressBook);} else {("没有找到该对象");}}/*** 查询默认地址*/@GetMapping("default")public R<AddressBook> getDefault() {LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(AddressBook::getUserId, CurrentId());queryWrapper.eq(AddressBook::getIsDefault, 1);//SQL:select * from address_book where user_id = ? and is_default = 1AddressBook addressBook = One(queryWrapper);if (null == addressBook) {("没有找到该对象");} else {return R.success(addressBook);}}/*** 查询指定用户的全部地址*/@GetMapping("/list")public R<List<AddressBook>> list(AddressBook addressBook) {addressBook.CurrentId());log.info("addressBook:{}", addressBook);//条件构造器LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(null != UserId(), AddressBook::getUserId, UserId());derByDesc(AddressBook::getUpdateTime);//SQL:select * from address_book where user_id = ? order by update_time descreturn R.success(addressBookService.list(queryWrapper));}
}
/*** 根据条件来查询对应的菜品数据* @param dish* @return*/@GetMapping("/list")public R<List<DishDto>> list(Dish dish){//构造查询条件LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.CategoryId() != null ,Dish::CategoryId());//添加条件,查询状态为1(起售状态)的菜品queryWrapper.eq(Dish::getStatus,1);//添加排序条件derByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);List<Dish> list = dishService.list(queryWrapper);List<DishDto> dishDtoList = list.stream().map((item) -> {DishDto dishDto = new DishDto();pyProperties(item,dishDto);Long categoryId = CategoryId();//分类id//根据id查询分类对象Category category = ById(categoryId);if(category != null){String categoryName = Name();dishDto.setCategoryName(categoryName);}//当前菜品的idLong dishId = Id();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;}).List());return R.success(dishDtoList);}
/*** 添加购物车* @param shoppingCart* @return*/@PostMapping("/add")public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){log.info("购物车数据:{}",shoppingCart);//设置用户id,指定当前是哪个用户的购物车数据Long currentId = CurrentId();shoppingCart.setUserId(currentId);Long dishId = DishId();LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ShoppingCart::getUserId,currentId);if(dishId != null){//添加到购物车的是菜品queryWrapper.eq(ShoppingCart::getDishId,dishId);}else{//添加到购物车的是套餐queryWrapper.eq(ShoppingCart::SetmealId());}//查询当前菜品或者套餐是否在购物车中//SQL:select * from shopping_cart where user_id = ? and dish_id/setmeal_id = ?ShoppingCart cartServiceOne = One(queryWrapper);if(cartServiceOne != null){//如果已经存在,就在原来数量基础上加一Integer number = Number();cartServiceOne.setNumber(number + 1);shoppingCartService.updateById(cartServiceOne);}else{//如果不存在,则添加到购物车,数量默认就是一shoppingCart.setNumber(1);shoppingCart.w());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::CurrentId());derByAsc(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::CurrentId());ve(queryWrapper);return R.success("清空购物车成功");}
@Autowiredprivate ShoppingCartService shoppingCartService;@Autowiredprivate UserService userService;@Autowiredprivate AddressBookService addressBookService;@Autowiredprivate OrderDetailService orderDetailService;/*** 用户下单* @param orders*/@Transactionalpublic void submit(Orders orders) {//获得当前用户idLong userId = CurrentId();//查询当前用户的购物车数据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 = ById(userId);//查询地址数据Long addressBookId = AddressBookId();AddressBook addressBook = ById(addressBookId);if(addressBook == null){throw new CustomException("用户地址信息有误,不能下单");}long orderId = Id();//订单号AtomicInteger amount = new AtomicInteger(0);List<OrderDetail> orderDetails = shoppingCarts.stream().map((item) -> {OrderDetail orderDetail = new OrderDetail();orderDetail.setOrderId(orderId);orderDetail.Number());orderDetail.DishFlavor());orderDetail.DishId());orderDetail.SetmealId());orderDetail.Name());orderDetail.Image());orderDetail.Amount());amount.Amount().multiply(new Number())).intValue());return orderDetail;}).List());orders.setId(orderId);orders.w());orders.w());orders.setStatus(2);orders.setAmount(new ()));//总金额orders.setUserId(userId);orders.setNumber(String.valueOf(orderId));orders.Name());orders.Consignee());orders.Phone());orders.setAddress((ProvinceName() == null ? "" : ProvinceName())+ (CityName() == null ? "" : CityName())+ (DistrictName() == null ? "" : DistrictName())+ (Detail() == null ? "" : Detail()));//向订单表插入数据,一条数据this.save(orders);//向订单明细表插入数据,多条数据orderDetailService.saveBatch(orderDetails);//清空购物车数据ve(wrapper);}
基础篇完结散花 欢迎指正
我会在另一篇更新后面的内容
去玩星穹铁道了!
比心
本文发布于:2024-02-01 10:09:09,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170675334735889.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |