前提引入aop依赖完成(具体不介绍)
1. 实操 代码
1.1 创建自定义接口类 Loggable (记录登录日志)
1.1.1 自定义注解 Loggable
import java.lang.annotation.*;
/**
* @Author hw
* @Date 2024/4/7 9:22
* @PackageName:xxxxxxxxx
* @ClassName: Loggable
* @Description: 登录日志
* @Version 1.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Loggable {
/**
* 说明
*/
String operatorDetails() default "";
/**
* 操作
*/
String operation();
/**
* 操作位置
*/
String operationAddress();
/**
* 操作模块
*/
String operatorModule();
}
1.1.2 定义切面类
/**
* @Author hw
* @Date 2024/3/1 9:28
* @PackageName:xxxxxxxxx
* @ClassName: LoggableAspect
* @Description: 登录模块的aop
* @Version 1.0
*/
@Aspect
@Slf4j
@Component
public class LoggableAspect {
@Autowired
private EipSysLogLoginServiceImpl eipSysLogLoginService;
@Pointcut("@annotation(com.redxun.eip.annotation.Loggable)")
public void loggableAspect() {
}
@AfterReturning("execution(* *.*(..)) && @annotation(loggable)")
public void logAfterReturningMethod(JoinPoint joinPoint, Loggable loggable) throws Exception{
log.info("==========执行业务操作日志留痕===============");
EipSysLogLogin eipSysLogLogin = new EipSysLogLogin();
// 操作 操作位置 操作内容 操作模块
eipSysLogLogin.setOperator(loggable.operation());
eipSysLogLogin.setOperatorAddress(loggable.operationAddress());
eipSysLogLogin.setOperatorDetails(loggable.operatorDetails());
eipSysLogLogin.setOperatorModule(loggable.operatorModule());
eipSysLogLoginService.saveLoginLog(joinPoint,eipSysLogLogin);
}
}
1.1.3Service层写具体的代码
/**
* description:
* 保存登录日志操作
* @author: hw
* @since: 1.0.0
* @date: 2024/4/7 10:35
* @Param joinPoint:
* @Param eipSysLogLogin:
* @return: void
*/
public void saveLoginLog(JoinPoint joinPoint, EipSysLogLogin eipSysLogLogin) {
EipEhrEmp eipEhrEmp = (EipEhrEmp) redisTemplate.opsForHash().get(EipConstant.USER_PERMISSION, SecurityGetUserUtils.getUserByWorkId());
if (ObjectUtil.isNotEmpty(eipEhrEmp)) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 主键
eipSysLogLogin.setId(IdGenerator.getIdStr());
// 记录时间
eipSysLogLogin.setRecordTime(new Date());
// 操作人
eipSysLogLogin.setOperatorWorkId(eipEhrEmp.getWorkId());
eipSysLogLogin.setOperatorName(eipEhrEmp.getName());
// 创建时间、更新时间
eipSysLogLogin.setCreateTime(new Date());
eipSysLogLogin.setUpdateTime(new Date());
// 创建人 更新人
eipSysLogLogin.setCreateBy(eipEhrEmp.getWorkId());
eipSysLogLogin.setUpdateBy(eipEhrEmp.getWorkId());
eipSysLogLogin.setCreateByName(eipEhrEmp.getName());
eipSysLogLogin.setUpdateByName(eipEhrEmp.getName());
// 逻辑删除(1-有效 0-删除)
eipSysLogLogin.setDelete(1);
// ip地址
String ipAddr = IpUtil.getIpAddr(request);
eipSysLogLogin.setIp(ipAddr);
// MAC 地址
eipSysLogLogin.setMac(MacAddressUtils.getMacAddressByIp(ipAddr));
// 主机名
eipSysLogLogin.setHostname(request.getHeader("Host"));
// 登录地区 需要具体的api
eipSysLogLogin.setLoginAddress(request.getLocale().toString());
// 浏览器
eipSysLogLogin.setBrowser(BrowserUtils.getBrowser(request));
// 操作系统
eipSysLogLogin.setOperatorSystem(OperatingSystemUtils.getOperatingSystem(request));
// 创建部门,公司
eipSysLogLogin.setCreateDepId(eipEhrEmp.getDeptId());
eipSysLogLogin.setCompanyId(eipEhrEmp.getCompanyId());
// 获取当年的年份
LocalDate currentDate = LocalDate.now();
int currentYear = currentDate.getYear();
eipSysLogLoginMapper.saveEipSysLogLogin(eipSysLogLogin,currentYear);
}
}
1.2 创建自定义注解 OperationLog(操作日志)
1.2.1 自定义注解 OperationLog
package com.redxun.eip.annotation;
import java.lang.annotation.*;
/**
* @Author hw
* @Date 2024/4/7 9:22
* @PackageName:com.hkc.rdm.annotation
* @ClassName: Loggable
* @Description: 登录日志
* @Version 1.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface OperationLog {
/**
* 说明
*/
String operatorDetails() default "";
/**
* 操作
*/
String operation();
/**
* 操作位置
*/
String operationAddress();
/**
* 操作模块
*/
String operatorModule();
}
1.2.2 定义切面类
package com.redxun.eip.aspect;
import com.hkc.common.base.entity.IUser;
import com.hkc.common.tool.IdGenerator;
import com.hkc.common.utils.ContextUtil;
import com.redxun.eip.annotation.OperationLog;
import com.redxun.eip.controller.workbench3.entity.EipSysLogOperator;
import com.redxun.eip.controller.workbench3.service.EipSysLogOperatorServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Author hw
* @Date 2024/4/7 15:32
* @PackageName:com.hkc.rdm.aspect
* @ClassName: OperationAspect
* @Description: 操作行为的aop
* @Version 1.0
*/
@Aspect
@Slf4j
@Component
public class OperationAspect {
@Autowired
private EipSysLogOperatorServiceImpl eipSysLogOperatorService;
@Pointcut("@annotation(com.redxun.eip.annotation.OperationLog)")
public void operationAspect() {
}
@AfterReturning("execution(* *.*(..)) && @annotation(operationLog)")
public void logAfterReturningMethod(JoinPoint joinPoint, OperationLog operationLog) throws Exception{
log.info("==========执行业务操作日志留痕===============");
EipSysLogOperator eipSysLogOperator = new EipSysLogOperator();
// 操作 操作位置 操作内容 操作模块
eipSysLogOperator.setOperator(operationLog.operation());
eipSysLogOperator.setOperatorAddress(operationLog.operationAddress());
eipSysLogOperator.setOperatorDetails(operationLog.operatorDetails());
eipSysLogOperator.setOperatorModule(operationLog.operatorModule());
eipSysLogOperatorService.saveOperationLog(joinPoint,eipSysLogOperator);
}
}
1.2.3 Service层写具体的代码
package com.redxun.eip.controller.workbench3.service;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hkc.common.base.db.BaseDao;
import com.hkc.common.base.db.BaseService;
import com.hkc.common.base.entity.IUser;
import com.hkc.common.service.impl.SuperServiceImpl;
import com.hkc.common.tool.IdGenerator;
import com.hkc.common.utils.ContextUtil;
import com.redxun.eip.controller.workbench2.entity.EipEhrEmp;
import com.redxun.eip.controller.workbench2.enums.EipConstant;
import com.redxun.eip.controller.workbench3.entity.EipSysLogOperator;
import com.redxun.eip.controller.workbench3.mapper.EipSysLogOperatorMapper;
import com.redxun.eip.util.SecurityGetUserUtils;
import org.aspectj.lang.JoinPoint;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.Date;
/**
* [操作日志]业务服务类
*
* @author 0179733
*/
@Service
public class EipSysLogOperatorServiceImpl extends SuperServiceImpl<EipSysLogOperatorMapper, EipSysLogOperator> implements BaseService<EipSysLogOperator> {
@Resource
private EipSysLogOperatorMapper eipSysLogOperatorMapper;
@Resource
private RedisTemplate redisTemplate;
@Override
public BaseDao<EipSysLogOperator> getRepository() {
return eipSysLogOperatorMapper;
}
/**
* description:
* 保存操作日志
*
* @author: hw
* @since: 1.0.0
* @date: 2024/4/7 10:53
* @Param joinPoint:
* @Param eipSysLogOperator:
* @return: void
*/
public void saveOperationLog(JoinPoint joinPoint, EipSysLogOperator eipSysLogOperator) {
EipEhrEmp eipEhrEmp = (EipEhrEmp) redisTemplate.opsForHash().get(EipConstant.USER_PERMISSION, SecurityGetUserUtils.getUserByWorkId());
if (ObjectUtil.isNotEmpty(eipEhrEmp)) {
// 主键
eipSysLogOperator.setId(IdGenerator.getIdStr());
// 记录时间
eipSysLogOperator.setRecordTime(new Date());
// 操作人
eipSysLogOperator.setOperatorWorkId(eipEhrEmp.getWorkId());
eipSysLogOperator.setOperatorName(eipEhrEmp.getName());
// 创建时间、更新时间
eipSysLogOperator.setCreateTime(new Date());
eipSysLogOperator.setUpdateTime(new Date());
// 创建人 更新人
eipSysLogOperator.setCreateBy(eipEhrEmp.getWorkId());
eipSysLogOperator.setUpdateBy(eipEhrEmp.getWorkId());
eipSysLogOperator.setCreateByName(eipEhrEmp.getName());
eipSysLogOperator.setUpdateByName(eipEhrEmp.getName());
// 逻辑删除(1-有效 0-删除)
eipSysLogOperator.setDelete(1);
// 创建部门,公司
eipSysLogOperator.setCreateDepId(eipEhrEmp.getDeptId());
eipSysLogOperator.setCompanyId(eipEhrEmp.getCompanyId());
// 获取当年的年份
LocalDate currentDate = LocalDate.now();
int currentYear = currentDate.getYear();
eipSysLogOperatorMapper.saveEipSysLogOperator(eipSysLogOperator, currentYear);
}
}
}
2. 数据库表 定时任务生成
package com.redxun.eip.job;
import com.redxun.eip.controller.workbench3.service.EipServiceDataSourceImpl;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author hw
* @Date 2024/4/7 9:53
* @PackageName:com.hkc.rdm.job
* @ClassName: SysLogSpreadSheetJob
* @Description:
* 每年的 12月份 创建明年的登录日志和操作日志表
* w_eip_sys_log_login_xxxx => w_eip_sys_log_login_2025
* w_eip_sys_log_operator_xxxx => w_eip_sys_log_operator_2025
* @Version 1.0
*/
@Slf4j
@Component
public class SysLogSpreadSheetJob extends IJobHandler {
@Autowired
private EipServiceDataSourceImpl eipServiceDataSource;
@XxlJob("SysLogSpreadSheetJob")
@Override
public void execute() throws Exception {
log.info("================ 每年的12月份创建明年的登录日志和操作日志表 w_eip_sys_log_operator_xxxx ==================");
eipServiceDataSource.createSpreadSheet();
eipServiceDataSource.createLogLoginSheet();
}
}
package com.redxun.eip.controller.workbench3.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Calendar;
/**
* @Author hw
* @Date 2024/3/21 10:10
* @PackageName:com.hkc.rdm.service.impl
* @ClassName: RdmServiceDataSource
* @Description: 数据库连接对象
* @Version 1.0
*/
@Slf4j
@Service
public class EipServiceDataSourceImpl {
private final DataSource dataSource;
@Autowired
public EipServiceDataSourceImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* description: 每年的12月份创建明年的操作日志表 w_eip_sys_log_operator_xxxx => w_eip_sys_log_operator_2025
* 1、创建分表
*
* @author: hw
* @since: 1.0.0
* @date: 2024/4/7 9:58
* @return: void
*/
public void createSpreadSheet() {
// 获取当前日期,并计算下一年的年份
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 1);
int nextYear = calendar.get(Calendar.YEAR);
// 创建表的SQL语句
String createTableSQL = "CREATE TABLE IF NOT EXISTS w_eip_sys_log_operator_"+nextYear+" (" +
"ID_ VARCHAR(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键'," +
"OPERATOR_WORK_ID_ VARCHAR(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作工号'," +
"OPERATOR_NAME_ VARCHAR(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作名称'," +
"OPERATOR_ VARCHAR(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作'," +
"OPERATOR_ADDRESS_ VARCHAR(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作位置'," +
"OPERATOR_MODULE_ VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作模块'," +
"OPERATOR_DETAILS_ VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作内容'," +
"RECORD_TIME_ DATETIME NOT NULL COMMENT '记录时间'," +
"OPERATOR_HOURS_ VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作时长(ms)'," +
"DELETE_ INT DEFAULT NULL COMMENT '逻辑删除(0-有效 1-删除)'," +
"REF_ID_ VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '外键'," +
"PARENT_ID_ VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '父ID'," +
"UPDATE_VERSION_ DECIMAL(14,0) DEFAULT '1' COMMENT '版本号'," +
"INST_ID_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '流程实例ID'," +
"INST_STATUS_ VARCHAR(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '状态'," +
"TENANT_ID_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '租户ID'," +
"CREATE_TIME_ DATETIME DEFAULT NULL COMMENT '创建时间'," +
"UPDATE_TIME_ DATETIME DEFAULT NULL COMMENT '更新时间'," +
"CREATE_BY_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人ID'," +
"UPDATE_BY_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人ID'," +
"CREATE_DEP_ID_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '组ID'," +
"COMPANY_ID_ VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公司ID'," +
"CREATE_BY_NAME VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称'," +
"UPDATE_BY_NAME VARCHAR(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人名称'," +
"PRIMARY KEY (ID_,RECORD_TIME_)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='操作日志'" +
"PARTITION BY RANGE ( MONTH(RECORD_TIME_) ) (" +
"PARTITION p0 VALUES LESS THAN (2)," +
"PARTITION p1 VALUES LESS THAN (3)," +
"PARTITION p2 VALUES LESS THAN (4)," +
"PARTITION p3 VALUES LESS THAN (5)," +
"PARTITION p4 VALUES LESS THAN (6)," +
"PARTITION p5 VALUES LESS THAN (7)," +
"PARTITION p6 VALUES LESS THAN (8)," +
"PARTITION p7 VALUES LESS THAN (9)," +
"PARTITION p8 VALUES LESS THAN (10)," +
"PARTITION p9 VALUES LESS THAN (11)," +
"PARTITION p10 VALUES LESS THAN (12)," +
"PARTITION p11 VALUES LESS THAN MAXVALUE" +
")";
try (Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(createTableSQL)) {
// 执行创建SQL语句
preparedStatement.executeUpdate();
log.info("======================================================= 创建表成功");
} catch (SQLException e) {
e.printStackTrace();
log.error("======================================= 创建表失败:" + e.getMessage());
}
}
/**
* description:
* 每年的12月份创建明年的操作日志表 w_eip_sys_log_login_xxxx => w_eip_sys_log_login_2025
* @author: hw
* @since: 1.0.0
* @date: 2024/4/7 10:33
* @Param null:
* @return: null
*/
public void createLogLoginSheet() {
// 获取当前日期,并计算下一年的年份
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 1);
int nextYear = calendar.get(Calendar.YEAR);
// 创建表的SQL语句
String createTableSQL = "CREATE TABLE IF NOT EXISTS w_eip_sys_log_login_"+nextYear+" (" +
" ID_ varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键'," +
" OPERATOR_WORK_ID_ varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作工号'," +
" OPERATOR_NAME_ varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作名称'," +
" IP_ varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'IP地址'," +
" MAC_ varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'MAC地址'," +
" HOSTNAME_ varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主机名'," +
" LOGIN_ADDRESS_ varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '登陆地区'," +
" BROWSER_ varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '浏览器'," +
" OPERATOR_SYSTEM_ varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作系统'," +
" RECORD_TIME_ datetime NOT NULL COMMENT '记录时间'," +
" OPERATOR_ varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作'," +
" OPERATOR_ADDRESS_ varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作位置'," +
" OPERATOR_MODULE_ VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作模块'," +
" OPERATOR_DETAILS_ varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作内容'," +
" OPERATOR_HOURS_ varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作时长(ms)'," +
" DELETE_ int DEFAULT '0' COMMENT '逻辑删除(0-有效 1-删除)'," +
" REF_ID_ varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '外键'," +
" PARENT_ID_ varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '父ID'," +
" UPDATE_VERSION_ decimal(14,0) DEFAULT '1' COMMENT '版本号'," +
" INST_ID_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '流程实例ID'," +
" INST_STATUS_ varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '状态'," +
" TENANT_ID_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '租户ID'," +
" CREATE_TIME_ datetime DEFAULT NULL COMMENT '创建时间'," +
" UPDATE_TIME_ datetime DEFAULT NULL COMMENT '更新时间'," +
" CREATE_BY_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人ID'," +
" UPDATE_BY_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人ID'," +
" CREATE_DEP_ID_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '组ID'," +
" COMPANY_ID_ varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公司ID'," +
" CREATE_BY_NAME varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称'," +
" UPDATE_BY_NAME varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人名称'," +
" PRIMARY KEY (ID_,RECORD_TIME_)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='登录日志'" +
"PARTITION BY RANGE ( MONTH(RECORD_TIME_) ) (" +
"PARTITION p0 VALUES LESS THAN (2)," +
"PARTITION p1 VALUES LESS THAN (3)," +
"PARTITION p2 VALUES LESS THAN (4)," +
"PARTITION p3 VALUES LESS THAN (5)," +
"PARTITION p4 VALUES LESS THAN (6)," +
"PARTITION p5 VALUES LESS THAN (7)," +
"PARTITION p6 VALUES LESS THAN (8)," +
"PARTITION p7 VALUES LESS THAN (9)," +
"PARTITION p8 VALUES LESS THAN (10)," +
"PARTITION p9 VALUES LESS THAN (11)," +
"PARTITION p10 VALUES LESS THAN (12)," +
"PARTITION p11 VALUES LESS THAN MAXVALUE" +
")";
try (Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(createTableSQL)) {
// 执行创建SQL语句
preparedStatement.executeUpdate();
log.info("======================================================= 创建表成功");
} catch (SQLException e) {
e.printStackTrace();
log.error("======================================= 创建表失败:" + e.getMessage());
}
}
}
3. 实际运用
在controller层 加自定义注解