• 项目实战(一) 瑞吉外卖


    技术

    SpringBoot+SSM 企业级项目
    SSM=Spring+Spring MVC+MyBatis

    在这里插入图片描述

    展示

    移动端:消费者使用
    后台管理端:餐饮企业员工使用
    在这里插入图片描述

    数据表

    在这里插入图片描述
    employee,员工表:用于存储后台管理系统的登陆账号,展示在“员工管理”栏。
    category,菜品和套餐分类表:用于展示在c端左侧分类栏,同时展示在后台的“分类管理”栏。
    dish,菜品表
    setmeal,套餐表
    setmeal_dish,套餐菜品关系表:一个套餐是多个菜品的集合。
    dish_flavor,菜品口味关系表
    uer,用户表:c端的用户
    address_book,地址薄表
    shopping_cart,购物车表
    orders,订单表
    order_detail,订单明细表
    一个菜品有多个口味;
    一个顾客可以同时把多个菜品和多个套餐放入购物车中。

    配置

    WebMvcConfig,使网页地址可以直接访问静态资源,默认需要放在static包下。
    在这里插入图片描述
    将/backend开头的所有网页地址,映射到类路径的backend包下

    功能开发:

    一、后台登录退出

    1. 后台登录

    在这里插入图片描述

    实体类Entity —— 数据表
    String —— varchar(50)
    Long —— bigint
    Integer —— int
    LocalDateTime —— datetime —— 2021-05-06 17:20:07

    在这里插入图片描述

    通用结果类 R

    后端返给前端的响应结果

    在这里插入图片描述
    在这里插入图片描述

    后台登录

    在这里插入图片描述
    在这里插入图片描述

    /**
         * 员工登录
         * @param request
         * @param employee
         * @return
         */
        @PostMapping("/login")
        public R<Employee> login(HttpServletRequest request,@RequestBody Employee employee){
       
    
            //1、将页面提交的密码password进行md5加密处理
            String password = employee.getPassword();
            password = DigestUtils.md5DigestAsHex(password.getBytes());
    
            //2、根据页面提交的用户名username查询数据库
            LambdaQueryWrapper<Employee> 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
    • 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

    错误时,前端展示
    在这里插入图片描述

    2. 后台退出

    在这里插入图片描述
    在这里插入图片描述

    3. 登录放行

    在这里插入图片描述本项目使用的是过滤器!
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 检查用户是否已经完成登录
     */
    @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();// /backend/index.html
    
            log.info("拦截到请求:{}",requestURI);
    
            //定义不需要处理的请求路径
            String[] urls = new String[]{
       
                    "/employee/login",
                    "/employee/logout",
                    "/backend/**",
                    "/front/**",
                    "/common/**",
                    "/user/sendMsg",
                    "/user/login"
            };
    
            //2、判断本次请求是否需要处理
            boolean check = check(urls, requestURI);
    
            //3、如果不需要处理,则直接放行
            if(check){
       
                log.info("本次请求{}不需要处理",requestURI);
                filterChain.doFilter(request,response);
                return;
            }
    
            //4-1、判断登录状态,如果已登录,则直接放行
            if(request.getSession().getAttribute("employee") != null){
       
                log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
    
                Long empId = (Long) request.getSession().getAttribute("employee");
                BaseContext.setCurrentId(empId);
    
                filterChain.doFilter(request,response);
                return;
            }
    
            //4-2、判断登录状态,如果已登录,则直接放行
            if(request.getSession().getAttribute("user") != null){
       
                log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));
    
                Long userId = (Long) request.getSession().getAttribute("user");
                BaseContext.setCurrentId(userId);
    
                filterChain.doFilter(request,response);
                return;
            }
    
            log.info("用户未登录");
            //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
            response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
            return;
    
        }
    
        /**
         * 路径匹配,检查本次请求是否需要放行
         * @param urls
         * @param requestURI
         * @return
         */
        public boolean check(String[] urls,String requestURI){
       
            for (String url : urls) {
       
                boolean match = PATH_MATCHER.match(url, requestURI);
                if(match){
       
                    return true;
                }
            }
            return false;
        }
    
    • 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

    启动类上加@ServletComponentScan注解
    filterChain.doFilter():放行

    二、员工管理

    1. 新增员工

    新增员工

    在这里插入图片描述

    异常处理器(进行全局异常捕获)

    在这里插入图片描述

    /**
     * 全局异常处理
     */
    @ControllerAdvice(annotations = {
       RestController.class, Controller.class})
    @ResponseBody
    @Slf4j
    public class GlobalExceptionHandler {
       
    
        /**
         * 异常处理方法
         * @return
         */
        @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("未知错误");
        }
    }
    
    • 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

    2.员工信息分页查询

    在这里插入图片描述

    /**
     * 配置MP的分页插件
     */
    @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
    • 13
    • 14
    • 15
        @GetMapping("/page")
        public R<Page> page(int page,int pageSize,String name){
       
            log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
    
            //构造分页构造器
            Page pageInfo = new Page(page,pageSize);
    
            //构造条件构造器
            LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();
            //添加过滤条件
            queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
            //添加排序条件
            queryWrapper.orderByDesc(Employee::getUpdateTime);
    
            //执行查询
            employeeService.page(pageInfo,queryWrapper);
    
            return R.success(pageInfo);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3. 启用/禁用员工账号

    更新员工状态

    在这里插入图片描述
    前端控制
    在这里插入图片描述

    消息转换器、对象转换器

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 对象映射器:基于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);
        }
    }
    
    • 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
        /**
         * 扩展mvc框架的消息转换器
         * @param converters
         */
        @Override
        protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
       
            log.info("扩展消息转换器...");
            //创建消息转换器对象
            MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
            //设置对象转换器,底层使用Jackson将Java对象转为json
            messageConverter.setObjectMapper(new JacksonObjectMapper(
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    Web(二)html5基础-超链接的应用(知识训练和编程训练)
    F- Kobolds and Catacombs_gov
    常写的页面(备份)
    【SpringBoot整合MQ】-----SpringBoot整合RocketMQ
    数据库(2):表的CRUD\内置函数\多表符合查询
    选择排序--java(详解)
    VB.net读写S50/F08IC卡,修改卡片密码控制位源码
    el-form简单封装一个列表页中的搜索栏
    Vue3 - 局部指令(详细教程)
    搞定这些问题,你就搞定了MySQL的视图
  • 原文地址:https://blog.csdn.net/weixin_40816843/article/details/126700600