• 【JAVA后端开发】Part2--瑞吉外卖项目(员工管理)


    1,新增员工

    1.1 需求分析和数据模型

    后台系统中通过点击【添加员工】按钮跳转到新增页面,如下:

    在这里插入图片描述

    注意:新增员工其实就是将我们新增页面录入的员工数据插入employee表,需注意的是,employee表中对username 字段加入了唯一约束,因为username是员工的登录账号,必须唯一。

    1.2 代码开发

    1,当我们在添加页面填好信息,并点击保存按钮后。页面会发送ajax请求,将新增员工页面输入的数据以json的形式提交到服务端。形式如下:

    在这里插入图片描述

    2,服务端Controller接收页面提交的数据并调用service将数据进行保存。

    3,Service调用Mapper操作数据库,保存数据,

    整体代码如下:

        /**
         * 新增员工
         * @param employee
         * @return
         */
        @PostMapping
        public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
            log.info("新增员工,员工信息:{}",employee.toString());
    
            //设置初始密码123456,需要进行md5加密处理
            employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
    
            employee.setCreateTime(LocalDateTime.now());
            employee.setUpdateTime(LocalDateTime.now());
    
            //获得当前登录用户的id,即创建者
            Long empId = (Long) request.getSession().getAttribute("employee");
    
            employee.setCreateUser(empId);
            employee.setUpdateUser(empId);
    
            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

    1.3 bug修复

    bug: 因为数据库中的username是唯一的,所以当我们重复保存username相同的用户时,会抛出异常:java.sql.SQLIntegerityConstrainViolationException:Duplicate entry 'username' for key 'idx_username'

    修复:

    方式一:在controller方法中加入try、catch进行异常捕获,这样会导致代码重复冗余。因此提出方式二。

    方式二:使用异常处理器进行全局异常捕获。编写通用类GlobalExceptionHandler。代码如下:

    package com.wang.reggie.common;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.sql.SQLIntegrityConstraintViolationException;
    
    /**
     * 全局异常处理
     */
    @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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    2员工信息分页查询

    2.1需求分析:

    系统中的员工很多的时候,一个页面中全部展示出来会显得比较乱,不宜与查看,所以一般的系统中都会以分页的方式来展示列表数据。页面需要的信息如下:

    在这里插入图片描述

    2.2,代码开发

    程序执行流程:

    1,页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端。

    2,服务端Controller接收页面提交的数据并调用service层查询数据

    3,Service调用Mapper操作数据库,查询分页数据

    4,Controller将查询到的分页数据响应给页面

    5,页面接收到分页数据并通过ElementUI的Table组件展示到页面上

    代码开发步骤:

    1,配置MP的分页插件

    package com.wang.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;
    
    /**
     *@Author wjq
     *@Date 2022/6/28
     *@Version v1.0
     *@Description 配置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
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2,在Controller中创建方法,接收数据并返回查询数据。

        /**
         *@Author wjq
         *@Date 2022/6/28
         *@Version v1.0
         *@Description 员工 信息分页查询
         */
        @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
    • 21
    • 22
    • 23
    • 24
    • 25

    注:1. 当我们点击搜索框的放大镜和页面索引(过程相同)时,都会重新发送ajax请求,并调用controller方法进行查询显示。

    在这里插入图片描述

    在这里插入图片描述

    1. 将url请求中的参数转化为json格式的数据,是在request.js 文件的拦截器中实现的,代码如下:
      // request拦截器
      service.interceptors.request.use(config => {
        // 是否需要设置 token
        // const isToken = (config.headers || {}).isToken === false
        // if (getToken() && !isToken) {
        //   config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
        // }
        // get请求映射params参数
        if (config.method === 'get' && config.params) {
          let url = config.url + '?';
          for (const propName of Object.keys(config.params)) {
            const value = config.params[propName];
            var part = encodeURIComponent(propName) + "=";
            if (value !== null && typeof(value) !== "undefined") {
              if (typeof value === 'object') {
                for (const key of Object.keys(value)) {
                  let params = propName + '[' + key + ']';
                  var subPart = encodeURIComponent(params) + "=";
                  url += subPart + encodeURIComponent(value[key]) + "&";
                }
              } else {
                url += part + encodeURIComponent(value) + "&";
              }
            }
          }
          url = url.slice(0, -1);
          config.params = {};
          config.url = url;
        }
        return config
      }
    
    • 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

    3启用禁用员工账号

    3.1 需求分析

    1,在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作,账号禁用的员工不能登录系统,启用后的员工可以登录

    2,只有管理用可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示、

    在这里插入图片描述

    在这里插入图片描述

    3.2代码开发

    1,不同用户登录界面效果不同,管理员有禁用员工的权利和按钮。前端实现功能:

    第一步,获取到登录用户名

    created() {
      this.init()
      //add
      if(localStorage.getItem('userInfo')!=null){
        //获取当前登录员工的账号,并赋值给模型数据User
        this.user = JSON.parse(localStorage.getItem('userInfo')).username
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第二步,判断是否为管理员,如果是则展示当前按钮,否则不展示。

                <el-button
                  type="text"
                  size="small"
                  class="delBut non"
                  @click="statusHandle(scope.row)"
                  v-if="user === 'admin'"
                >
                  {{ scope.row.status == '1' ? '禁用' : '启用' }}
                </el-button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2,管理员点击禁用、启用按钮,页面发送ajax请求将参数(id,status)提交到服务端。

    3,服务端Controller接收页面提交的数据并调用Service更新数据

    4,Service调用Mapper操作数据库,修改用户信息。(启用禁用本质上也是一个更新操作,就是对status状态字段进行操作,因此创建一个通用的修改员工信息的方法update)。

    整体代码如下:

        /**
         * 根据id修改员工信息
         * @param employee
         * @return
         */
        @PutMapping
        public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
            log.info(employee.toString());
    
            Long empId = (Long)request.getSession().getAttribute("employee");
            employee.setUpdateTime(LocalDateTime.now());
            employee.setUpdateUser(empId);
            employeeService.updateById(employee);
    
            return R.success("员工信息修改成功");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.3代码分析

    在3.2中,代码没有问题也没有报错,但是功能没有实现,查看数据库中的数据也没有变换。观察控制台输出的Sql。可以发现,页面传回来参数id和数据库中对应记录的id并不相同。如下图所示:

    在这里插入图片描述

    问题出现原因: js对long型数据进行处理时丢失精度,导致提交的id和数据库中id不一致。

    3.4代码修复

    方案:在服务端给页面响应json数据时进行处理,将long型数据统一转化为String字符串,效果如下:

    在这里插入图片描述

    实施步骤:

    1,将项目提供的JacksonObjecMapper对象转化器复制到通用类common目录下,该转换器提供了将java对象转化为json数据的对象转化器。

    package com.wang.reggie.common;
    
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
    import java.math.BigInteger;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
    
    /**
     * 对象映射器:基于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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    2,在WebMvcConfig配置类中扩展Spring mvc的消息转化器,在此消息转换器中使用对应的对象转化器。

        /**
         * 扩展mvc框架的消息转换器
         * @param converters
         */
        @Override
        protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            log.info("扩展消息转换器...");
            //创建消息转换器对象
            MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
            //设置对象转换器,底层使用Jackson将Java对象转为json
            messageConverter.setObjectMapper(new JacksonObjectMapper());
            //将上面的消息转换器对象追加到mvc框架的转换器集合中
            converters.add(0,messageConverter);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4编辑员工信息

    4.1 需求分析

    在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面中前端回显员工信息并进行修改,最后点击报错按钮完成编辑操作。

    在这里插入图片描述

    在这里插入图片描述

    注:编辑和添加员工都用到通用的显示页面add.html。

    4.2 代码开发

    1,点击编辑按钮时,页面跳转到add.html,并在url中携带参数【员工唯一id】。

    2,在add.html页面获取url中的参数【员工id】。

    3,发送ajax请求,请求服务端并同时提交员工id参数。

    4,服务端接收请求,根据员工id查询员工信息,并将员工信息以json形式响应给前端页面。

    5,页面接收服务端相应的json数据,并通过VUE的数据绑定进行员工信息回显。

    6,前端修改信息并点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端。

    7,服务端接收员工信息,并进行处理,完成后给页面响应。

    8,页面接收到服务端响应信息后进行相应处理。

    整体代码涉及到两次与服务端进行交互,但是第二次修改员工信息点击保存按钮调用的方法同功能三相同。因此整体代码如下:

       /**
         * 根据id查询员工信息
         * @param id
         * @return
         */
        @GetMapping("/{id}")
        public R<Employee> getById(@PathVariable Long id){
            log.info("根据id查询员工信息...");
            Employee employee = employeeService.getById(id);
            if(employee != null){
                return R.success(employee);
            }
            return R.error("没有查询到对应员工信息");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注:从前端返回的url请求中获取对应的参数,该功能在index.js中实现。代码如下:

    //获取url地址上面的参数
    function requestUrlParam(argname){
      var url = location.href
      var arrStr = url.substring(url.indexOf("?")+1).split("&")
      for(var i =0;i<arrStr.length;i++)
      {
          var loc = arrStr[i].indexOf(argname+"=")
          if(loc!=-1){
              return arrStr[i].replace(argname+"=","").replace("?","")
          }
      }
      return ""
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    python常用数据结构-元组
    1.6 列表(Python)
    小程序 步骤条组件
    MongoDB基础
    听说大家很感兴趣玮子的学习心得,采访来了
    Cesium: 如何将倾斜摄影数据转换为3dTiles格式
    论文翻译:多延迟块频域自适应滤波器
    C++ 连接MySQL数据库并读取数据
    [华为云云服务器评测] 华为云耀云服务器 Java、node环境配置
    [附源码]计算机毕业设计JAVA濒危物种科普系统
  • 原文地址:https://blog.csdn.net/qq_44085437/article/details/125570813