Java自定义日志注解

项目中必不可少的业务操作日志,为了减少代码侵入以及公共参数提取,写了一个自定义注解,供大家参考。

@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();
		//参数名进行遍历
		//对所有参数中的platform参数进行的拦截,如果参数是空串,设置默认值。
		for (Object arg : args) {
			// 获取该对象的Class
			Class objClass = arg.getClass();
			dealParentClass(objClass, logPlatForm, arg);
		}
		//处理ip和设备类型
		//获取Ip地址
		String ipAddress = WebUtils.getIpAddr();
		//获取UA
		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) {
				//如果大于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);
		}
		//处理操作key
		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());

		//如果是拖动排序就需要手动将position值减现已置顶的数量
		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());

	
			//如果当前位置比目标位置小, 那么就向上排
			
			//如果是向上则查询出来区间所有的数据, 区间的数据需要降序批量-1
			
			//如果是向下排序, 区间数据需要升序批量+1
			
		//构造日志
		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);
	}
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

里丶夜行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值