package com.yunhai.bschclassmanage.aspect; import com.alibaba.fastjson.JSON; import com.yunhai.bschclassmanage.annotation.SyncOpLog; import com.yunhai.bschclassmanage.entity.SyncLog; import com.yunhai.bschclassmanage.enums.SyncDataType; import com.yunhai.bschclassmanage.service.ISyncLogService; import com.yunhai.bschclassmanage.utils.OtherUtil; import com.yunhai.bschclassmanage.utils.SensitiveDataUtil; import com.yunhai.bschclassmanage.utils.SyncLogRetryContext; import com.yunhai.bschclassmanage.utils.UserContext; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.LinkedHashMap; import java.util.Map; /** * 同步调用日志切面。 * 拦截所有标注 @SyncLog 的方法(当前仅 SyncUserBaseService), * 统一采集:枚举类型 / 大类(Sync-Send) / 类名.方法名 / 操作人 / 学校 / IP / 脱敏入参 / 异常, * 并在 finally 中异步落库(不影响主流程、异常照常抛出)。 */ @Slf4j @Aspect @Component @Order(10) public class SyncLogAspect { private static final int MAX_ERROR_MSG = 2000; @Autowired private ISyncLogService syncLogService; @Around("@annotation(syncLog)") public Object around(ProceedingJoinPoint pjp,SyncOpLog syncLog) throws Throwable { SyncDataType dataType = syncLog.value(); String classMethod = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName(); String jsonData = buildJsonData(pjp); SyncLog entity = new SyncLog(); // 枚举类型 key/name entity.setSyncDataTypeKey(dataType.getKey()); String typeName = dataType.getName(); if (SyncLogRetryContext.isRetry()) { // 重试重放时,在同步类型名上追加"(重试)"标记,便于与首次调用区分 typeName = (typeName == null ? "" : typeName) + "(重试)"; } entity.setSyncDataTypeName(typeName); // 大类:Send / Sync(由 key 前缀推导) boolean isSend = dataType.getKey() != null && dataType.getKey().startsWith("Send"); entity.setSyncType(isSend ? "Send" : "Sync"); entity.setSyncTypeName(isSend ? "推送" : "同步"); entity.setClassMethod(classMethod); entity.setUserId(UserContext.getUserId()); entity.setUserName(UserContext.getUserName()); entity.setSchId(UserContext.getSchId()); entity.setRequestIp(StringUtils.hasText(UserContext.getIp()) ? UserContext.getIp() : OtherUtil.getClientIp()); entity.setJsonData(jsonData); Object result; try { result = pjp.proceed(); } catch (Throwable t) { String msg = t.getMessage(); if (msg == null || msg.trim().isEmpty()) { msg = t.getClass().getName(); } entity.setErrorMsg(truncate(msg, MAX_ERROR_MSG)); entity.setStatus(0); syncLogService.saveAsync(entity); throw t; } entity.setStatus(1); syncLogService.saveAsync(entity); return result; } /** * 将方法参数按"参数名->值"序列化为 JSON,并用 SensitiveDataUtil 脱敏 pwd/idCard/phone 等。 */ private String buildJsonData(ProceedingJoinPoint pjp) { try { MethodSignature ms = (MethodSignature) pjp.getSignature(); String[] names = ms.getParameterNames(); Object[] args = pjp.getArgs(); if (names == null || args == null) return ""; Map map = new LinkedHashMap<>(); for (int i = 0; i < args.length && i < names.length; i++) { map.put(names[i], args[i]); } return SensitiveDataUtil.mask(JSON.toJSONString(map)); } catch (Exception e) { return ""; } } private String truncate(String s, int max) { return s != null && s.length() > max ? s.substring(0, max) : s; } }