瑞吉外卖05-启用、禁用员工账号&&编辑员工信息
瑞吉外卖05-启用、禁用员工账号&&编辑员工信息
需求分析
1. 启用、禁用员工账号需求
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。如果某个员工账号状态为正常,则按钮显示为 "禁用",如果员工账号状态为已禁用,则按钮显示为"启用"。
需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示。
admin 管理员登录
普通用户登录
显然如上述描述的一样,admin 与 普通员工 在“操作”这一栏的界面是不一样的!!!
2. 编辑员工信息需求
在员工管理列表页面点击 "编辑" 按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击 "保存" 按钮完成编辑操作。
想一想我们在 “1” 里面写的 updateInfo() 方法,是不是可以直接用上?!
前端代码分析
1. 启用、禁用员工账号前端代码分析
页面按钮动态展示
在上述的需求中,我们提到需要实现的效果是 :
只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示 。
页面中是怎么做到只有管理员admin能够看到启用、禁用按钮的?
我们可以来分析一下前端的代码(resources\backend\page\member\list.html)
触发钩子函数created(),在钩子函数中,会从localStorage中获取到用户登录信息,然后获取到用户名 。
created() {
this.init()
this.user = JSON.parse(localStorage.getItem('userInfo')).username
},
通过Vue指令 v-if 进行判断,如果登录用户为admin将展示 启用/禁用 按钮,否则不展示
<el-button
type="text"
size="small"
class="delBut non"
@click="statusHandle(scope.row)"
v-if="user === 'admin'"
>
{{ scope.row.status == '1' ? '禁用' : '启用' }}
</el-button>j
执行流程分析
根据上面的代码分析,我们知道,当管理员admin点击 "启用" 或 "禁用" 按钮时,调用方法statusHandle()
@click="statusHandle(scope.row)"
statusHandle() 方法中进行二次确认,然后发起ajax请求,传递id、status参数(给后端)
function enableOrDisableEmployee (params) {
return $axios({
url: '/employee',
method: 'put',
data: { ...params }
})
}
最终发起异步请求,请求服务端
请求 | 说明 |
---|---|
请求方式 | PUT |
请求路径 | /employee |
请求参数 | {"id":xxx,"status":xxx} |
{...params} : 三点是ES6中出现的扩展运算符。作用是遍历当前使用的对象能够访问到的所有属性,并将属性放入当前对象中。
2. 编辑员工信息前端代码分析
(1)点击编辑按钮时,页面跳转到add.html,并在url中携带参数 [员工id]
<el-button
type="text"
size="small"
class="blueBug"
@click="addMemberHandle(scope.row.id)"
:class="{notAdmin:user !== 'admin'}"
>
编辑
</el-button>
点击编辑按钮,调用addMemberHandle()方法
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)
}
},
(2)在add.html页面获取 url 中的参数 [员工id]
(3)发送ajax请求,请求服务端,同时提交员工id参数
(4)服务端接收请求,根据员工id查询员工信息,将员工信息以json形式响应给页面
(5)页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显
(6)点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端
(7)服务端接收员工信息,并进行处理,完成后给页面响应
(8)页面接收到服务端响应信息后进行相应处理
请求 | 说明 |
---|---|
请求方式 | PUT |
请求路径 | /employee |
请求参数 | {.......} json格式数据 |
代码开发
1. 启用、禁用员工账号
我们要实现的功能点大致如下:
(1)页面发送ajax请求,将参数(id、status)提交到服务端
(2)服务端Controller接收页面提交的数据并调用Service更新数据
(3)Service调用Mapper操作数据库
Controller层
package com.harmony.reggie.controller.EmployeeController
/**
* 根据 ID 来修改员工信息
* @param employee
* @return
* @RequestBody主要用来接收前端传递给后端的json字符串中的数据的
*/
@PutMapping
public R<String> updateInfo(HttpServletRequest request,@RequestBody Employee employee) {
return employeeService.updateInfo(request, employee);
}
@RequestBody 主要用来接收前端传递给后端的JSON字符串中的数据的
Service层
package com.harmony.reggie.service.impl.EmployeeServiceImpl
@Override
public R<String> updateInfo(HttpServletRequest request, Employee employee) {
Long empId = (Long)request.getSession().getAttribute("employee");
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(empId);
employeeMapper.updateById(employee);
System.out.println(employee);
return R.success("员工信息修改成功!");
}
注意:
本来这个需要我们只要更改employees表的status属性就行了,但是我们为了代码有更好的复用性,所以这边采用的是传入一个employee实体类对象,更改employees表中指定 id 的字段。
这样以后只要有employee对象的任意属性字段的变更操作,调用这个方法就可以了。
“黑马”故意设计的脑残BUG
通过观察控制台输出的SQL发现页面传递过来的员工 id 的值和数据库中的 id 值不一致,导致状态修改失败。
在分页查询时,服务端会将返回的R对象进行JSON序列化,转换为JSON格式的数据。而员工的ID是一个Long类型的数据,而且是一个长度为 19 位的长整型数据, 该数据返回给前端是没有问题的。
问题就出现在前端JS中, js在对长度较长的长整型数据进行处理时, 会损失精度, 从而导致提交的id和数据库中的id不一致。
解决方案
由于在SpringMVC中,将Controller方法返回值转换为json对象,是通过jackson来实现的,涉及到SpringMVC中的一个消息转换器MappingJackson2HttpMessageConverter,所以我们要解决这个问题,就需要对该消息转换器的功能进行拓展。
(1)提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换
/**
* 对象映射器:基于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);
}
}
在进行JSON数据序列化及反序列化时,LocalDateTime、LocalDate、LocalTime的处理方式,以及BigInteger及Long类型数据,直接转换为字符串(解决办法)
(2)在WebMvcConfig配置类中扩展SpringMVC的消息转换器,在此消息转换器中使用提供的对象转换器进行Java对象到JSON数据的转换
@Configuration
public class WebMVCConfig extends WebMvcConfigurationSupport {
// 之前代码 ... 略
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(new JacksonObjectMapper());
converters.add(0, messageConverter);
}
}
2. 编辑员工信息
之前更新操作我们在“1”中已经写过了,所以这里只要编写一个查询的功能就可以了。
第8步里面,页面接收到服务端响应信息后进行相应处理,也就是说我们在后端要查询出一个employee对象,传给前端用form进行渲染。
/**
* 查询员工信息
* @param id
* @return
* @PathVariable id在请求路径里面
*/
@GetMapping("/{id}")
public R<Employee> getEmployeeById(@PathVariable Long id) {
Employee employee = employeeService.getById(id);
if (employee != null) {
return R.success(employee);
}
return R.error("没有查询出来!");
}