文章目录
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方法进行查询显示。
- 将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 ""
}