方案选择
针对记录用户操作日志,面向切面编程无疑是最好的解决方法:
创建日志注解类;
创建方法所属模块枚举、接口操作类型枚举、相关常量枚举;
创建日志切面 ;
创建日志写入文件工具类 ;
创建日志注解类
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogTail {
/**
* 方法名称
*/
String name();
/**
* 所属模块
*/
FunctionModuleEnum module();
/**
* 方法的操作类型
*/
OperateTypeEnum operateType();
}
创建方法所属模块枚举
根据项目中的模块创建所属模块
/**
* 方法所属模块
*/
public enum FunctionModuleEnum {
STATISTICS("统计分析"),
DATA_MANAGEMENT("数据管理"),
SYSTEM("系统配置")
;
private String name;
FunctionModuleEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
创建接口操作类型枚举
根据甲方文档要求创建接口操作类型枚举
/**
* 接口操作类型
*/
public enum OperateTypeEnum {
LOG_IN(0, "登录"),
QUERY(1, "查询"),
ADD(2, "新增"),
UPDATE(3, "更新"),
DELETE(4, "删除"),
LOG_OUT(5, "退出"),
;
private Integer code;
private String title;
OperateTypeEnum(Integer code, String title) {
this.code = code;
this.title = title;
}
public Integer getCode() {
return code;
}
public String getTitle() {
return title;
}
public static OperateTypeEnum getByCode(Integer code) {
for (OperateTypeEnum value : values()) {
if (value.code.equals(code)) {
return value;
}
}
return null;
}
}
创建相关常量枚举
/**
* 相关常量
*/
public class Constants {
/**
* 分隔符
*/
public static final String SEPARATOR = "|";
/**
* 换行符
*/
public static final String LINE = "\n";
}
创建日志切面
/**
* 日志切面
*/
@Aspect
@Component
public class LogTailAspect {
private static final Logger LOG = LoggerFactory.getLogger("operate");
private static Field[] fields;
@Resource
private LogRecordService logRecordService;
@Value("${product.id:310000000000}")
private String productId;
@Value("${product.name:机场打击整治非法客运管理}")
private String productName;
@Pointcut("execution(* com.shxp.project.*.controller..*.*(..))")
public void excudeService() {
}
static {
Class<LogBean> clazz = LogBean.class;
fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
}
}
@Around("@annotation(com.shxp.project.sys.annotation.LogTail)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Date start = new Date();
Object object = joinPoint.proceed();
Date end = new Date();
execute(joinPoint, object, start, end);
return object;
}
private void execute(ProceedingJoinPoint joinPoint, Object result, Date start, Date end) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
MethodSignature methodSignature = Optional.of(joinPoint)
.map(ProceedingJoinPoint::getSignature)
.map(e -> (MethodSignature) e)
.orElse(null);
LogTail logTail = Optional.of(methodSignature)
.map(MethodSignature::getMethod)
.map(e -> e.getAnnotation(LogTail.class))
.orElse(null);
Method method = methodSignature.getMethod();
Annotation[][] annotations = method.getParameterAnnotations();
Class[] classes = methodSignature.getParameterTypes();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
LogBean logBean = new LogBean();
/**
* 获取用户信息
*/
UserMessage user = getUser(names, args);
if (Objects.nonNull(user)) {
logBean.setOperatorID(user.getUser_id()+"");
logBean.setOperatorAccount(user.getUser_id()+"");
logBean.setOperatorName(user.getUser_name());
logBean.setOrganizationID(user.getOrg_code());
logBean.setOrganizationName(user.getFull_name());
}else{
ShiroUserEntity currentUser = UserUtil.getCurrentUser();
assert currentUser != null;
logBean.setOperatorID(currentUser.getId()+"");
logBean.setOperatorAccount(currentUser.getUserAccount()+"");
logBean.setOperatorName(currentUser.getName());
DeptEnum deptEnum = DeptEnum.getByCode(currentUser.getDept());
if (deptEnum!=null){
logBean.setOrganizationID(deptEnum.getOrg());
logBean.setOrganizationName(deptEnum.getTitle());
}
}
logBean.setProductID(productId);
logBean.setProductName(productName);
logBean.setLogID(UUID.randomUUID().toString());
logBean.setOperatorIdentity(1);
logBean.setOpTime(DateUtils.format(start, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setResTime(DateUtils.format(end, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setTerminalType(0);
logBean.setOpType(logTail.operateType().getCode());
logBean.setClientIp(request.getRequestURL().toString());
logBean.setClientPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
logBean.setURL(request.getRequestURI());
// logBean.setObjectParams();
logBean.setSessionID(request.getRequestedSessionId());
logBean.setFuncModuleName(logTail.module().getName()+"-"+logTail.name());
logBean.setObjectIP(request.getRequestURL().toString());
logBean.setObjectPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
// logBean.setOperateCondition();
logBean.setOperateCondition(getRequestParam(joinPoint));
// logBean.setTerminalID();
// logBean.setTerminalMac();
// logBean.setQuerySql();
String log = Stream.of(fields).map(e -> {
try {
return e.get(logBean);
} catch (IllegalAccessException ex) {
return "";
}
}).map(Util::getString).collect(Collectors.joining(Constants.SEPARATOR));
LogUtils.insertLog(log);
logRecordService.insertLog(logBean);
}
/**
* 从参数列表中获取userId参数,返回用户信息
*
* @param names
* @param args
* @return
*/
private UserMessage getUser(String[] names, Object[] args) {
UserMessage user = null;
for (int i = 0; i < names.length; i++) {
if ("userId".equals(names[i]) && args.length >= i+1) {
String userId = StringTools.getStringValue(args[i]);
List<UserMessage> userMessages = KoalClient.queryUserByIdcard(userId);
if (!CollectionUtils.isEmpty(userMessages)){
user = userMessages.get(0);
}
}
}
return user;
}
private String getRequestParam(JoinPoint pjp){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
if (HttpMethod.PUT.name().equals(request.getMethod()) || HttpMethod.POST.name().equals(request.getMethod())){
return getBodyContent(pjp);
} else {
Map<String,Object> map = getParamContent(request.getParameterMap());
return JSON.toJSONString(map);
}
}
private Map<String,Object> getParamContent(Map<String, String[]> params) {
Map<String,Object> resultMap = new HashMap<>();
try {
for (Map.Entry<String, String[]> entry : params.entrySet()) {
Object value = new Object();
if (entry.getValue() != null && entry.getValue().length == 1) {
value = entry.getValue()[0];
} else {
value = entry.getValue();
}
resultMap.put(entry.getKey(),value);
}
} catch (Exception e) {
LOG.error("日志记录解析param参数失败",e.getMessage());
resultMap.put("paramParse","参数解析失败");
}
return resultMap;
}
private String getBodyContent(JoinPoint pjp) {
String bodyContent = "";
try {
Object[] args = pjp.getArgs();
List arguments = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments.add(args[i]);
}
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(arguments) && arguments.size() == 1) {
bodyContent = JSON.toJSONString(arguments.get(0));
} else {
bodyContent = JSON.toJSONString(arguments);
}
}catch (Exception e) {
LOG.error("日志记录解析body参数失败",e);
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("paramParse","参数解析失败");
}
return bodyContent;
}
}