添加员工信息
执行流程
第一步: 用户点击添加员工按钮
跳转到add.html
页面,然后在页面中输入要添加的员工的信息
第二步: 用户点击保存按钮
发送Ajax请求将用户输入的员工信息以json的格式提交到服务端
第三步: 服务端Controller接收页面提交的json格式的数据并转化为java对象, 通过Service调用Mapper将员工信息插入到employee表
中,最后向前端返回通用结果类
id
: 由雪花算法/自动递增机制自动生成username
: 设置唯一性约束, 员工的的登录账户必须是唯一的password
: 默认指定一个密码(身份证后六位,123456等),但是这个密码不能直接在数据库中设为默认值,因为数据库设置的默认值无法加密status
: 设定员工的状态,默认值1表示启用(正常),0表示禁用,直接在数据库中设置默认值不需要加密createTime/updateTime
: 创建/更新员工信息的时间,这个就指定当前时间就好了createUser/updateUser
: 创建/更新员工信息的管理员ID,依靠这个可以溯源避免出现莫名的员工账号
前端页面
add.html
为公共页面,新增员工和编辑员工都是在此页面操作,只不过添加员工时还会显示保存并继续添加按钮
<script>
// add.html页面提交的数据模型
ruleForm : {
'name': '',
'phone': '',
'sex': '男',
'idNumber': '',
username: ''
}
</script>
<el-form
ref="ruleForm"
:model="ruleForm"
:rules="rules"
:inline="false"
label-width="180px"
class="demo-ruleForm"
>
<el-form-item label="账号:" prop="username">
<el-input v-model="ruleForm.username" placeholder="请输入账号" maxlength="20"/>
</el-form-item>
<el-form-item
label="员工姓名:"
prop="name"
>
<el-input
v-model="ruleForm.name"
placeholder="请输入员工姓名"
maxlength="20"
/>
</el-form-item>
<el-form-item
label="手机号:"
prop="phone"
>
<el-input
v-model="ruleForm.phone"
placeholder="请输入手机号"
maxlength="20"
/>
</el-form-item>
<el-form-item
label="性别:"
prop="sex"
>
<el-radio-group v-model="ruleForm.sex">
<el-radio label="男"></el-radio>
<el-radio label="女"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="身份证号:"
prop="idNumber"
>
<el-input
v-model="ruleForm.idNumber"
placeholder="请输入身份证号"
maxlength="20"
/>
</el-form-item>
<div class="subBox address">
<el-form-item>
<el-button @click="goBack()">
取消
</el-button>
<el-button
type="primary"
<!--发送ajax请求将用户输入的员工信息以json的格式提交到服务端-->
@click="submitForm('ruleForm', false)"
>
保存
</el-button>
<el-button
<!--如果是添加员工显示该按钮,如果是修改员工则不显示该按钮-->
v-if="actionType == 'add'"
type="primary"
class="continue"
@click="submitForm('ruleForm', true)"
>
保存并继续添加
</el-button>
</el-form-item>
</div>
</el-form>
submitForm函数
调用addEmployee
函数发送Ajax请求将用户输入的员工信息以json的格式提交到服务端,最后接收服务器响应的通用结果类执行Ajax的回调函数
submitForm (formName, st) {
this.$refs[formName].validate((valid) => {// 对表单的数据进行校验
if (valid) {
// 判断是添加还是修改
if (this.actionType === 'add') {
const params = {
...this.ruleForm,
sex: this.ruleForm.sex === '女' ? '0' : '1'// 将表单中的男女转换为数据库中对应的0或1
}
addEmployee(params).then(res => {// 接收服务器Controller响应的结果
if (res.code === 1) {
this.$message.success('员工添加成功!')
if (!st) {
this.goBack()
} else {// 员工添加失败
this.ruleForm = {
username: '',
'name': '',
'phone': '',
// 'password': '',
// 'rePassword': '',/
'sex': '男',
'idNumber': ''
}
}
} else {
this.$message.error(res.msg || '操作失败')
}
}).catch(err => {
this.$message.error('请求出错了:' + err)
})
} else {
// 修改员工信息......
}
} else {
console.log('error submit!!')
return false
}
})
}
// 发起ajax请求新增添加员工
function addEmployee (params) {
return $axios({
url: '/employee',
method: 'post',
data: { ...params }
})
}
后端处理请求
处理请求路径为/employee
的POST请求将添加的员工信息保存到数据库
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping
public Result<String> save(HttpServletRequest request, @RequestBody Employee employee) {
// 接收的employee对象的id,password,status,createTime属性都还是null
log.info("新增的员工信息:{}", employee.toString());
// 设置默认密码为123456,并采用MD5加密
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
// 设置createTime和updateTime
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
// 在session中获取创建人的id设置CreateUser和UpdateUser
Long empId = (Long) request.getSession().getAttribute("employee");
employee.setCreateUser(empId);
employee.setUpdateUser(empId);
// 将employee对象存入数据库
employeeService.save(employee);
// 响应通用的结果封装类
return Result.success("添加员工成功");
}
}
全局异常处理器
添加员工时存入相同的username(唯一)控制台会报java.sql.SQLIntegrityConstraintViolationException:Duplicate entry...
,有两种方式捕获
- 在Controller方法中对
employeeService.save(employee)
使用try...catch
进行异常捕获,这种方式只能捕获当前代码发生的异常 - 使用
全局异常处理器
可以捕获整个项目中指定类型的异常
创建一个全局异常处理类common/GlobalExceptionHandler
,添加exceptionHandler
方法用来捕获异常,并返回通用错误信息如提示用户名xxx已存在
@Slf4j
// 只有加了@Controller注解的类发生的异常才会捕获
@ControllerAdvice(annotations = {RestController.class,Controller.class})
@ResponseBody
public class GlobalExceptionHandler {
// 拦截SQLIntegrityConstraintViolationException异常
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException exception) {
// 控制台日志输出的异常信息为Duplicate entry 'zhangsan' for key 'employee.idx_username'
log.error(exception.getMessage());
// 如果异常信息包含Duplicate entry,则说明有条目重复
if (exception.getMessage().contains("Duplicate entry")) {
// 将字符串按照空格分割得到username(字符串格式是固定的)
String[] split = exception.getMessage().split(" ");
String username = split[2];
// 拼串作为错误信息返回
return Result.error("用户名" + username + "已存在");
}
// 其他错误信息
return Result.error("未知错误");
}
}
员工信息分页查询
执行流程
第一步: 在加载index.html
页面时自动调用list.html
中的creatued()函数
发送ajax请求,将分页查询需要的参数page,pageSize,name
提交到服务端
- 如果当前就在index.html页面中可以
通过指定查询条件发起Ajax请求查询分页数据
, 请求参数除了携带page,pageSize外
还携带name表示查询条件
- 如果用户点击页数上下翻页的时候也会发起Ajax请求提交
page,pageSize请求参数
查询员工分页数据
第二步: 服务端Controller接收页面提交的参数,通过Service调用Mapper操作数据库查询分页数据,然后将查询到的分页数据响应给页面
第三步: 页面接收到分页数据并通过ElementUI的Table组件
展示到页面上
前端页面
list.html页面
携带分页查询需要的参数page,pageSize,name
发起Ajax请求,然后将服务端的Controller响应的所有员工数据展示到页面
created() {
this.init()
this.user = JSON.parse(localStorage.getItem('userInfo')).username
},
async init () {
// json格式的请求参数
const params = {
page: this.page,
pageSize: this.pageSize,
name: this.input ? this.input : undefined
}
await getMemberList(params).then(res => {// 执行回调函数接收服务端Controller响应的所有员工数据
if (String(res.code) === '1') {
this.tableData = res.data.records || []
this.counts = res.data.total
}
}).catch(err => {
this.$message.error('请求出错了:' + err)
})
function getMemberList (params) {
return $axios({
url: '/employee/page',
method: 'get',
params
})
}
request全局拦截器
拦截get请求将json格式
的请求参数转化为name=value
的格式拼接到URL上
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
}, error => {
console.log(error)
Promise.reject(error)
})
在list.html
页面中展示数据的时候,将后端响应的Integer类型的status
转化为已禁用或者正常
的字符串
<el-table-column label="账号状态">
<template slot-scope="scope">
{{ String(scope.row.status) === '0' ? '已禁用' : '正常' }}
</template>
</el-table-column>
后端处理请求
第一步: 在config/MybatisPlusConfig
中添加MyBatisPlus的分页插件对象
并交给IoC容器管理
@Configuration
//可以将主类中的扫描Mapper接口的注解移到此处
@MapperScan("com.atguigu.mybatisplus.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 配置MyBatis Plus中插件的对象
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 在插件对象中添加内部插件并设置数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
第二步: 查询所有的员工信息并进行分页,Page对象
中封装了查询到的所有员工信息(records属性
)和其他分页信息如total,size属性
@GetMapping("/page")
public Result<Page> page(int page, int pageSize, String name) {
log.info("page={},pageSize={},name={}", page, pageSize, name);
// 在分页对象中设置分页参数,当前页码数和每页显示的记录数
Page<Employee> pageInfo = new Page<>(page, pageSize);
// 构造条件构造器
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
// 添加过滤条件,当我们没有输入name查询条件时表示查询所有
wrapper.like(!(name == null || "".equals(name)), Employee::getName, name);
// wrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
// 添加排序条件,根据更新时间对查询的结果进行降序排序
wrapper.orderByDesc(Employee::getUpdateTime);
// 添加分页对象和条件构造器查询所有,最终数据都会被封装到分页对象中
employeeService.page(pageInfo, wrapper);
// 将Page对象封装到Result对象的data属性中,Page对象的records属性中封装了查询到的员工信息
return Result.success(pageInfo);
}
启用/禁用员工账号
执行流程
第一步: 用户点击启用/禁用
按钮时页面发送Ajax的PUT请求
将参数元素所在行的id和status
提交到服务端
第二步: 服务端Controller接收页面提交的数据通过Service调用Mapper操作数据库更新对应id员工的status的值
前端页面
只有管理员(admin)可以对其他所有员工账号进行启用、禁用操作
, 普通用户登录系统后启用、禁用按钮不显示
- 如果某个员工账号状态为正常则按钮显示为
禁用
,如果员工账号状态为已禁用则按钮显示为启用
当加载完list.html页面时获取一下当前登录账号的username并判断是不是admin
,如果是就显示启用/禁用按钮否则不显示
<script>
created() {
this.init()
this.user = JSON.parse(localStorage.getItem('userInfo')).username
}
</script>
<el-button
type="text"
size="small"
class="delBut non"
@click="statusHandle(scope.row)"
v-if="user === 'admin'"
>
{{ scope.row.status == '1' ? '禁用' : '启用' }}
</el-button>
用户点完启用禁用按钮
后调用statusHandle函数
弹出提示窗口,点击确定之后调用enableOrDisableEmployee函数
携带当前行的id值与!status
发起PUT请求
statusHandle (row) {
// row表示每行数据对应的json对象
this.id = row.id
this.status = row.status
this.$confirm('确认调整该账号的状态?', '提示', {
'confirmButtonText': '确定',
'cancelButtonText': '取消',
'type': 'warning'
}).then(() => {
// 对当前状态进行取反操作实现切换禁用/启用状态,如果this.status为1则status为0,如果this.status为0则status为1
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)
})
})
}
// 修改启用/禁用接口
function enableOrDisableEmployee (params) {
return $axios({
url: '/employee',
method: 'put',
data: { ...params }
})
}
配置状态转换器
执行完分页查询请求服务器响应给页面的Employee对象的id是正确的,但JS将id渲染到页面上时会丢失精度
,这样获取的页面id就和数据库中id有差异
JS对Long型数据进行处理时会丢失精度导致提交的id和数据库中的id不一致
, 需要服务端在给页面响应json数据时将Long型数据统一转为String字符串
对象转换器
:基于jackson
将Java对象转为json[序列化Java对象到JSON],或者将json转为Java对象[从JSON反序列化Java对象]
第一步: 创建commom/JacksonObjectMapper
继承ObjectMapper
,不需要向工程中导入依赖
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)
// 将Long类型转化为String类型
.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);
}
}
第二步: 扩展Mvc框架的消息转换器
,在此消息转换器中使用自己的对象转换器进行java数据到json对象的转换
@Configuration
@Slf4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
// 设置自己的对象转化器,底层使用jackson将java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
// 将自己的消息转换器对象追加到mvc框架的转换器集合当中(index设置为0,表示设置在第一个位置,避免被其它转换器接收,从而达不到想要的功能)
converters.add(0, messageConverter);
}
}
后端处理请求
更新员工记录的status状态
字段的值,该方法也可以修改员工信息的其他字段
@PutMapping
public Result<String> update(@RequestBody Employee employee, HttpServletRequest request) {
log.info(employee.toString());
Long id = (Long) request.getSession().getAttribute("employee");
employee.setUpdateUser(id);
employee.setUpdateTime(LocalDateTime.now());
employeeService.updateById(employee);
return Result.success("员工信息修改成功");
}
编辑员工信息
执行流程
第一步用户点击编辑按钮
跳转到add.html
公共页面中并在URL中携带编辑员工的id
作为请求参数,如果URL中没有携带id表示添加操作
<el-button
type="text"
size="small"
class="blueBug"
@click="addMemberHandle(scope.row.id)"
:class="{notAdmin:user !== 'admin'}"
>
编辑
</el-button>
<script>
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)
}
}
</script>
第二步: 在add.html
页面中获取URL中携带的员工id,如果获取得到id发起修改员工的Ajax请求,如果获取不到发起添加员工的Ajax请求
created() {
this.id = requestUrlParam('id')
// id有值表示修改,没有值表示添加
this.actionType = this.id ? 'edit' : 'add'
if (this.id) {
this.init()
}
}
// 获取url地址上面的参数
function requestUrlParam(argname){
var url = location.href
var arrStr = url.substring(url.indexOf("?")+1).split("&")
for(var i =0;i<arrStr.length;i++){
// 查找"id="出现的索引
var loc = arrStr[i].indexOf(argname+"=")
if(loc!=-1){
return arrStr[i].replace(argname+"=","").replace("?","")
}
}
return ""
}
员工信息回显
第一步: 根据编辑员工的id查询员工信息然后以json形式响应给页面
@GetMapping("/{id}")// 使用占位符接收员工的id
public Result<Employee> getById(@PathVariable Long id){
log.info("根据id查询员工信息..");
Employee employee = employeeService.getById(id);
return Result.success(employee);
}
第二步: 在回调函数中接收服务端响应的json数据,并通过Vue的双向绑定功能进行员工信息回显
- 前端接收到服务端响应的json数据之后先判断状态码是否为1,如果是1则说明操作成功,然后将获取到的数据渲染到表单中从而达到回显数据的效果
// created钩子函数中调用的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 || '操作失败')
}
})
}
更新员工信息
第一步: 用户点击保存按钮
发送Ajax请求将页面中修改后的员工信息以json形式提交给服务端
<el-button
type="primary"
@click="submitForm('ruleForm', false)"
>
保存
</el-button>
<script>
submitForm (formName, st) {
this.$refs[formName].validate((valid) => {
if (valid) {
// 判断是添加还是修改
if (this.actionType === 'add') {
// 添加员工信息的接口
addEmployee(params).then(res => {// 接收服务器响应的结果执行回调函数
} else {
const params = {
...this.ruleForm,
sex: this.ruleForm.sex === '女' ? '0' : '1'
}
// 修改员工信息
editEmployee(params).then(res => {
if (res.code === 1) {
this.$message.success('员工信息修改成功!')
this.goBack()
} else {
this.$message.error(res.msg || '操作失败')
}
}).catch(err => {
this.$message.error('请求出错了:' + err)
})
}
} else {
console.log('error submit!!')
return false
}
})
}
// 修改员工的接口
function editEmployee (params) {
return $axios({
url: '/employee',
method: 'put',
data: { ...params }
})
}
</script>
第二步: 服务端将修改后的员工信息
保存到数据库,然后响应成功的结果状态信息
@PutMapping
public Result<String> update(@RequestBody Employee employee, HttpServletRequest request) {
log.info(employee.toString());
Long id = (Long) request.getSession().getAttribute("employee");
employee.setUpdateUser(id);
employee.setUpdateTime(LocalDateTime.now());
employeeService.updateById(employee);
return Result.success("员工信息修改成功");
}
第三步: 页面Ajax请求的回调函数接收到服务端响应的信息后进行相应处理,调用goBack函数
跳转至员工管理页面
goBack(){
window.parent.menuHandle({
id: '2',
url: '/backend/page/member/list.html',
name: '员工管理'
},false)
}
填充公共字段
添加/修改员工
数据的时候,都需要指定一下创建人,创建时间,修改人,修改时间
等公共字段,在员工表,菜品表,分类表
等其他表中都拥有的字段
使用MybatisPlus
提供的自动填充功能
将这些公共字段在一个地方统一管理,在插入或者更新的时候为指定字段赋予指定的值
实现步骤
第一步: 在实体类的需要自动填充的属性上方加入@TableFiled注解
并指定自动填充的策略
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;//身份证号码
private Integer status;
// 插入时填充字段
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 插入和更新时填充字段
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
第二步: 按照框架要求编写元数据对象处理器common/MyMetaObjectHandle
在此类中实现插入和更新的方法
并统一对公共字段赋值
- 在MyMetaObjectHandler类中不能获得
HttpSession
对象,所以我们需要使用ThreadLocal
存储当前登录用户的Id
方法名 | 功能 |
---|---|
metaObject(元数据) | 封装了需要执行更新和插入的实体类对象,它提供了setValue方法用来完成字段的填充 |
insertFill(MetaObject metaObject) | 在执行插入语句时对指定的公共字段进行填充 |
updateFill(MetaObject metaObject) | 在执行更新语句时对指定的公共字段进行填充 |
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充(insert)...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充(update)...");
log.info(metaObject.toString());
metaObject.setValue("updateTime", LocalDateTime.now());
}
}
ThreadLocal
对于客户端每次发送的Http请求, 服务端都会创建一个对应的线程来处理
- 在一次请求过程中
LocalCheekFilter中的doFilter-->EmployeeController中的update-->MyMetaObjectHandler中的updateFill
处于同一个线程当中
ThreadLocal
并不是一个Thread(线程)而是Thread的局部变量,主要为每个使用该变量的线程提供独立的变量副本
- 使用ThreadLocal维护变量时每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
- ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问
方法名 | 功能 |
---|---|
public void set(T value) | 设置当前线程对应的线程局部变量的值 |
public T get() | 返回当前线程所对应的线程局部变量的值 |
第一步: 新建一个基于ThreadLocal的工具类common/BaseContext
,用于存储和获取当前登录用户的Id
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
第二步: 在LoginCheckFilter
类中使用request.getSession
获取当前登录用户的Id,然后使用BaseContext工具类
将Id值存到ThreadLocal当中,key是当前线程
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 判断用户的登录状态,如果已登录,则直接放行
if (request.getSession().getAttribute("employee") != null) {
log.info("用户已登录,id为{}", request.getSession().getAttribute("employee"));
// 在这里获取一下当前线程的id
long id = Thread.currentThread().getId();
log.info("doFilter的线程id为:{}", id);
// 从session中获取之前我们存的用户Id
Long empId = (Long) request.getSession().getAttribute("employee");
// 使用BaseContext工具类将用户的Id存到ThreadLocal当中,key是当前线程
BaseContext.setCurrentId(empId);
filterChain.doFilter(request, response);
return;
}
}
第三步: 在MyMetaObjectHandler类中使用BaseContext工具类从当前线程中获取对应登录用户的Id,然后填充到对应表中的字段
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入操作自动填充
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段填充(create)...");
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
// 获取当前线程对应的用户Id,然后设置创建人Id
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
// 更新操作自动填充
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段填充(insert)...");
metaObject.setValue("updateTime", LocalDateTime.now());
// 设置更新人id
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}