项目中必不可少的业务操作日志,为了减少代码侵入以及公共参数提取,写了一个自定义注解,供大家参考。
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface DocOperateLog {
String value() default "";
PermissionEnum operateKey();
}
PermissionEnum为业务权限点枚举类,与操作行为契合
@Getter
public enum PermissionEnum {
…………
WEB_YIQIANFA_BAOCUNANDPUBLISH(712, "web.signed.baocunandpublish", "保存并发布"),
…………
public static PermissionEnum getByOperateKey(String operateKey) {
for (PermissionEnum operateKeyEnum : values()) {
if (operateKeyEnum.getOperateKey().equals(operateKey)) {
return operateKeyEnum;
}
}
return null;
}
}
切面处理逻辑
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class DocOperateLogAspect {
private final RemoteApprovalWorkflowService remoteApprovalWorkflowService;
private final HttpServletRequest request;
@Pointcut("@within(com.tmzh.mediacube.common.bussiness.log.annotation.DocOperateLog) || @annotation(com.tmzh.mediacube.common.bussiness.log.annotation.DocOperateLog)")
public void pointcut() {
}
@Around(" pointcut() && @annotation(logPlatForm) ")
public Object around(ProceedingJoinPoint joinPoint, DocOperateLog logPlatForm) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
Class objClass = arg.getClass();
dealParentClass(objClass, logPlatForm, arg);
}
String ipAddress = WebUtils.getIpAddr();
String UA = WebUtils.getUserAgent();
LogUtils.setThreadLocal(new LogAspectDTO().setIpAddress(ipAddress).setUA(UA));
try {
Object proceed = joinPoint.proceed(args);
return proceed;
} finally {
LogUtils.clearThreadLocal();
}
}
private void dealParentClass(Class objClass, DocOperateLog logPlatForm, Object arg) throws IllegalAccessException {
Class superclass = objClass;
while (null != superclass && !superclass.getName().contains("DocBaseCmd")) {
superclass = superclass.getSuperclass();
}
if (null == superclass) {
return;
}
Field[] fields = superclass.getDeclaredFields();
Integer approvalLevel = 0;
String approvalLevelName = null;
PermissionEnum operateKey;
Map<String, Field> fieldMap = Arrays.stream(fields)
.collect(Collectors.toMap(Field::getName, field -> {
field.setAccessible(true);
return field;
}));
Long siteId = null;
if (fieldMap.containsKey("siteId")) {
siteId = (Long) fieldMap.get("siteId").get(arg);
}
if (fieldMap.containsKey("approvalLevel") && !ObjectUtils.isEmpty(fieldMap.get("approvalLevel").get(arg))) {
approvalLevel = (Integer) fieldMap.get("approvalLevel").get(arg);
if (approvalLevel > 0) {
try {
if (siteId != null) {
R<String> r = remoteApprovalWorkflowService.getApprovalLevelName(siteId, approvalLevel);
approvalLevelName = r.getData();
} else {
log.warn("送审相关操作异常,operateKey:{},platform:{}", fieldMap.get("operateKey"), fieldMap.get("platform"));
approvalLevelName = "待审";
}
} catch (Exception e) {
approvalLevelName = "待审";
}
}
}
if (fieldMap.containsKey("platform")) {
Field platformField = fieldMap.get("platform");
platformField.setAccessible(true);
String platform = StringUtils.isEmpty(approvalLevelName) ? logPlatForm.value() : approvalLevelName;
platformField.set(arg, platform);
}
if (fieldMap.containsKey("operateKey")) {
Field operateKeyField = fieldMap.get("operateKey");
operateKeyField.setAccessible(true);
operateKey = approvalLevel > 0 ? PermissionEnum.getByOperateKey(logPlatForm.operateKey().getOperateKey().replace(".unaudited1", MessageFormat.format(".unaudited{0}", approvalLevel))) : logPlatForm.operateKey();
operateKeyField.set(arg, operateKey);
}
}
}
业务场景需要记录稿件排序时的操作,使用案例:
@ApiOperation("稿件排序(已签发)")
@PostMapping("/sort")
@DocOperateLog(value = "已签发", operateKey = WEB_YIQIANFA_GAOJIANPAIXU)
public R sort(@RequestBody SortDocCmd cmd){
websiteDocService.sortDoc(cmd);
return R.ok();
}
通过注解赋值指定渠道,以及具体权限位,再构造日志体的时候可以直接获取
public void execute(SortDocCmd sortWebsiteDocCmd) {
ChannelDoc channelDoc = channelDocRepository.getById(sortWebsiteDocCmd.getChannelDocId());
if (sortWebsiteDocCmd.getOperateType() != null && sortWebsiteDocCmd.getOperateType() == 2) {
Long topCount = channelDocRepository.cntTopDocByChannelId(sortWebsiteDocCmd.getChannelId());
sortWebsiteDocCmd.setPosition(sortWebsiteDocCmd.getPosition() - topCount.intValue());
}
Channel channel = channelRepository.getByChannelId(channelDoc.getChannelId());
channelDoc.setUpdateByManual(channelDoc.getUpdateBy());
Long cntTop = channelDocRepository.cntTopDocByChannelId(sortWebsiteDocCmd.getChannelId()) + 1;
if (DEFAULT_TOP_ORDER > sortWebsiteDocCmd.getPosition() || sortWebsiteDocCmd.getPosition() == 1){
LocalDateTime now = LocalDateTime.now();
channelDoc.setDocOrder(channelDocRepository.generateDocOrder(sortWebsiteDocCmd.getChannelId()));
channelDoc.setRelTime(now);
channelDocRepository.updateById(channelDoc);
extensionExecutor.executeVoid(PublishExtPt.class, BusinessType.valueOf(WEBSITE, COLUMN_PUBLISH),
executor -> executor.publish(channel));
DocOperateLogDTO docOperateLogDTO = new DocOperateLogDTO()
.setDocId(channelDoc.getDocId()).setCreateTimeManual(now).setUpdateTimeManual(now)
.setSrcChannelId(sortWebsiteDocCmd.getChannelId()).setOperateKey(sortWebsiteDocCmd.getOperateKey().getOperateKey())
.setPlatform(sortWebsiteDocCmd.getPlatform()).setSiteId(sortWebsiteDocCmd.getSiteId());
docOperateLogDomainService.saveLog(docOperateLogDTO, SiteMediaTypeEnum.WEB_SITE);
return;
}
ChannelDoc targetPosition = channelDocRepository.getSortPosition(sortWebsiteDocCmd.getChannelId(),
sortWebsiteDocCmd.getPosition());
LocalDateTime now = LocalDateTime.now();
DocOperateLogDTO docOperateLogDTO = new DocOperateLogDTO()
.setDocId(channelDoc.getDocId()).setCreateTimeManual(now).setUpdateTimeManual(now)
.setSrcChannelId(sortWebsiteDocCmd.getChannelId()).setOperateKey(sortWebsiteDocCmd.getOperateKey().getOperateKey())
.setPlatform(sortWebsiteDocCmd.getPlatform()).setSiteId(sortWebsiteDocCmd.getSiteId());
docOperateLogDomainService.saveLog(docOperateLogDTO, SiteMediaTypeEnum.WEB_SITE);
}