瑞吉外卖笔记系列(2) —— 完善员工的后台系统登录功能,实现员工信息管理

本文档主要 完善员工的后台系统登录功能,新增员工,员工信息分页查询,启用/禁用员工账号,编辑员工信息

一、完善后台系统登录功能

1.1 问题分析

目前存在的问题是:理论上,用户必须在 http://localhost:8080/backend/page/login/login.html 登录,才能访问系统首页。但是,现在用户如果不登录,依然可以访问系统的首页 http://localhost:8080/backend/index.html

1.2 解决方案

针对该问题,解决方案主要有:使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有则跳转到登录页面。

本项目选择过滤器这一解决方案 , 实现步骤为:

  1. 创建自定义过滤器 LoginCheckfilter
  2. 在启动类上加入注解 @ServletComponentScan
  3. 完善过滤器实现逻辑

过滤器具体的处理逻辑如下:

  1. 获取本次请求的URI
  2. 判断本次请求是否需要处理
  3. 如果不需要处理,则直接放行
  4. 判断登录状态,如果已登录,则直接放行
  5. 如果未登录则返回未登录结果
    在这里插入图片描述

1.3 前端代码分析

src/main/resources/backend/js/request.js 文件,前端响应拦截器的代码如下:

 // 响应拦截器
  service.interceptors.response.use(res => {
      if (res.data.code === 0 && res.data.msg === 'NOTLOGIN') {// 返回登录页面
        console.log('---/backend/page/login/login.html---')
        localStorage.removeItem('userInfo')
        window.top.location.href = '/backend/page/login/login.html'
      } else {
        return res.data
      }
    }

NOTLOGIN 字符串将会用于 后端的拦截器的实现。

1.4 代码实现

新建 java 类 src/main/java/com/idealzouhu/reggie/filter/LoginCheckFilter.java, 具体代码如下:

/**
 * 检查用户是否已经完成了登录
 */
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    // 路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /**
     * 路径匹配,检测本次请求是否需要放行
     * @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;
    }

    @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();
        log.info("拦截到请求: {}",request.getRequestURI());

        // 2. 判断本次请求是否需要处理
        String[] urls = new String[]{        // 定义不需要处理的请求路径;
                "/employee/login",
                "/emplyee/logout",
                "/backend/**",
                "/front/**"
        };
        boolean check = check(urls, requestURI);

        // 3. 如果不需要处理,则直接放行
        if(check){
            log.info("本次请求  {} 不需要处理",request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }

        // 4. 判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee") != null){
            log.info("用户已登录,id为:  {} ",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request, response);
            return;
        }

        // 5. 如果未登录则返回未登录结果. 通过输出流方式向客户端页面响应数据
        log.info("用户未登录");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }
}

注意,官方视频在这里少打了一个 return ,导致登录用户存放在session里面会被删掉(即误执行response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))) ,间接执行前端代码localStorage.removeItem('userInfo') )。

1.5 功能测试

直接访问 http://localhost:8080/backend/index.html , 看页面是否会跳转到登录页面

二、新增员工

2.1 需求分析

在后台管理系统中, 通过新增员工来添加后台系统用户。

在这里插入图片描述

2.2 数据模型

本质上,将新增的员工数据新增到 employee 表里面
在这里插入图片描述

需要注意,employee 表中对 username 字段加入了唯一约束。因为 username 是员工的登录账号,具备唯一性。
在这里插入图片描述

2.3 前端代码分析

员工列表页面为 src/main/resources/backend/page/member/list.html

 <el-button
          type="primary"
          @click="addMemberHandle('add')"
        >
          + 添加员工
</el-button>

..........

 // 添加
          addMemberHandle (st) {
            if (st === 'add'){
              window.parent.menuHandle({
                id: '2',
                url: '/backend/page/member/add.html',
                name: '添加员工'
              },true)
            } else {
              window.parent.menuHandle({
                id: '2',
                url: '/backend/page/member/add.html?id='+st,
                name: '修改员工'
              },true)
            }
          }

menuHandle方法的实现在 src/main/resources/backend/index.html 里面,

mounted() {
  window.menuHandle = this.menuHandle
},

 menuHandle(item, goBackFlag) {
            this.loading = true
            this.menuActived = item.id
            this.iframeUrl = item.url
            this.headTitle = item.name
            this.goBackFlag = goBackFlag
            this.closeLoading()
          }

最后,在添加员工的页面 src/main/resources/backend/page/member/add.html,点击保存按钮, 会触发 addEmployee() 方法

 submitForm (formName, st) {
            this.$refs[formName].validate((valid) => {
              if (valid) {
                if (this.actionType === 'add') {
                  const params = {
                    ...this.ruleForm,
                    sex: this.ruleForm.sex === '女' ? '0' : '1'
                  }
                  addEmployee(params).then(res => {
                    if (res.code === 1) {
                      this.$message.success('员工添加成功!')
                      if (!st) {
                        this.goBack()
                      } else {
                        this.ruleForm = {
                          username: '',
                          'name': '',
                          'phone': '',
                          // 'password': '',
                          // 'rePassword': '',/
                          'sex': '男',
                          'idNumber': ''
                        }
                      }

addEmployee() 方法的实现代码在 backend/api/member.js 里面

// 新增---添加员工
function addEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'post',
    data: { ...params }
  })
}

需要注意,我将 url 改成 ‘/employee/addEmployee’

前端代码较为复杂,建议观看原视频业务开发Day2-06-新增员工_梳理程序执行流程_哔哩哔哩_bilibili

2.4 代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:

  1. 页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
  2. 服务端Contraller接收页面提交的数据并调用Service将数据进行保存
  3. Service调用Mapper操作数据库,保存数据

![

  1. 在这里插入图片描述

](https://img-blog.csdnimg.cn/direct/33fdd9a32b4148eabc0bb6500e8d3782.png)

在 employee 里面新建方法 ,

  /**
     * 新增员工
     * @param employee
     * @return
     */
    @PostMapping("/addemployee")
    public R<String> addEmployee(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("新增员工成功");
//        return null;  // 如果返回 null 值,会导致前端页面莫名其妙跳转到 登录页面,并删除登录信息
    }

2.5 功能测试

输入员工信息,点击 “保存”

在这里插入图片描述

在数据库里面,也可以看到相应的信息

在这里插入图片描述

另外,如果我们再次新增用户名为 1235 的用户,后台会报错

java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1235' for key 'employee.idx_username'

这是因为 username 是员工的登录账号,具备唯一性。

2.6 代码修复

前面的程序还存在一个问题:当新增员工的账号已经存在时,由于 username 具备唯一性,程序会抛出异常

java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1235' for key 'employee.idx_username'

此时需要我们进行异常捕获,通常有两种处理方式:

  1. 在Controller 方法中加入 try、catch 进行异常捕获
  2. 使用异常处理器进行全局异常捕获 (本文采用这种方式)

创建 src/main/java/com/idealzouhu/reggie/common/GlobalExceptionHandler.java, 实现代码为:

package com.idealzouhu.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;

/**
 * 全局异常处理
 */
@Slf4j
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
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.7 总结

  1. 根据产品原型明确业务需求
  2. 重点分析数据的流转过程和数据格式
  3. 通过debug断点调试跟踪程序执行过程

在这里插入图片描述

三、员工信息分页查询

3.1 需求分析

当员工数量较多时,我们需要分页查询员工信息,便于查看。

同时,我们还可以根据员工姓名查询,即按条件分页查询

在这里插入图片描述

其中,参数主要有 page、pagesize,name。name是我们按条件分页查询的条件

在这里插入图片描述

3.2 前端代码分析

(1) List.html

员工列表页面为 src/main/resources/backend/page/member/list.html

<el-pagination
        class="pageList"
        :page-sizes="[10, 20, 30, 40]"   
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="counts"
        :current-page.sync="page"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      ></el-pagination>

...................

data() {
          return {
             input: '',
             counts: 0,
             page: 1,         // 分页查询的默认值
             pageSize: 10,
             tableData : [],
             id : '',
             status : '',
          }
        }
 
 .....................
 
created() {
          this.init()
          this.user = JSON.parse(localStorage.getItem('userInfo')).username
        }
 
 .......
 
   async init () {
            const params = {
              page: this.page,
              pageSize: this.pageSize,
              name: this.input ? this.input : undefined
            }
            await getMemberList(params).then(res => {
              if (String(res.code) === '1') {
                this.tableData = res.data.records || []
                this.counts = res.data.total
              }
            }).catch(err => {
              this.$message.error('请求出错了:' + err)
            })
          },
          handleQuery() {
            this.page = 1;
            this.init();
          }

这部分代码展示了 为什么员工信息查询会每次自动请求。

:page-sizes="[10, 20, 30, 40]" : 对应 分页查询里面的下拉条, 即每页显示多少页。

pageSize: 10: 表示默认值,即每页显示 10 页

(2)getMemberList()

getMemberList() 方法的实现代码在 backend/api/member.js 里面

function getMemberList (params) {
  return $axios({
    url: '/employee/page',
    method: 'get',
    params
  })
}

(3) request.js

我们会发现一个问题:在list.html, page 和pagesize 参数是 json 格式。但是,在URI里面,参数并不是 json 格式,而是 http://localhost:8080/employee/page?page=1&pageSize=10

在 src/main/resources/backend/js/request.js , request拦截器将参数重新组装,放在 URI 的后面

  // request拦截器
  service.interceptors.request.use(config => {
    // 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) + "&";
          }
        }
      }

3.3 代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:

  1. 页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端
  2. 服务端Controller接收页面提交的数据并调用Service查询数据
  3. Service调用Mapper操作数据库,查询分页数据
  4. Controller将查询到的分页数据响应给页面
  5. 页面接收到分页数据并通过ElementUI的Table组件展示到页面上

为了更好地实现分页查询,我们使用MP提供的分页查询:

(1)配置 MP 分页插件

创建src/main/java/com/idealzouhu/reggie/config/MybatisPlusConfig.java

package com.idealzouhu.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;

/**
 * 配置 MP 的分页插件
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

}

(2)创建 controller 方法

src/main/java/com/idealzouhu/reggie/controller/EmployeeController.java类里面,新建方法

 @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name){
        log.info("page = {}, pageSize = {}, name={} ", page, pageSize,name);

        // 1. 构造分页查询器
        Page pageInfo = new Page(page, pageSize);

        // 2. 构造条件构造器
        LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name); // 添加过滤条件
        lambdaQueryWrapper.orderByDesc(Employee::getUpdateTime);  // 添加排序条件

        // 3.执行查询
        employeeService.page(pageInfo, lambdaQueryWrapper);

        return R.success(pageInfo);
    }

3.4 功能测试

直接查看员工信息分页是否正常

四、启用/禁用员工账号

4.1 需求分析

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

需要注意的是,只有管理员 admin 具备权限,可以对所用员工账号进行启用、禁用操作。所以普通用户登录系统后启用、禁用按钮不显示。

如果某个员工账号状态为正常,则按钮显示为 禁用;如果员工账号状态为已禁用, 则按钮显示为 ”启用“。

在这里插入图片描述

4.2 前端代码分析

(1) List.html

在员工列表页面 src/main/resources/backend/page/member/list.html

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

created() {
          this.init()
          // 获取当前登录员工的账号,并赋值给模型数据
          this.user = JSON.parse(localStorage.getItem('userInfo')).username
        }
        
.................
//状态修改
          statusHandle (row) {
            this.id = row.id
            this.status = row.status
            this.$confirm('确认调整该账号的状态?', '提示', {
              'confirmButtonText': '确定',
              'cancelButtonText': '取消',
              'type': 'warning'
              }).then(() => {
              enableOrDisableEmployee({ 'id': this.id, 'status': !this.status ? 1 : 0 }).then(res => {
                console.log('enableOrDisableEmployee',res)
                if (String(res.code) === '1') {
                  this.$message.success('账号状态更改成功!')
                  this.handleQuery()
                }
              }).catch(err => {
                this.$message.error('请求出错了:' + err)
              })
            })
          }

{{ scope.row.status == '1' ? '禁用' : '启用' }}: 如果员工账号状态scope.row.status为正常(即1),则按钮显示为 禁用;如果员工账号状态为已禁用, 则按钮显示为 启用

v-if="user === 'admin'": 如果登录用户是管理员,则看到 禁用启用按钮。否则的话,则不显示。

statusHandle(): 修改员工状态的 核心函数

(2)enableOrDisableEmployee()

enableOrDisableEmployee() 方法的实现代码在 backend/api/member.js 里面

// 修改---启用禁用接口
function enableOrDisableEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}

4.2 代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:

  1. 页面发送ajax请求,将参数(id、status)提交到服务端
  2. 服务端Controller接收页面提交的数据并调用Service更新数据
  3. Service调用Mapper操作数据库

本质上,这段代码的作用是更改员工的 status 字段。

4.3 代码修复

问题:”禁用/启用“功能可以运行,但是数据库里面 status 状态并没有更新

**原因:**前端返回过来的json数据里面,员工 id 和其实际 id 不一样。 js 对 long 型数据进行处理时丢失精度,导致提交的 id 和 数据库中的id 不一致。

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

具体实现步骤:

  1. 提供 对象转换器 JacksonObject , 基于 JackSon 进行 Java 对象到json数据的转换。 创建 src/main/java/com/idealzouhu/reggie/common/JacksonObjectMapper.java , 相应代码如下
/**
 * 对象映射器:基于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. 在 WebMvcConfig 配置类中扩展 Spring MVC 的消息转换器,在此消息转换器中使用提供对象转换器进行 Java 对象到 json 数据的转换。如果不扩展的话,那么项目会使用 Spring MVC 所使用的默认配置器。
/**
     * 扩展 MVC 框架的消息转换器
     * @param converters
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器");

        // 创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();

        // 设置对象转换器, 底层使用 Jackson 将对象转为 json
        messageConverter.setObjectMapper(new JacksonObjectMapper());

        // 将上面的消息转换器对象追加到 MVC 框架的转换器集合中
        converters.add(0, messageConverter);

//        WebMvcConfigurer.super.extendMessageConverters(converters);
    }

我们可以通过调试来观察消息转换器是否添加成功

在这里插入图片描述

我们再次运行启用/禁用 功能,可以很明显观察到 json 对象的变化

在这里插入图片描述

五、编辑员工账号

5.1 需求分析

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

对于编辑员工,如果员工不存在,则是 新增员工。如果员工已存在,则是 编辑员工。因此,对于后端而言,编辑员工和新增员工的代码基本上类似。

5.2 前端代码分析

(1) add.html

注意: add.html 页面为公共页面,新增员工和编辑员工都是在此页面操作

src/main/resources/backend/page/member/add.html 里面,

created() {
  this.id = requestUrlParam('id')
  this.actionType = this.id ? 'edit' : 'add'
  if (this.id) {
    this.init()
  }
}
......
async init () {
            queryEmployeeById(this.id).then(res => {
              console.log(res)
              if (String(res.code) === '1') {
                console.log(res.data)
                this.ruleForm = res.data
                this.ruleForm.sex = res.data.sex === '0' ? '女' : '男'
                // this.ruleForm.password = ''
              } else {
                this.$message.error(res.msg || '操作失败')
              }
            })
          }

this.actionType = this.id ? 'edit' : 'add': id 有值的话,就是编辑员工。否则新增员工

(2)requestUrlParam()

src/main/resources/backend/js/index.js 里,requestUrlParam() 解析 url地址上面的参数。举个例子,http://localhost:8080/backend/page/member/add.html?id=1745800121368453122,该函数会将 id 参数动态提取出来。

//获取url地址上面的参数
function requestUrlParam(argname){
  var url = location.href
//  alert(url)
  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 ""
}

(3) queryEmployeeById()

src/main/resources/backend/api/member.js里, queryEmployeeById() 根据员工 id 查询员工信息

// 修改页面反查详情接口
function queryEmployeeById (id) {
  return $axios({
    url: `/employee/${id}`,
    method: 'get'
  })
}

5.2 代码开发

在开发代码之前需要梳理一下操作过程和对应的程序的执行流程:

  1. 点击编辑按钮时,页面跳转到 add.html,并在 url 中携带参数[员工id] (例如 http://localhost:8080/backend/page/member/add.html?id=1745800121368453122
  2. 在 add.html 页面获取 url中的参数[员工id]
  3. 发送 ajax 请求,请求服务端,同时提交员工 id 参数
  4. 服务端接收请求,根据员工 id 查询员工信息,将员工信息以json形式响应给页面
  5. 页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显
  6. 点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端
  7. 服务端接收员工信息,并进行处理,完成后给页面响应
  8. 页面接收到服务端响应信息后进行相应处理

src/main/java/com/idealzouhu/reggie/controller/EmployeeController.java 里面, 添加以下代码:

/**
     * 根据id 编辑员工 (事实上,这段代码与下面的功能”启用/禁用员工“一样)
     * @param employee
     * @return
     */
    @PutMapping("/editEmployee")
    public R<String> editEmployee(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("员工信息修改成功");
    }

/**
     * 根据员工 id 查询信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<Employee> getById(@PathVariable Long id){
        log.info("根据员工id({})查询信息:", id);
        Employee employee = employeeService.getById(id);
        if(employee != null){
            return R.success(employee);
        }
        return R.error("没有查询到对应员工信息");
    }

参考资料

idealzouhu/reggie-take-out: 瑞吉外卖项目, 利用SpringBoot + SSM技术栈实现 (github.com)

黑马程序员Java项目实战《瑞吉外卖》bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/317833.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

宽度优先搜索算法(BFS)详解(超级详细讲解,附有大图)

目录 一.宽度优先搜索&#xff08;BFS&#xff09;是什么&#xff1f; 二.图解宽搜&#xff08;BFS&#xff09; 三.对比与发现 四。工具——队列 五.模板 六.最后 一.宽度优先搜索&#xff08;BFS&#xff09;是什么&#xff1f; 百度百科这样说&#xff1a; 宽度优先搜索…

蓝桥杯省赛无忧 STL 课件15 queue

01 queue队列 02 priority_queue优先队列 接下来介绍几种优先队列修改比较函数的方法 03 deque双端队列 04 例题讲解 https://www.lanqiao.cn/problems/1113/learning/?page1&first_category_id1&problem_id1113输入 5 IN xiaoming N IN Adel V IN laozhao N OUT …

抓交通肇事犯(python)

问题描述&#xff1a; 一辆卡车违反交通规则&#xff0c;撞人后逃跑。现场有三人目击该事件&#xff0c;但都没有记住车号&#xff0c;只记下了车号的一些特征。甲说&#xff1a;牌照的前两位数字是相同的&#xff1b;乙说&#xff1a;牌照的后两位数字是相同的&#xff0c;但…

【金融数据分析】计算2023年沪深300行业涨跌幅

本文我们来计算2023年沪深300行业涨跌幅&#xff0c;最后呈现的页面是这样的 先来说后端的代码&#xff0c;计算行业涨跌幅的原理就是将某个行业所有成分股的涨跌幅加起来&#xff0c;然后除以股票数量得到一个平均涨跌幅 // 查询行业涨跌幅public List<CSI300IndustryRankV…

如何将.NET 8.0的ASP.NET Core Web API部署成Windows服务

写在前面 前面写了一篇关于将.NET应用转换成Windows服务的方法&#xff0c;其实真正的目的是为了探索如何将Asp.Net Core Web Api 部署成Windows 服务。基于上一篇的基础&#xff0c;只需把创建 WebApplication 的代码放到 BackgroundService 的ExecuteAsync方法中即可。 其中…

【一】通信协议概述

通信协议概述 简介&#xff1a; 很早之前就思考了要写一下电力系统常用的几种通信协议&#xff0c;一直拖着也没有行动&#xff0c;这次终于下定决心来出一个《通信协议》这样的专栏。电力行业数字化方面资料较少&#xff0c;我理解主要一方面是数字化程度还不高&#xff0c;一…

笔记系统的部署架构

前天给笔记系统打了0.0.3的tag&#xff0c;一个简单的全栈功能闭环基本完成。既然是开源&#xff0c;因此&#xff0c;这里有必要分享一下部署结构&#xff0c;希望能够获得小伙伴们的反馈。 目前整个系统采用docker容器来部署。应用介绍 auth_app: 登录/注册的前端应用 web_ap…

WebRTC入门:基础的核心协议与概念(二十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

Open3D对产生偏差的点云数据进行校正

由于相机安装问题&#xff0c;导致点云数据两边翘起来&#xff0c;为了计算把翘起来的部分拉平整 import time import open3d as o3d; import numpy as np; import matplotlib.pyplot as plt from scipy.signal import find_peaks import pandas as pdOriginalPly o3d.io.rea…

自养号测评:掌握Shopee运营黑科技的必备攻略

虾皮卖家们经常挂在嘴边的“权重”&#xff0c;简而言之&#xff0c;就是商品或店铺在Shopee平台上受到重视的程度。它的存在是为了评估商品或店铺是否满足用户需求&#xff0c;能否助力订单转化&#xff0c;为平台创造更多收益。就像我们给孩子们打分一样&#xff0c;分数越高…

Vue3函数式弹窗实现

要在一些敏感操作进行前要求输入账号和密码&#xff0c;然后将输入的账号和密码加到接口请求的header里面。如果每个页面都去手动导入弹窗组件&#xff0c;在点击按钮后弹出弹窗。再拿到弹窗返回的账号密码后去请求接口也太累了&#xff0c;那么有没有更简单的实现方式呢&#…

python_locust压测

可以在命令行启动&#xff0c;cmd命令框&#xff0c;进入此文件的目录输入&#xff1a; locust -f .\dash.py --host"https://litemall.hogwarts.ceshiren.com" -f&#xff1a; 指定性能测试脚本文件的绝对路径。 –host&#xff1a; 指定被测试应用的URL的地址&…

Qt/QML编程学习之心得:Grid、GridLayout、GridView、Repeater(33)

GRID网格用处非常大,不仅在excel中,在GUI中,也是非常重要的一种控件。 Grid 网格是一种以网格形式定位其子项的类型。网格创建一个足够大的单元格网格,以容纳其所有子项,并将这些项从左到右、从上到下放置在单元格中。每个项目都位于其单元格的左上角,位置为(0,0)。…

【JavaScript】深度理解js的函数(function、Function)

简言 学了这么久的JavaScript&#xff0c;函数在JavaScript中最常用之一&#xff0c;如果你不会函数&#xff0c;你就不会JavaScript。 函数就是Function对象&#xff0c;一个函数是可以通过外部代码调用的一个“子程序”&#xff0c;它是头等&#xff08;first-class&#xf…

微机原理常考简答题(二)

一&#xff0c;简述8086CPU响应可屏蔽中断的条件及过程。 CPU响应可屏蔽中断的条件是有中断请求&#xff0c;中断标志IF1开中断&#xff0c;现行指令执行结束。 CPU响应可屏蔽中断的过程&#xff1a;CPU在INTR引脚上接到一个中断请求信号&#xff0c;如果此时IF1&#xff0c;并…

group by 查询慢的话,如何优化?

1、说明 根据一定的规则&#xff0c;进行分组。 group by可能会慢在哪里&#xff1f;因为它既用到临时表&#xff0c;又默认用到排序。有时候还可能用到磁盘临时表。 如果执行过程中&#xff0c;会发现内存临时表大小到达了上限&#xff08;控制这个上限的参数就是tmp_table…

基于完整熵编码系数组的JPEG图像加密方案

论文题目&#xff1a;JPEG image encryption with grouping coefficients based on entropy coding 期刊&#xff1a;Journal of Visual Communication and Image Representation 分区&#xff1a;中科苑三区&#xff0c;老牌图像处理期刊 文章目录 摘要概要整体架构流程实验结…

mac怎么拼图?Mac拼图技巧分享

mac怎么拼图&#xff1f;在Mac上拼图是一种令人愉悦的创意表达方式&#xff0c;可以让你将多张图片巧妙地融合在一起&#xff0c;创造出令人惊叹的艺术品。本文将向你介绍在Mac上进行拼图的几种方法&#xff0c;帮助你轻松实现这一目标。 一、使用Mac内置的预览功能进行拼图 M…

100个GEO基因表达芯片或转录组数据处理之GSE159676(002)

写在前边 虽然现在是高通量测序的时代&#xff0c;但是GEO、ArrayExpress等数据库储存并公开大量的基因表达芯片数据&#xff0c;还是会有大量的需求去处理芯片数据&#xff0c;并且建模或验证自己所研究基因的表达情况&#xff0c;芯片数据的处理也可能是大部分刚学生信的道友…

物联网协议Coap之Core和NetWork简介

目录 前言 一、Coap的Core包 1、Coap对象 2、Message对象 3、Request对象 4、Response对象 二、Coap的NetWork调试 1、UDP运行模式 2、Network消息接收 3、Sender线程发送数据 三、总结 前言 在之前的博文中&#xff0c;对Californium中Coap的实现进行了简要的介绍&a…