【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.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("未知错误");
    }
}

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;
    }
}

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. 当我们点击搜索框的放大镜和页面索引(过程相同)时,都会重新发送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
  }

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
  }
},

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

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

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("员工信息修改成功");
    }

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);
    }
}

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);
    }

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("没有查询到对应员工信息");
    }

注:从前端返回的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 ""
}

猜你喜欢

转载自blog.csdn.net/qq_44085437/article/details/125570813
今日推荐