刘帅
2026-09-22 bc68f4393cafe39c4fce614c8128936067ea25f7
cloud-server-management/src/main/java/com/dsh/guns/core/aop/LogAop.java
@@ -8,7 +8,10 @@
import com.dsh.guns.core.log.factory.LogTaskFactory;
import com.dsh.guns.core.support.HttpKit;
import com.dsh.guns.core.util.Contrast;
import com.dsh.guns.modular.system.audit.ManagementAuditWriter;
import com.dsh.guns.modular.system.audit.AuditDataFormatter;
import com.dsh.guns.modular.system.model.User;
import com.dsh.guns.modular.system.service.IUserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
@@ -19,8 +22,13 @@
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
/**
 * 日志记录
@@ -33,6 +41,8 @@
public class LogAop {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Resource private ManagementAuditWriter auditWriter;
    @Resource private IUserService userService;
    @Pointcut(value = "@annotation(com.dsh.guns.core.common.annotion.BussinessLog)")
    public void cutService() {
@@ -40,17 +50,119 @@
    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
        //先执行业务
        Object result = point.proceed();
        String beforeData = beforeData();
        try {
            handle(point);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
            Object result = point.proceed();
            appendUnifiedAudit(point, "SUCCESS", null, beforeData);
            return result;
        } catch (Throwable e) {
            appendUnifiedAudit(point, "FAIL", e.getMessage(), beforeData);
            throw e;
        }
        }
        return result;
    /**
     * @BussinessLog 是后台已有的“需要记录的用户操作”白名单。
     * 在这里写统一审计,能够覆盖 /mgr/add 等直接访问管理端的请求,
     * 并避免将其内部 Feign 调用和 POST 查询误记为操作。
     */
    private void appendUnifiedAudit(ProceedingJoinPoint point, String result, String errorMessage, String beforeData) {
        try {
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            BussinessLog annotation = method.getAnnotation(BussinessLog.class);
            User user = UserExt.getUser();
            if (annotation == null || user == null) return;
            HttpServletRequest request = HttpKit.getRequest();
            Map<String, Object> audit = new HashMap<String, Object>();
            audit.put("traceId", UUID.randomUUID().toString().replace("-", ""));
            audit.put("sourceService", "mb-cloud-management");
            audit.put("sourceType", "HTTP");
            audit.put("operationType", operationType(annotation.value()));
            audit.put("riskLevel", "NORMAL");
            audit.put("operationName", annotation.value());
            audit.put("requestMethod", request.getMethod());
            audit.put("requestUri", request.getRequestURI());
            // 审计表字段为 varchar,必须在调用审计服务前序列化;直接传 Map 会导致
            // Spring 无法绑定到 SysAccountAuditLog.requestParams(String),从而整条日志写入失败。
            audit.put("requestParams", AuditDataFormatter.format(maskedParameters(HttpKit.getRequestParameters()), User.class));
            audit.put("beforeData", beforeData);
            audit.put("afterData", afterData(beforeData));
            audit.put("userId", user.getId());
            audit.put("userAccount", user.getAccount());
            audit.put("clientIp", HttpKit.getIp());
            audit.put("result", result);
            audit.put("errorMessage", maskText(errorMessage));
            audit.put("createdAt", new Date());
            auditWriter.append(audit);
        } catch (Exception auditException) {
            // 审计失败不能影响原有业务。
            log.error("统一操作审计记录出错", auditException);
        }
    }
    private String operationType(String operationName) {
        String value = operationName == null ? "" : operationName;
        if (value.contains("删除")) return "DELETE";
        if (value.contains("添加") || value.contains("新增")) return "CREATE";
        if (value.contains("冻结") || value.contains("解冻") || value.contains("审核") || value.contains("状态")) return "STATUS_CHANGE";
        return "UPDATE";
    }
    private Map<String, String> maskedParameters(Map<String, String> parameters) {
        Map<String, String> masked = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            String key = entry.getKey();
            masked.put(key, key != null && key.toLowerCase().matches(".*(password|token|secret|idcard|identity|bank|card|mobile|phone).*")
                    ? "***" : entry.getValue());
        }
        return masked;
    }
    private String maskText(String text) {
        return text == null ? null : text.replaceAll("(?i)(password|token|authorization|idcard|bankCard)\\s*[:=]\\s*[^,\\s&}]+", "$1=***");
    }
    /** 员工修改前后均从数据库读取真实记录,避免将前端提交值误当作旧值。 */
    private String beforeData() {
        try {
            HttpServletRequest request = HttpKit.getRequest();
            if (!"/mgr/edit".equals(request.getRequestURI())) return null;
            Integer userId = Integer.valueOf(request.getParameter("id"));
            return userSnapshot(userService.getById(userId));
        } catch (Exception ignored) {
            return null;
        }
    }
    private String afterData(String beforeData) {
        if (beforeData == null) return null;
        try {
            Integer userId = Integer.valueOf(HttpKit.getRequest().getParameter("id"));
            return userSnapshot(userService.getById(userId));
        } catch (Exception ignored) {
            return null;
        }
    }
    private String userSnapshot(User target) {
        if (target == null) return null;
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("id", target.getId());
        data.put("account", target.getAccount());
        data.put("name", target.getName());
        data.put("phone", target.getPhone());
        data.put("birthday", target.getBirthday());
        data.put("sex", target.getSex());
        data.put("email", target.getEmail());
        data.put("roleid", target.getRoleid());
        data.put("deptid", target.getDeptid());
        data.put("status", target.getStatus());
        data.put("objectType", target.getObjectType());
        data.put("objectId", target.getObjectId());
        data.put("idCard", target.getIdCard());
        data.put("avatar", target.getAvatar());
        return AuditDataFormatter.format(data, User.class);
    }
    private void handle(ProceedingJoinPoint point) throws Exception {