1.概述 软件开发整体介绍 软件开发流程
角色分工
软件环境
瑞吉外卖项目介绍 项目介绍
产品原型展示
技术选型
功能架构
角色
开发环境搭建 数据库环境搭建
maven项目搭建
配置pom文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.4.5</version > <relativePath /> </parent > <groupId > com.itheima</groupId > <artifactId > reggie_take_out</artifactId > <version > 1.0-SNAPSHOT</version > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <scope > compile</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.2</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.20</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.76</version > </dependency > <dependency > <groupId > commons-lang</groupId > <artifactId > commons-lang</artifactId > <version > 2.6</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <scope > runtime</scope > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.23</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > 2.4.5</version > </plugin > </plugins > </build > </project >
配置springboot配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server : port : 8080 spring : application : name : reggie_take_out datasource : druid : driver-class-name : com.mysql.cj.jdbc.Driver url : jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true username : root password : Xuwei19941214~ mybatis-plus : configuration : map-underscore-to-camel-case : true log-impl : org.apache.ibatis.logging.stdout.StdOutImpl global-config : db-config : id-type : ASSIGN_ID
编写springboot启动类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.itheima.reggie;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@Slf4j @SpringBootApplication public class ReggieApplication { public static void main (String[] args) { SpringApplication.run(ReggieApplication.class,args); log.info("项目启动成功..." ); } }
导入前端页面的静态资源
由于MVC无法识别非static下的静态资源,需通过配置修改
修改mvc配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.itheima.reggie.config;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Slf4j @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers (ResourceHandlerRegistry registry) { log.info("开始进行静态资源映射..." ); registry.addResourceHandler("/backend/**" ).addResourceLocations("classpath:/backend/" ); registry.addResourceHandler("/front/**" ).addResourceLocations("classpath:/front/" ); super .addResourceHandlers(registry); } }
成功访问。
后台开发登录功能 需求分析
代码开发 1)创建实体类Employee,和employee表进行映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.itheima.reggie.entity;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.TableField;import lombok.Data;import java.io.Serializable;import java.time.LocalDateTime;@Data public class Employee implements Serializable { private static final long serialVersionUID = 1L ; private Long id; private String username; private String name; private String password; private String phone; private String sex; private String idNumber; private Integer status; private LocalDateTime createTime; private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT) private Long createUser; @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser; }
2)创建contrller、service、mapper
3)导入返回结果类R
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.itheima.reggie.common;import lombok.Data;import java.util.HashMap;import java.util.Map;@Data public class R <T> { private Integer code; 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 ; } }
4)在Controller中创建登录方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @PostMapping("/login") public R<Employee> login (HttpServletRequest request, @RequestBody Employee employee) { String password=employee.getPassword(); DigestUtils.md5DigestAsHex(password.getBytes()); LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Employee::getUpdateTime,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.getId()); return R.success(emp); }
修改前端超时时间方便调试,并清除浏览器缓存
登录测试
登录成功
账号密码错误
账号禁用
后台退出功能 需求分析
代码开发 1 2 3 4 5 @PostMapping("/logout") public R<String> logout (HttpServletRequest request) { request.getSession().removeAttribute("employee" ); return R.success("退出成功" ); }
验证:
2.员工管理业务开发 效果展示
完善登录功能 问题分析
代码实现
创建过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package com.itheima.reggie.filter;import com.alibaba.fastjson.JSON;import com.itheima.reggie.common.R;import lombok.extern.slf4j.Slf4j;import org.springframework.util.AntPathMatcher;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.logging.LogRecord;@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*") @Slf4j public class LoginCheckFilter implements Filter { public static final AntPathMatcher PATH_MATCHER=new AntPathMatcher (); @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest)servletRequest; HttpServletResponse response=(HttpServletResponse)servletResponse; String requestURI = request.getRequestURI(); log.info("拦截到请求:{}" ,requestURI); String[] urls=new String []{ "/employee/login" , "/employee/logout" , "/backend/**" , "/front/**" }; boolean check= check(urls,requestURI); if (check){ log.info("本次请求不需要处理" ,requestURI); filterChain.doFilter(request,response); return ; } if (request.getSession().getAttribute("employee" )!=null ){ log.info("用户已登录,用户id为:{}" ,request.getSession().getAttribute("employee" )); filterChain.doFilter(request,response); return ; } log.info("用户未登录" ); response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN" ))); } private boolean check (String[] urls, String requestURI) { for (String url:urls){ if (PATH_MATCHER.match(url,requestURI)){ return true ; } } return false ; } }
启动类添加注解@ServletComponentScan
演示效果
未登录状态下直接请求[瑞吉外卖管理端](http://localhost:8080/backend/index.html)
新增员工 需求分析
数据模型
代码开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @PostMapping public R<String> save (HttpServletRequest request,Employee employee) { log.info("新增员工,员工信息:{}" ,employee.toString()); employee.setPassword(DigestUtils.md5DigestAsHex("123456" .getBytes())); employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); Long empId = (Long)request.getSession().getAttribute("employee" ); employee.setCreateUser(empId); employee.setUpdateUser(empId); employeeService.save(employee); return R.success("新增员工成功" ); }
定义异常类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.itheima.reggie.common;import lombok.extern.slf4j.Slf4j;import org.omg.CORBA.PUBLIC_MEMBER;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import java.sql.SQLIntegrityConstraintViolationException;@ControllerAdvice @ResponseBody @Slf4j public class GlobalExceptionHandler { @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("失败了" ); } }
效果
员工信息分页查询 需求分析
代码开发
配置MP的分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.itheima.reggie.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor (); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor ()); return mybatisPlusInterceptor; } }
编写分页接口
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/page") public R<Page> page (int page,int pageSize,String name) { Page<Employee> pageInfo = new Page <>(page,pageSize); LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.like(StringUtils.isNotBlank(name),Employee::getName,name); employeeService.page(pageInfo,queryWrapper); return R.success(pageInfo); }
效果
启用/禁用员工账号 需求分析
代码开发
编写更新接口
1 2 3 4 5 6 7 8 9 10 11 @PutMapping public R<String> update (HttpServletRequest request,@RequestBody Employee employee) { log.info(employee.toString()); Long empId = (Long) request.getSession().getAttribute("employee" ); employee.setUpdateUser(empId); employee.setUpdateTime(LocalDateTime.now()); employeeService.updateById(employee); return R.success("员工信息修改成功" ); }
拷贝消息转换器
配置扩展mvc框架的消息转换器
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected void extendMessageConverters (List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter (); messageConverter.setObjectMapper(new JacksonObjectMapper ()); converters.add(0 ,messageConverter); }
效果
编辑员工信息 需求分析
代码开发
编写查询详情接口
1 2 3 4 5 6 7 8 9 10 @GetMapping("/{id}") public R<Employee> getById (@PathVariable Long id) { log.info("根据id查询员工信息..." ); Employee employee = employeeService.getById(id); if (employee!=null ){ return R.success(employee); }else { return R.error("没有查询到对应的员工信息" ); } }
3.分类管理业务 效果展示
公共字段自动填充 问题分析
代码实现
添加@TableField
注解
1 2 3 4 5 6 7 8 9 10 11 @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;
编写baseContext工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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(); } }
在拦截器中设置threadlocal变量
完善自动填充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.itheima.reggie.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.LocalDateTime;@Component @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { log.info("公共字段自动填充【insert】...." ); metaObject.setValue("creatTime" , LocalDateTime.now()); metaObject.setValue("updateTime" , LocalDateTime.now()); metaObject.setValue("creatUser" , BaseContext.getCurrentId()); metaObject.setValue("updateUser" , BaseContext.getCurrentId()); } @Override public void updateFill (MetaObject metaObject) { log.info("公共字段自动填充【update】...." ); metaObject.setValue("updateTime" , LocalDateTime.now()); metaObject.setValue("updateUser" , BaseContext.getCurrentId()); } }
取消手动填充
演示
新增分类 需求分析
数据模型
代码开发
1 2 3 4 5 6 7 8 9 10 11 @PostMapping public R<String> save (@RequestBody Category category) { log.info("category:{}" ,category); categoryService.save(category); return R.success("新增分类成功" ); }
分类信息的分页查询 需求分析
代码开发
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/page") public R<Page> 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); }
删除分类 需求分析
代码开发
1 2 3 4 5 6 7 8 9 10 11 @DeleteMapping public R<String> delete (Long id) { log.info("删除分类,id为:{}" ,id); return R.success("分类信息删除成功" ); }
功能完善
定义业务异常类
1 2 3 4 5 6 7 8 9 10 11 12 package com.itheima.reggie.common;public class CustomException extends RuntimeException { public CustomException (String message) { super (message); } }
处理业务异常
1 2 3 4 5 6 7 8 9 10 @ExceptionHandler(CustomException.class) public R<String> exceptionHandler (CustomException ex) { log.error(ex.getMessage()); return R.error(ex.getMessage()); }
定义删除方法
1 2 3 4 5 6 7 8 9 package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itheima.reggie.entity.Category;public interface CategoryService extends IService <Category> { void remove (Long id) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.itheima.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itheima.reggie.common.CustomException;import com.itheima.reggie.entity.Category;import com.itheima.reggie.entity.Dish;import com.itheima.reggie.entity.Setmeal;import com.itheima.reggie.mapper.CategoryMapper;import com.itheima.reggie.service.CategoryService;import com.itheima.reggie.service.DishService;import com.itheima.reggie.service.SetmealService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Service public class CategoryServiceImpl extends ServiceImpl <CategoryMapper, Category> implements CategoryService { @Autowired private DishService dishService; @Autowired private SetmealService setmealService; @Override public void remove (Long id) { LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper <>(); dishLambdaQueryWrapper.eq(Dish::getCategoryId,id); int count1 = dishService.count(dishLambdaQueryWrapper); if (count1>1 ){ throw new CustomException ("当前分类下关联了菜品,不能删除" ); } LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper <>(); setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id); int count2 = setmealService.count(setmealLambdaQueryWrapper); if (count2>0 ){ throw new CustomException ("当前分类下关联了套餐,不能删除" ); } super .removeById(id); } }
调用删除方法
1 2 3 4 5 6 7 8 9 10 11 @DeleteMapping public R<String> delete (Long id) { log.info("删除分类,id为:{}" ,id); categoryService.remove(id); return R.success("分类信息删除成功" ); }
修改分类 需求分析
代码开发 1 2 3 4 5 6 7 8 9 10 11 @PutMapping public R<String> update (@RequestBody Category category) { log.info("修改分类信息:{}" ,category); categoryService.updateById(category); return R.success("修改分类信息成功" ); }
4.菜品管理业务开发
文件上传下载 文件上传介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Slf4j @RestController @RequestMapping("/common") public class CommonController { @Value("${reggie.path}") private String basePath; @PostMapping("/upload") public R<String> upload (MultipartFile file) { log.info(file.toString()); String originalFileName = file.getOriginalFilename(); String suffix=originalFileName.substring(originalFileName.lastIndexOf("." )); String fileName= UUID.randomUUID().toString()+suffix; 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); } }
文件下载介绍
文件上传代码实现
文件下载代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @GetMapping("/download") public void download (String name, HttpServletResponse response) { try { FileInputStream fileInputStream = new FileInputStream (new File (basePath + name)); ServletOutputStream outputStream = response.getOutputStream(); response.setContentType("image/jpge" ); 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(); } }
新增菜品 需求分析
数据模型
代码开发
编写菜品分类查询列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @GetMapping("/list") public R<List<Category>> list (Category category) { LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(category.getType()!=null ,Category::getType,category.getType()); queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime); List<Category> list = categoryService.list(queryWrapper); return R.success(list); }
编写菜品以及其口味保存方法
1 2 3 4 5 public interface DishService extends IService <Dish> { void saveWithFlavor (DishDto dishDto) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.itheima.reggie.service.impl;import com.itheima.reggie.dto.DishDto;import com.itheima.reggie.entity.Dish;import com.itheima.reggie.entity.DishFlavor;import com.itheima.reggie.mapper.DishMapper;import com.itheima.reggie.service.DishFlavorService;import com.itheima.reggie.service.DishService;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.util.List;import java.util.stream.Collectors;@Service public class DishServiceImpl extends ServiceImpl <DishMapper, Dish> implements DishService { @Autowired private DishFlavorService dishFlavorService; @Override @Transactional public void saveWithFlavor (DishDto dishDto) { this .save(dishDto); Long dishId = dishDto.getId(); List<DishFlavor> flavors = dishDto.getFlavors(); flavors = flavors.stream().map((item) -> { item.setDishId(dishId); return item; }).collect(Collectors.toList()); dishFlavorService.saveBatch(flavors); } }
springboot开启事务支持
controller层调用方法
1 2 3 4 5 6 7 8 9 10 11 @PostMapping public R<String> save (@RequestBody DishDto dishDto) { log.info(dishDto.toString()); dishService.saveWithFlavor(dishDto); return R.success("新增菜品成功" ); }
菜品分页查询 需求分析
代码开发
编写分页查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @GetMapping("/page") public R<Page> page (int page, int pageSize, String name) { Page<Dish> pageInfo = new Page <>(page, pageSize); Page<DishDto> dishDtoPage = new Page <>(); LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.like(name!=null ,Dish::getName,name); queryWrapper.orderByDesc(Dish::getUpdateTime); dishService.page(pageInfo,queryWrapper); BeanUtils.copyProperties(pageInfo,dishDtoPage,"records" ); List<Dish> records = pageInfo.getRecords(); List<DishDto> collect = records.stream().map((item) -> { DishDto dishDto = new DishDto (); BeanUtils.copyProperties(item, dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null ) { String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } return dishDto; }).collect(Collectors.toList()); dishDtoPage.setRecords(collect); return R.success(dishDtoPage); }
修改菜品 需求分析
代码开发
编写菜品信息回显代码
1 2 3 4 5 public interface DishService extends IService <Dish> { DishDto getByIdWithFlavor (Long id) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Override 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; }
controller层调用方法
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/{id}") public R<DishDto> get (@PathVariable Long id) { DishDto byIdWithFlavor = dishService.getByIdWithFlavor(id); return R.success(byIdWithFlavor); }
演示
正常回显
编写更新方法
1 void updateWithFlavor (DishDto dishDto) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override @Transactional public void updateWithFlavor (DishDto dishDto) { this .updateById(dishDto); LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(DishFlavor::getDishId,dishDto.getId()); dishFlavorService.remove(queryWrapper); List<DishFlavor> flavors = dishDto.getFlavors(); List<DishFlavor> collect = flavors.stream().map(flavor -> { flavor.setDishId(dishDto.getId()); return flavor; }).collect(Collectors.toList()); dishFlavorService.saveBatch(collect); }
controller层调用
1 2 3 4 5 6 7 8 9 10 11 12 13 @PutMapping public R<String> update (@RequestBody DishDto dishDto) { log.info(dishDto.toString()); dishService.updateWithFlavor(dishDto); return R.success("新增菜品成功" ); }
删除菜品 DishController.java
1 2 3 4 5 6 7 8 9 10 /** * 删除菜品 * @param ids * @return */ @DeleteMapping public R<String> delete(String ids){ dishService.deleteWithFlavor(ids); return R.success("删除成功"); };
DishService.java
1 void deleteWithFlavor (String ids) ;
DishServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override @Transactional public void deleteWithFlavor (String ids) { List<String> stringList= Arrays.asList(ids.split("," )); stringList.forEach(item->{ Long id=Long.parseLong(item); this .removeById(id); LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(DishFlavor::getDishId,id); dishFlavorService.remove(queryWrapper); }); }
5.套餐管理业务开发 效果展示
新增套餐 需求分析
数据模型
开发工作
完成根据菜品分类获取对应菜品数据的接口
DishController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @GetMapping("/list") public R<List<Dish>> list (Dish dish) { LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(dish.getCategoryId()!=null ,Dish::getCategoryId,dish.getCategoryId()); queryWrapper.eq(Dish::getStatus,1 ); queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); return R.success(list); }
完成套餐表单提交接口
编写保存方法
setmealService
1 void saveWithDish (SetmealDto setmealDto) ;
SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Transactional @Override public void saveWithDish (SetmealDto setmealDto) { this .save(setmealDto); List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); setmealDishes.stream().map(setmealDish -> { setmealDish.setSetmealId(setmealDto.getId()); return setmealDish; }).collect(Collectors.toList()); setmealDishService.saveBatch(setmealDishes); }
SetmealController.java
1 2 3 4 5 6 7 8 9 10 11 @PostMapping() public R<String> save (@RequestBody SetmealDto setmealDto) { log.info("套餐信息:{}" ,setmealDto); setmealService.saveWithDish(setmealDto); return R.success("新增套餐成功" ); }
套餐信息分页查询 需求分析
代码开发
编写分页接口
SetmealController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @GetMapping("/page") public R<Page> page (int page,int pageSize,String name) { Page<Setmeal> pageInfo = new Page <>(page, pageSize); Page<SetmealDto> dtoPage = new Page <SetmealDto>(); LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper <>(); 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> collect = records.stream().map(item -> { SetmealDto setmealDto = new SetmealDto (); BeanUtils.copyProperties(item, setmealDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null ) { String categoryName = category.getName(); setmealDto.setCategoryName(categoryName); } return setmealDto; }).collect(Collectors.toList()); dtoPage.setRecords(collect); return R.success(dtoPage); }
删除套餐 需求分析
代码开发
SetmealService
1 void removeWithDish (List<Long> ids) ;
SetmealServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Transactional @Override public void removeWithDish (List<Long> ids) { LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.in(Setmeal::getId,ids); queryWrapper.eq(Setmeal::getStatus,1 ); int count = this .count(queryWrapper); if (count>0 ){ throw new CustomException ("套餐正在售卖中,不能删除" ); } this .removeByIds(ids); LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper <>(); lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids); setmealDishService.remove(lambdaQueryWrapper); }
SetmealController.java
1 2 3 4 5 6 7 8 9 10 11 @DeleteMapping public R<String> delete (@RequestParam List<Long> ids) { log.info("ids:{}" ,ids); setmealService.removeWithDish(ids); return R.success("套餐数据删除成功" ); }
更新套餐 编写获取详情接口
SetmealController.java
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/{id}") public R<SetmealDto> getDetail (@PathVariable String id) { log.info(id); Setmeal byId = setmealService.getById(id); SetmealDto setmealDto = new SetmealDto (); BeanUtils.copyProperties(byId,setmealDto); LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(SetmealDish::getSetmealId,id); List<SetmealDish> list = setmealDishService.list(queryWrapper); setmealDto.setSetmealDishes(list); return R.success(setmealDto); }
编写更新接口
SetmealService
1 void updateWithDish (SetmealDto setmealDto) ;
SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override @Transactional public void updateWithDish (SetmealDto setmealDto) { this .updateById(setmealDto); List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); setmealDishes.stream().map(setmealDish -> { setmealDish.setSetmealId(setmealDto.getId()); return setmealDish; }).collect(Collectors.toList()); LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(SetmealDish::getSetmealId,setmealDto.getId()); setmealDishService.remove(queryWrapper); setmealDishService.saveBatch(setmealDishes); }
SetmealController.java
1 2 3 4 5 6 @PutMapping() public R<String> update (@RequestBody SetmealDto setmealDto) { log.info("套餐信息:{}" ,setmealDto); setmealService.updateWithDish(setmealDto); return R.success("更新套餐成功" ); }
更新套餐状态 编写更新状态接口
1 2 3 4 5 6 7 8 9 10 11 @PostMapping("/status/{status}") public R<String> changeStatus (@PathVariable Integer status,@RequestParam List<Long> ids) { List<Setmeal> collect = ids.stream().map(item -> { Setmeal setmeal = new Setmeal (); setmeal.setStatus(status); setmeal.setId(item); return setmeal; }).collect(Collectors.toList()); setmealService.updateBatchById(collect); return R.success("状态更新成功" ); }
6.手机验证码登录 效果展示
短信服务介绍
阿里云短信服务
注册账号
设置短信签名
完成申请后,等待审核
审核无法通过 可以采用测试api
赠送了一个模板
设置AccessKey
注意:accesskeyid 和secret需要及时保存,secret只会出现一次,也可以重新创建accesskey
编辑accesskey对应的权限
代码开发
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-core</artifactId > <version > 4.5.16</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-dysmsapi</artifactId > <version > 2.1.0</version > </dependency >
需求分析
数据模型
代码开发
编写短信发送接口
UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @PostMapping("/sendMsg") public R<String> sendMsg (@RequestBody User user, HttpSession httpSession) { String phone = user.getPhone(); if (StringUtils.isNotBlank(phone)){ String code = ValidateCodeUtils.generateValidateCode(4 ).toString(); log.info("code={}" ,code); SMSUtils.sendMessage("阿里云短信测试" ,"SMS_154950909" ,phone,code); httpSession.setAttribute(phone,code); return R.success("手机验证码短信发送成功" ); } return R.error("短信发送失败" ); }
编写登录接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @PostMapping("/login") public R<Object> login (@RequestBody Map map, HttpSession httpSession) { log.info(map.toString()); String phone=map.get("phone" ).toString(); String code=map.get("code" ).toString(); Object codeInSession = httpSession.getAttribute(phone); 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); } httpSession.setAttribute("user" ,user.getId()); return R.success(user); } return R.error("登陆成功失败" ); }
7.菜品展示、购物车、下单 效果展示
导入用户地址簿相关功能代码 需求分析
数据模型
导入功能代码
controller层直接导入
测试效果
菜品展示 需求分析
代码开发-梳理交互过程
代码开发 修改DishController.java
下的/list
的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @GetMapping("/list") public R<List<DishDto>> list (Dish dish) { LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(dish.getCategoryId()!=null ,Dish::getCategoryId,dish.getCategoryId()); queryWrapper.eq(Dish::getStatus,1 ); queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); List<DishDto> collect = list.stream().map((item) -> { DishDto dishDto = new DishDto (); BeanUtils.copyProperties(item, dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null ) { String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } Long dishId=item.getId(); LambdaQueryWrapper<DishFlavor> queryWrapper1 = new LambdaQueryWrapper <>(); queryWrapper1.eq(DishFlavor::getDishId,dishId); List<DishFlavor> list1 = dishFlavorService.list(queryWrapper1); dishDto.setFlavors(list1); return dishDto; }).collect(Collectors.toList()); return R.success(collect); }
效果
编写根据套餐获取菜品信息接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @GetMapping("/list") public R<List<Setmeal>> list (Setmeal setmeal) { LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(setmeal.getCategoryId()!=null ,Setmeal::getCategoryId,setmeal.getCategoryId()); queryWrapper.eq(setmeal.getStatus()!=null ,Setmeal::getStatus,setmeal.getStatus()); queryWrapper.orderByDesc(Setmeal::getUpdateTime); List<Setmeal> list = setmealService.list(queryWrapper); return R.success(list); }
效果
购物车 需求分析
数据模型
代码开发-梳理交互过程
代码开发-准备工作
接口1:购物车新增
ShoppingCartController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @PostMapping("/add") public R<ShoppingCart> add (@RequestBody ShoppingCart shoppingCart) { log.info("购物车数据:{}" ,shoppingCart); 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()); } ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper); if (cartServiceOne!=null ){ Integer number = cartServiceOne.getNumber(); cartServiceOne.setNumber(number+1 ); shoppingCartService.updateById(cartServiceOne); }else { shoppingCart.setNumber(1 ); shoppingCartService.save(shoppingCart); cartServiceOne=shoppingCart; } return R.success(cartServiceOne); }
接口2:获取购物车列表
1 2 3 4 5 6 7 8 9 @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); }
接口3:清空购物车
1 2 3 4 5 6 7 8 9 10 11 @DeleteMapping("/clean") public R<String> clean () { LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId()); shoppingCartService.remove(queryWrapper); return R.success("清空成功!" ); }
接口4:购物车商品减数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @PostMapping("/sub") public R<String> sub (@RequestBody Map map) { LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper <>(); if (map.get("dishId" )!=null ){ queryWrapper.eq(ShoppingCart::getDishId,map.get("dishId" )); }else { queryWrapper.eq(ShoppingCart::getDishId,map.get("setmealId" )); } queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId()); ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper); if (cartServiceOne.getNumber()>1 ){ cartServiceOne.setNumber(cartServiceOne.getNumber()-1 ); shoppingCartService.updateById(cartServiceOne); }else { shoppingCartService.removeById(cartServiceOne); } return R.success("减数成功!" ); }
用户下单 需求分析
数据模型
代码开发-梳理交互过程
代码开发-准备工作
接口1:下单接口
OrdersService
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface OrdersService extends IService <Orders> { void submit (Orders orders) ; }
OrdersServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 @Service @Slf4j public class OrdersServiceImpl extends ServiceImpl <OrdersMapper, Orders> implements OrdersService { @Autowired private ShoppingCartService shoppingCartService; @Autowired private UserService userService; @Autowired private AddressBookService addressBookService; @Autowired private OrderDetailService orderDetailService; @Override @Transactional public void submit (Orders orders) { 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); } }
OrdersController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @RestController @RequestMapping("/order") @Slf4j public class OrdersController { @Autowired private OrdersService ordersService; @PostMapping("/submit") public R<String> submit (@RequestBody Orders orders) { log.info("订单数据:{}" ,orders); ordersService.submit(orders); return R.success("下单成功" ); } }
分页查询历史订单 OrdersController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @GetMapping("/userPage") public R<Page<OrderDto>> userPage (int page,int pageSize) { Page<Orders> ordersPage = new Page <>(page,pageSize); LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Orders::getUserId, BaseContext.getCurrentId()); Page<Orders> ordersPage1 = ordersService.page(ordersPage, queryWrapper); List <Orders> ordersList=ordersPage1.getRecords(); Page<OrderDto> objectPage = new Page <>(); BeanUtils.copyProperties(ordersPage1,objectPage,"records" ); List<OrderDto> collect = ordersList.stream().map(item -> { OrderDto orderDto = new OrderDto (); BeanUtils.copyProperties(item, orderDto); LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper <>(); orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId, item.getId()); List<OrderDetail> list = orderDetailService.list(orderDetailLambdaQueryWrapper); orderDto.setOrderDetails(list); orderDto.setSumNum(list.size()); return orderDto; }).collect(Collectors.toList()); objectPage.setRecords(collect); return R.success(objectPage); }
8.订单明细模块 接口1.分页查询订单
1 2 3 4 5 6 7 8 9 10 @GetMapping("/page") public R<Page> page (int page, int pageSize, Long number, String beginTime, String endTime) { Page<Orders> ordersPage = new Page <>(page,pageSize); LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(number!=null ,Orders::getNumber,number); queryWrapper.between(beginTime!=null &&endTime!=null ,Orders::getOrderTime,beginTime,endTime); queryWrapper.orderByDesc(Orders::getOrderTime); Page<Orders> page1 = ordersService.page(ordersPage, queryWrapper); return R.success(page1); }
接口2.更新订单信息
1 2 3 4 5 @PutMapping public R<String> put (@RequestBody Orders orders) { boolean b = ordersService.updateById(orders); return R.success("更新成功" ); }
9.缓存优化 问题说明
使用git管理项目 创建仓库
将项目纳入到仓库管理
添加gitignore文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 .git logs rebel.xml target/ log.path_IS_UNDEFINED .DS_Store offine_user.md .apt_generated .classpath .factorypath .project .settings .springBeans .idea *.iws *.iml *.ipr nbproject/private/ build/ nbbuild/ dist/ nbdist/ .nb-gradle/ generatorConfig.xml
Add 项目
commit项目
定义远程仓库
推送项目至远程仓库
如果.idea文件再ignore中添加了依旧备推送至仓库中,则打开控制台输入以下指令
1 2 3 4 5 6 git rm -r --cached .idea git commit -m 'delete .ides' git push
刷新仓库会返现.idea文件夹消失了。
创建新的分支v1.0
推送分支v1.0
环境搭建 maven坐标 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
填写redis配置文件
配置类 1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<Object,Object> redisTemplate (RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate <>(); redisTemplate.setKeySerializer(new StringRedisSerializer ()); redisTemplate.setConnectionFactory(connectionFactory); return redisTemplate; } }
缓存短信验证码 实现思路
代码改造 注入RedisTemplate 1 2 @Autowired private RedisTemplate redisTemplate;
修改短信发送接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @PostMapping("/sendMsg") public R<String> sendMsg (@RequestBody User user, HttpSession httpSession) { String phone = user.getPhone(); if (StringUtils.isNotBlank(phone)){ String code = ValidateCodeUtils.generateValidateCode(4 ).toString(); log.info("code={}" ,code); SMSUtils.sendMessage("阿里云短信测试" ,"SMS_154950909" ,phone,code); redisTemplate.opsForValue().set(phone,code,5 , TimeUnit.MINUTES); return R.success("手机验证码短信发送成功" ); } return R.error("短信发送失败" ); }
修改登录接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @PostMapping("/login") public R<Object> login (@RequestBody Map map, HttpSession httpSession) { log.info(map.toString()); String phone=map.get("phone" ).toString(); String code=map.get("code" ).toString(); Object codeInSession=redisTemplate.opsForValue().get(phone); 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); } httpSession.setAttribute("user" ,user.getId()); redisTemplate.delete(phone); return R.success(user); } return R.error("登陆成功失败" ); }
演示
发送短信观察redis服务器
输入验证码登录后观察redis服务器
缓存菜品数据 实现思路
代码改造 改造查询方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @GetMapping("/list") public R<List<DishDto>> list (Dish dish) { List<DishDto> dishDtoList=null ; String key="dish_" +dish.getCategoryId()+"_" +dish.getStatus(); dishDtoList = (List<DishDto>)redisTemplate.opsForValue().get(key); if (dishDtoList!=null ){ return R.success(dishDtoList); } LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(dish.getCategoryId()!=null ,Dish::getCategoryId,dish.getCategoryId()); queryWrapper.eq(Dish::getStatus,1 ); queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); List<DishDto> collect = list.stream().map((item) -> { DishDto dishDto = new DishDto (); BeanUtils.copyProperties(item, dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null ) { String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } Long dishId=item.getId(); LambdaQueryWrapper<DishFlavor> queryWrapper1 = new LambdaQueryWrapper <>(); queryWrapper1.eq(DishFlavor::getDishId,dishId); List<DishFlavor> list1 = dishFlavorService.list(queryWrapper1); dishDto.setFlavors(list1); return dishDto; }).collect(Collectors.toList()); redisTemplate.opsForValue().set(key,collect,60 , TimeUnit.MINUTES); return R.success(collect); }
改造保存更新方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @PutMapping public R<String> update (@RequestBody DishDto dishDto) { log.info(dishDto.toString()); dishService.updateWithFlavor(dishDto); String keys="dish_" +dishDto.getCategoryId()+"_1" ; redisTemplate.delete(keys); return R.success("新增菜品成功" ); }
1 2 3 4 5 6 7 8 9 10 11 12 @PostMapping public R<String> save (@RequestBody DishDto dishDto) { log.info(dishDto.toString()); dishService.saveWithFlavor(dishDto); String keys="dish_" +dishDto.getCategoryId()+"_1" ; redisTemplate.delete(keys); return R.success("新增菜品成功" ); }
改造删除和更新状态方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @DeleteMapping public R<String> delete (String ids) { String[] split = ids.split("," ); List<Long> collect = Arrays.stream(split).map(item -> { Long id = Long.parseLong(item); return id; }).collect(Collectors.toList()); LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.in(Dish::getId,collect); queryWrapper.select(Dish::getCategoryId).groupBy(Dish::getCategoryId); List<Dish> dishList=dishService.list(queryWrapper); dishList.forEach(dish->{ String keys="dish_" +dish.getCategoryId()+"_1" ; redisTemplate.delete(keys); }); dishService.deleteWithFlavor(ids); return R.success("删除成功" ); };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @PostMapping("/status/{status}") public R<String> changeStatus (@PathVariable Integer status, String ids) { String[] split = ids.split("," ); List<Long> collect = Arrays.stream(split).map(item -> { Long id = Long.parseLong(item); return id; }).collect(Collectors.toList()); LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.in(Dish::getId,collect); queryWrapper.select(Dish::getCategoryId).groupBy(Dish::getCategoryId); List<Dish> dishList=dishService.list(queryWrapper); dishList.forEach(dish->{ String keys="dish_" +dish.getCategoryId()+"_1" ; redisTemplate.delete(keys); }); dishService.changeStatus(status,ids); return R.success("更新状态成功" ); };
Spring Cache Spring Cache 介绍
Spring Cache 常用注解
Spring Cache使用 开启缓存注解功能 注入CacheManger
添加缓存和删除缓存-@CachePut、@CacheEvict
查询缓存-@Cacheable
修改
多参数作为key进行缓存
使用redis作为缓存产品
缓存套餐数据 实现思路
代码改造 导入maven坐标 1 2 3 4 5 6 7 8 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-cache</artifactId > </dependency >
配置过期时间
开启缓存
加入@Cacheable注解
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/list") @Cacheable(value = "setmealCache",key = "#setmeal.categoryId+'_'+#setmeal.status") public R<List<Setmeal>> list (Setmeal setmeal) { LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(setmeal.getCategoryId()!=null ,Setmeal::getCategoryId,setmeal.getCategoryId()); queryWrapper.eq(setmeal.getStatus()!=null ,Setmeal::getStatus,setmeal.getStatus()); queryWrapper.orderByDesc(Setmeal::getUpdateTime); List<Setmeal> list = setmealService.list(queryWrapper); return R.success(list); }
返回类R实现序列化接口
加入@CacheEvict注解 删除指定分类下所有的缓存
1 2 3 4 5 6 7 @DeleteMapping @CacheEvict(value = "setmealCache",allEntries = true) public R<String> delete (@RequestParam List<Long> ids) { log.info("ids:{}" ,ids); setmealService.removeWithDish(ids); return R.success("套餐数据删除成功" ); }
1 2 3 4 5 6 7 8 9 10 11 12 @PostMapping() @CacheEvict(value = "setmealCache",allEntries = true) public R<String> save (@RequestBody SetmealDto setmealDto) { log.info("套餐信息:{}" ,setmealDto); setmealService.saveWithDish(setmealDto); return R.success("新增套餐成功" ); }
10.读写分离
主从复制 介绍
配置-前置条件
配置-主库Master
Ubuntu-Mysql8.0 修改配置文件路径
1 /etc/mysql/mysql.conf.d/mysqld.cnf
查看主服务器配置文件:sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
,看第83行:确认log_bin和server-id已经取消注释,设置无错误。
Mysql8.0 重启命令
1 2 3 4 5 6 weishao@ubuntuno2:/etc/mysql/mysql.conf.d$ service mysql restart ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units === Authentication is required to restart 'mysql.service' . Authenticating as: weishao Password: ==== AUTHENTICATION COMPLETE ===
mysql 8.0 配置方法
1 2 3 4 5 6 7 8 mysql> create user 'slave' @'10.211.55.4' identified by '123456' ; Query OK, 0 rows affected (0.03 sec) mysql> grant all privileges on * .* to 'slave' @'10.211.55.4' with grant option; Query OK, 0 rows affected (0.00 sec) mysql> flush privileges; Query OK, 0 rows affected (0.00 sec)
1 2 3 4 5 6 7 mysql> show master status; + | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | + | binlog.000004 | 1784 | | | | + 1 row in set (0.00 sec)
配置-从库Slave
1 2 3 4 5 mysql> change master to master_host= '10.211.55.5' ,master_user= 'slave' ,master_password= '123456' ,master_log_file= 'mysql-bin.000001' ,master_log_pos= 157 ; Query OK, 0 rows affected, 8 warnings (0.03 sec) mysql> start slave; Query OK, 0 rows affected, 1 warning (0.02 sec)
读写分离案例 背景
Sharding-JDBC介绍
入门案例
导入maven坐标
配置读写分离规则
配置允许bean定义并覆盖
项目实现读写分离 数据库环境准备(主从复制)
代码改造
导入maven坐标 1 2 3 4 5 <dependency > <groupId > org.apache.shardingsphere</groupId > <artifactId > sharding-jdbc-spring-boot-starter</artifactId > <version > 4.0.0-RC1</version > </dependency >
applicartion配置读写分离 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 shardingsphere: datasource: names: master,slave master: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.138.100:3306/rw?characterEncoding=utf-8 username: root password: root slave: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.138.101:3306/rw?characterEncoding=utf-8 username: root password: root masterslave: load-balance-algorithm-type: round_robin name: dataSource master-data-source-name: master slave-data-source-names: slave props: sql: show: true
spring下添加配置允许覆盖 1 2 3 spring: main: allow-bean-definition-overriding: true
Idea2022.2配置会爆红但不影响运行
测试
11.Nginx Nginx概述 Nginx介绍
Nginx下载和安装
ubuntu安装nginx
1 2 3 sudo su root apt-get install nginx
查看nginx是否安装成功
1 2 root@weishao1:/home/weishao nginx version: nginx/1.18.0 (Ubuntu)
启动nginx
在网页重输入ip地址,即可看到nginx的欢迎页面
Nginx目录结构
ubuntu安装nginx目录结构
nginx文件安装完成之后的文件位置:
/usr/sbin/nginx:主程序
/etc/nginx:存放配置文件
/usr/share/nginx:存放静态文件
/var/log/nginx:存放日志
Nginx命令 查看版本
1 2 root@weishao1:/usr/sbin nginx version: nginx/1.18.0 (Ubuntu)
检查配置文件正确性
1 2 3 root@weishao1:/usr/sbin nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
启动和停止
1 2 3 4 5 6 7 root@weishao1:/usr/sbin root@weishao1:/usr/sbin root@weishao1:/usr/sbin root 6521 1 0 12:10 ? 00:00:00 nginx: master process ./nginx www-data 6522 6521 0 12:10 ? 00:00:00 nginx: worker process www-data 6523 6521 0 12:10 ? 00:00:00 nginx: worker process root 6525 5802 0 12:10 pts/1 00:00:00 grep --color=auto nginx
重新加载配置文件
centos修改环境变量
ubuntu添加环境变量
1 root@weishao1:/etc/nginx
Nginx配置文件结构
Nginx具体应用 部署静态资源
反向代理
演示
现准备两个ubuntu系统的虚拟机服务器
首先在10.211.55.4上运行springboot程序
直接访问可以成功
接下来在10.211.55.5上做nginx配置
Ubuntu nginx1.8 修改配置文件
1 /etc/nginx/sites-enabled/default
其余关于80端口的配置需要删干净
访问成功
负载均衡
根据权重发送
演示
修改配置文件
12.前后端分离开发 问题说明
介绍
开发流程
前段技术栈
13.YApi 介绍
使用方式
14.Swagger 介绍
使用方式
导入Maven坐标
1 2 3 4 5 <dependency > <groupId > org.apache.shardingsphere</groupId > <artifactId > sharding-jdbc-spring-boot-starter</artifactId > <version > 4.0.0-RC1</version > </dependency >
导入Knife4j相关配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Slf4j @Configuration @EnableSwagger2 @EnableKnife4j public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void extendMessageConverters (List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter (); messageConverter.setObjectMapper(new JacksonObjectMapper ()); converters.add(0 ,messageConverter); } @Bean public Docket createRestApi () { return new Docket (DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller" )) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo () { return new ApiInfoBuilder () .title("瑞吉外卖" ) .version("1.0" ) .description("瑞吉外卖接口文档" ) .build(); } }
设置静态资源映射
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected void addResourceHandlers (ResourceHandlerRegistry registry) { log.info("开始进行静态资源映射..." ); registry.addResourceHandler("/backend/**" ).addResourceLocations("classpath:/backend/" ); registry.addResourceHandler("/front/**" ).addResourceLocations("classpath:/front/" ); registry.addResourceHandler("doc.html" ).addResourceLocations("classpath:/META-INF/resources/" ); registry.addResourceHandler("/webjars/**" ).addResourceLocations("classpath:/META-INF/resources/webjars/" ); super .addResourceHandlers(registry); }
在LoginCheckFilter中设置不需要处理的请求路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 log.info("拦截到请求:{}" ,requestURI); String[] urls=new String []{ "/employee/login" , "/employee/logout" , "/backend/**" , "/front/**" , "/common/**" , "/user/sendMsg" , "/user/login" , "/doc.html" , "/webjars/**" , "/swagger-resources" , "/v2/api-docs" };
访问成功
常用注解
15.项目部署 部署架构
部署环境说明
部署前端项目
后端项目的部署
完结撒花🙃