瑞吉外卖项目详细分析笔记及所有功能补充代码

02-27 1125阅读

目录

  • 项目刨析简介
    • 技术栈
    • 项目介绍
    • 项目源码
    • 一.架构搭建
      • 1.初始化项目结构
      • 2.数据库表结构设计
      • 3.项目基本配置信息添加
        • 公共字段的自动填充
        • 全局异常处理类
        • 返回结果封装的实体类
        • 二.管理端业务开发
          • 1.员工管理相关业务
            • 1.1员工登录
            • 1.2员工退出
            • 1.3过滤器拦截
            • 1.4员工信息修改
            • 1.5员工信息分页查询
            • 1.6新增员工
            • 2.分类管理相关业务
              • 2.1分类的分页查询
              • 2.2新增分类
              • 2.3菜品或套餐的分类修改
              • 2.4菜品或套餐的分类删除
              • 3.菜品管理相关业务
                • 3.1分页查询
                • 3.2图片上传下载
                • 3.3新增菜品
                • 3.4修改菜品
                • 3.5删除菜品
                • 3.6菜品停售与起售(补充)
                • 4.套餐管理相关业务
                  • 4.1分页查询
                  • 4.2新增套餐
                  • 4.3修改套餐
                  • 4.4删除套餐
                  • 4.5套餐停售与起售(补充)
                  • 5.订单明细(补充)
                  • 三.移动端业务开发
                      • 1.用户登录与退出(退出为补充)
                      • 2.阿里云短信验证码
                      • 3.收货地址
                      • 4.菜品和套餐展示
                      • 5.菜品选规格
                      • 6.套餐点击展示(补充)
                      • 7.购物车
                      • 8.下订单
                      • 9.收货地址删除(补充)
                      • 10.用户支付后查看订单(补充)
                      • 11.再来一单(补充)
                      • 四.项目优化
                        • 1.使用Redis缓存
                          • 1.1缓存验证码
                          • 1.2缓存菜品查询数据
                          • 1.3Spring Cache缓存套餐数据
                          • 2.读写分离
                            • 2.1mysql主从复制
                            • 2.2Sharding-JDBC实现读写分离
                            • 2.3项目实现读写分离
                            • 3.使用Nginx服务器
                              • 3.1Nginx部署静态资源
                              • 3.2反向代理
                              • 3.3负载均衡
                              • 4.前后端分离开发
                                • 4.1YApi
                                • 4.2Swagger
                                • 4.3项目部署

                                  项目刨析简介

                                  #2022年末了,记录一下学习的项目实战经验和笔记吧

                                  这个是瑞吉外卖项目,补充一些视频里面没有定义的功能和记录一些功能实现逻辑的笔记;仅供学习参考,本人代码可能不太规范,也有可能自己写了有些错误自己没有察觉,但是功能自己测试是没有问题的;感谢各位的阅览,如有问题欢迎指正,如有遗漏后续继续补充


                                  技术栈

                                  涉及到的技术有Spring,Springboot,Mybatis-plus,MySQL,Redis,Linux,Git,Spring Cache,Sharding-JDBC,Nginx,Swagger。(Apifox这些工具应该不算技术吧,用的工具就不列举了)


                                  项目介绍

                                  该项目是一个外卖点餐系统,它分为后台管理端和用户移动端两方面开发,后台管理端为商家提供管理菜品套餐的服务,移动端为用户提供点菜下单功能。最终通过git管理项目,并用nginx部署前端,tomcat部署后端,使用mysql主从复制,从库读取,主库写入,再用shell脚本部署到服务器上。


                                  项目源码

                                  项目码云地址:https://gitee.com/dkgk8/reggie-git


                                  一.架构搭建

                                  1.初始化项目结构

                                  新建一个springboot项目

                                  pom导入的坐标

                                  		
                                  			org.springframework.boot
                                  			spring-boot-starter
                                  		
                                  		
                                  			com.github.xiaoymin
                                  			knife4j-spring-boot-starter
                                  			3.0.2
                                  		
                                  
                                  
                                  
                                  
                                  
                                  		
                                  			org.springframework.boot
                                  			spring-boot-starter-cache
                                  		
                                  		
                                  			org.springframework.boot
                                  			spring-boot-starter-data-redis
                                  		
                                  		
                                  			org.springframework.boot
                                  			spring-boot-starter-test
                                  			test
                                  		
                                  		
                                  			org.springframework.boot
                                  			spring-boot-starter-web
                                  			compile
                                  		
                                  		
                                  			com.baomidou
                                  			mybatis-plus-boot-starter
                                  			3.4.2
                                  		
                                  		
                                  			org.projectlombok
                                  			lombok
                                  			1.18.20
                                  		
                                  		
                                  			com.alibaba
                                  			fastjson
                                  			1.2.76
                                  		
                                  		
                                  			commons-lang
                                  			commons-lang
                                  			2.6
                                  		
                                  		
                                  			mysql
                                  			mysql-connector-java
                                  			runtime
                                  		
                                  		
                                  			com.alibaba
                                  			druid-spring-boot-starter
                                  			1.1.23
                                  		
                                  		
                                  			com.aliyun
                                  			aliyun-java-sdk-core
                                  			4.5.16
                                  		
                                  		
                                  			com.aliyun
                                  			aliyun-java-sdk-dysmsapi
                                  			2.1.0
                                  		
                                  

                                  yml配置文件添加的信息

                                  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: 123456
                                    redis:
                                      host: localhost
                                      port: 6379
                                      database: 0
                                    cache:
                                      redis:
                                        time-to-live: 1800000  #ms ->30min
                                  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
                                  reggie:
                                    path: D:\SpringBoot_Reggie\reggie_take_out\src\main\resources\static\front\hello\
                                  

                                  我后面将项目运行在服务器上了所以用了多环境开发,本地跑的不用在意这步

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  项目大致结构如下

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  感觉使用mybatis-plus之后就是

                                  实体类->mapper->service->serviceImpl->controller

                                  这个步骤写程序了

                                  2.数据库表结构设计

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  不每个表展示了,这里拿典型的员工表来看

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  3.项目基本配置信息添加

                                  导入前端资源

                                  在默认页面和前台页面的情况下,直接把这俩拖到resource目录下直接访问是访问不到的,因为被mvc框架拦截了,其实用springboot,可以直接放在static目录下,但是仍然不能直接访问前端页面,所以这里也可以直接放行static就好了

                                  所以我们要编写一个映射类放行这些资源

                                  WebMvcConfig类

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  公共字段的自动填充

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  这个我在另一篇文章写了很详细,链接:自动填充公共字段

                                  全局异常处理类

                                  虽然遇到异常后可以使用try-catch来处理,但是,代码量一大起来,许多的try catch就会很乱,代码也不简洁,不容易阅读,所以我们使用全局异常处理,在Common包下

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  自定义异常类

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  返回结果封装的实体类

                                  为了便于前后端数据传递,使用对象的形式封装数据更合适

                                  @Data
                                  public class R implements Serializable {
                                      private Integer code; //编码:1成功,0和其它数字为失败
                                      private String msg; //错误信息
                                      private T data; //数据
                                      private Map map = new HashMap(); //动态数据
                                      public static  R success(T object) {
                                          R r = new R();
                                          r.data = object;
                                          r.code = 1;
                                          return r;
                                      }
                                      public static  R error(String msg) {
                                          R r = new R();
                                          r.msg = msg;
                                          r.code = 0;
                                          return r;
                                      }
                                      public R add(String key, Object value) {
                                          this.map.put(key, value);
                                          return this;
                                      }
                                  }
                                  

                                  二.管理端业务开发

                                  1.员工管理相关业务

                                  1.1员工登录

                                  登录逻辑如下

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                      @PostMapping("/login")
                                      public R login(HttpServletRequest request, @RequestBody Employee employee){
                                          //1.将页面提交的明文密码进行md5加密
                                          String password = employee.getPassword();
                                          password = DigestUtils.md5DigestAsHex(password.getBytes());
                                          //2.根据页面提交的用户名username查数据库
                                          LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
                                          queryWrapper.eq(Employee::getUsername,employee.getUsername());
                                          Employee emp = employeeService.getOne(queryWrapper);
                                          //3.如果没有查询到则返回登录失败结果
                                          if (emp == null){
                                              return R.error("登录失败");
                                          }
                                          //4.密码比对,如果不一致则返回登录失败结果
                                          if (!emp.getPassword().equals(password)){
                                              return R.error("登录失败");
                                          }
                                          //5.查看员工账号状态是否锁定,若是禁用状态返回禁用信息
                                          if (emp.getStatus() == 0){
                                              return R.error("账号异常,已锁定");
                                          }
                                          //6.登录成功,将员工id存入Session  并返回登录成功结果
                                          request.getSession().setAttribute("employee",emp.getId());
                                          return R.success(emp);
                                      }
                                  

                                  1.2员工退出

                                  就是清除员工登录时存入session的员工id

                                      @PostMapping("/logout")
                                      public R logout(HttpServletRequest request){
                                          //1.清理Session中保存的当前登录员工id
                                          request.getSession().removeAttribute("employee");
                                          return R.success("退出成功");
                                      }
                                  

                                  1.3过滤器拦截

                                  现在没有过滤器,用户直接不用登录通过url+资源名可以随便访问,所以要加个过滤器,没有登陆时,拦截请求,不给访问,自动跳转到登陆页面

                                  过滤器处理逻辑

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  在启动类上添加注解@ServletComponentScan

                                  过滤器配置类注解@WebFilter(filterName=“拦截器类名首字母小写”,urlPartten=“要拦截的路径,比如/*”)

                                  判断用户是否已经登录,之前因为存入session里面有一个名为employee的对象,里面放的时用户id,那么只需要用getAttribute,看看session里get的数据是否为null就知道他是否在登陆状态

                                  这里提一嘴

                                  调用Spring核心包的字符串匹配类的对象,对路径进行匹配,并且返回比较结果

                                  如果相等就为true

                                  public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  直接上代码

                                  /**
                                   * 检查用户是否登录的过滤器
                                   */
                                  @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;
                                          //1.获取本次请求uri
                                          String requestURI = request.getRequestURI();
                                          //定义不需要处理的请求路径
                                          String[] urls=new String[]{
                                                  "/employee/login",
                                                  "/employee/logout",
                                                  "/backend/**",
                                                  "/front/**",
                                                  "/common/**",
                                                  "/user/sendMsg",
                                                  "/user/login",
                                                  "/doc.html",
                                                  "/webjars/**",
                                                  "/swagger-resources",
                                                  "/v2/api-docs"
                                          };
                                          //2.判断本次请求是否需要处理
                                          boolean check = check(urls, requestURI);
                                          //3.如果不需要处理则直接放行
                                          if (check){
                                              filterChain.doFilter(request,response);
                                              return;
                                          }
                                          //4-1.判断登录状态,如果已经登录,则直接放行
                                          if (request.getSession().getAttribute("employee")!=null){
                                              Long empId = (Long) request.getSession().getAttribute("employee");
                                              BaseContext.setCurrentId(empId);
                                              filterChain.doFilter(request,response);
                                              return;
                                          }
                                          //4-2.判断移动端登录状态,如果已经登录,则直接放行
                                          if (request.getSession().getAttribute("user")!=null){
                                              Long userId = (Long) request.getSession().getAttribute("user");
                                              BaseContext.setCurrentId(userId);
                                              filterChain.doFilter(request,response);
                                              return;
                                          }
                                          //5如果未登录则,通过输出流方式向客户端页面响应数据
                                          response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
                                          return;
                                      }
                                      /**
                                       * 路径匹配,检查本次请求是否需要放行
                                       */
                                      public boolean check(String[] urls,String requestURI){
                                          //遍历的同时调用PATH_MATCHER来对路径进行匹配
                                          for (String url : urls){
                                              boolean match = PATH_MATCHER.match(url,requestURI);
                                              if (match){
                                                  //匹配到了可以放行的路径,直接放行
                                                  return true;
                                              }
                                          }
                                          return false;
                                      }
                                  }
                                  

                                  1.4员工信息修改

                                  员工状态修改

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  遇到了问题,数据库id根据雪花算法有19位,而js对Long型数据处理时会丢失精度,只能保证前16位

                                  解决办法: 服务端给页面响应json数据时,将Long型数据统一转为String字符串

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  瑞吉外卖项目详细分析笔记及所有功能补充代码

                                  将Long型的Id转换为String类型的数据

                                  /**
                                   * 对象映射器:基于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);
                                      }
                                  }
                                  

                                  在MVC配置类中扩展一个消息转换器

                                      /**
                                       * 扩展mvc框架的消息转换器
                                       * @param converters
                                       */
                                      @Override
                                      protected void extendMessageConverters(List
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]