Spring的Aspect做异步系统日志、异常信息的记录,代码齐全,复制粘贴就可以用


import cn.hutool.core.util.BooleanUtil;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author mac
 * @date 2022/1/14 03:22
 */
public class AuthUser implements UserDetails {

    private final UserDto userDto;

    private final List<String> perms;

    public AuthUser(UserDto userDto, List<String> perms) {
        this.userDto = userDto;
        this.perms = perms;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //返回当前用户的权限
        return this.getPerms()
                .stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }

    @Override
    public String getPassword() {
        return userDto.getPassword();
    }

    @Override
    public String getUsername() {
        return userDto.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return BooleanUtil.isTrue(userDto.getEnable());
    }

    public UserDto getUserDto() {
        return userDto;
    }

    public List<String> getPerms() {
        return perms;
    }
}

/**
 * 总消息类 用来存放消息
 *
 * @date 2020-09-22 17:07
 */
public interface BaseMsg {

    /**
     * 获取消息的状态码
     */
    Integer getCode();

    /**
     * 获取消息提示信息
     */
    String getMessage();

}

import cn.hutool.core.util.StrUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;

/**
 * @author iqiao
 * @date 2018/8/18.
 */
public class ContextHolder {

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getRequest();
    }

    public static HttpServletResponse getResponse() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getResponse();
    }

    public static String getRequestIp() {
        return IPUtil.getClientId(getRequest());
    }

    public static String getToken() {
        return getRequest().getHeader(HeaderConstants.ACCESS_TOKEN);
    }

    public static Long getCurrUserId() {
        return getCurrUser().getId();
    }

    public static UserDto getCurrUser() {
        UserDto userDto = getCurrUserWithNull();
        if (userDto == null) {
            throw new ServiceException(TokenMsg.EXCEPTION_USER_NULL);
        }
        return userDto;
    }

    public static Long getCurrUserIdWithNull() {
        return Optional.ofNullable(getCurrUserWithNull()).map(UserDto::getId).orElse(null);
    }

    public static String getCurrUserNameWithNull() {
        return Optional.ofNullable(getCurrUserWithNull())
                .map(userDto -> StrUtil.blankToDefault(userDto.getUsername(), userDto.getRealName()))
                .orElse(null);
    }

    public static UserDto getCurrUserWithNull() {
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        if (auth == null) {
            return null;
        }
        Object principal = auth.getPrincipal();
        if ("anonymousUser" == principal) {
            return null;
        }
        AuthUser authUser = (AuthUser) principal;
        return authUser.getUserDto();
    }

}

/**
 * 核心类 - 消息
 *
 * @date 2020-09-13 19:36
 */
public enum CoreMsg implements BaseMsg {

    /**
     * SQL
     */
    SQL_EXCEPTION_UPDATE(10100, "更新数据失败,是否刷新页面重试?"),
    SQL_EXCEPTION_INSERT(10100, "新增数据失败,是否刷新页面重试?"),
    SQL_EXCEPTION_DELETE(10100, "删除数据失败,是否刷新页面重试?"),
    SQL_EXCEPTION_INTEGRITY_CONSTRAINT_VIOLATION(10105, "数据主键冲突或者已有该数据!"),
    SQL_EXCEPTION_NOT_HAVE_DEFAULT_VALUE(10106, "数据异常:{} 字段没有默认值!"),
    SQL_EXCEPTION_UNKNOWN(10106, "数据异常:未知异常,请联系系统管理员 {}"),
    SQL_EXCEPTION_ERROR(10107, "数据异常:未知异常,请联系系统管理员"),

    /**
     * Redis
     */
    REDIS_EXCEPTION_PUSH_SUB(10200, "Redis 订阅通道失败!"),
    REDIS_EXCEPTION_LOCK(10201, "无法申领分布式锁"),

    /**
     * Excel
     */
    EXCEL_EXPORT_SUCCESS(200, "Excel 导出成功!  -  数据行数:{}  -  耗时:{}"),
    EXCEL_EXPORT_ERROR(10301, "Excel 导出失败!  -  耗时:{}  -  失败信息:{}"),
    EXCEL_IMPORT_SUCCESS(200, "EXCEL 导入成功!  -  耗时:{}"),
    EXCEL_IMPORT_ERROR(10303, "Excel导入失败!   -  耗时:{}  -  失败信息:{}"),
    EXCEL_IMPORT_NO(10304, "导入对象为空"),
    EXCEL_FILE_NULL(10305, "请选择文件"),
    EXCEL_HANDLE_MAX(10700, "超出最大操作数量, 当前数据[{}]条,允许最大阈值[{}]条"),


    /**
     * 缓存
     */
    CACHE_PUNCTURE_EXCEPTION(10405, "当前服务繁忙,客官请稍微再次尝试!"),
    CACHE_DEL_EXCEPTION(10406, "无法清除缓存,请稍后再试"),


    /**
     * dubbo
     */
    RPC_INVOKE_ERROR(10600, "服务调用失败"),

    /**
     * 其他
     */
    OTHER_EXCEPTION_LIMITER(10700, "当前系统繁忙,请稍后再试"),
    OTHER_EXCEPTION_CRYPTO_EN(10702, "加密失败"),
    OTHER_EXCEPTION_CRYPTO_DE(10703, "解密失败"),
    OTHER_EXCEPTION_CRYPTO_REFLEX(10704, "解密反射失败"),
    OTHER_EXCEPTION_UTILS_INIT(10705, "系统工具类暂未初始化"),
    OTHER_EXCEPTION_SERVER_ERROR(10706, "当前系统繁忙,请稍后再试"),

    /**
     * 远程调用失败
     */
    REMOTE_INVOKE_ERROR(10800, "远程服务调用失败"),
    DUBBO_INVOKE_ERROR(10801, "消费服务调用失败"),

    /**
     * Excel 异常
     */
    EXCEPTION_FILE_FORMAT(10900, "文件格式错误!"),
    EXCEPTION_CREATE_ERROR(10901, "创建文件失败!"),


    ;

    private final int code;
    private final String message;

    CoreMsg(int code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }
}

/**
 * 自定义操作日志注解
 *
 * @date 2020-09-12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableLog {

    /**
     * 标题
     */
    String title() default "";

    /**
     * 忽略
     */
    boolean ignore() default false;

}

/**
 * @author mac
 * @date 2022/1/13 09:47
 */
public interface HeaderConstants {

    /**
     * ACCESS_TOKEN
     */
    String ACCESS_TOKEN = "X-Token";

    /**
     * USER_ID
     */
    String USER_ID = "X-USER-ID";

    /**
     * USER_ID
     */
    String USER_NAME = "X-USER-NAME";

}

import cn.hutool.core.lang.Validator;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;

import javax.servlet.http.HttpServletRequest;

/**
 * IP 工具类
 *
 * @date 2020-09-19 23:21
 */
public final class IPUtil {

    /**
     * 排除结果
     */
    private static final String UNKNOWN = "unknown";
    /**
     * 尝试字段
     */
    private static final String[] HEADERS_TO_TRY = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR",
            "X-Real-IP"
    };


    private IPUtil() {
    }

    /***
     * 获取客户端地址
     *
     * @param request request
     * @return String
     */
    public static String getClientAddress(HttpServletRequest request) {
        for (String header : HEADERS_TO_TRY) {
            String address = request.getHeader(header);
            if (StrUtil.isNotBlank(address)
                    && !UNKNOWN.equalsIgnoreCase(address)) {
                return address;
            }
        }
        return request.getRemoteAddr();
    }

    /***
     * 获取客户端地址 (多重代理只获得第一个)
     *
     * @param request request
     * @return String
     */
    public static String getClientAddressBySingle(HttpServletRequest request) {
        String clientAddress = getClientAddress(request);
        return NetUtil.getMultistageReverseProxyIp(clientAddress);
    }

    /***
     * 获取客户端IP
     *
     * @param request request
     * @return String
     */
    public static String getClientId(HttpServletRequest request) {
        for (String header : HEADERS_TO_TRY) {
            String ip = request.getHeader(header);
            if (StrUtil.isNotBlank(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
                String reverseProxyIp = NetUtil.getMultistageReverseProxyIp(ip);
                if (Validator.isIpv4(reverseProxyIp) || Validator.isIpv6(reverseProxyIp)) {
                    // 判断是否为IP 返回原始IP
                    return ip;
                }
            }
        }
        // 否则返回 空地址
        return "";
    }

    // ===============

    /***
     * 获取客户端IP (多重代理只获得第一个)
     *
     * @param request request
     * @return String
     */
    public static String getClientIdBySingle(HttpServletRequest request) {
        String clientIp = getClientId(request);
        return NetUtil.getMultistageReverseProxyIp(clientIp);
    }
}

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
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.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 参数校验 拦截处理
 *
 * @date 2020-09-16
 */
@Slf4j
@Order(150)
@Aspect
@Component
public class LogAop {


    @Pointcut("execution(public * com.traffic.admin..*.*Controller*.*(..))")
    public void requestMapping() {
    }

    /**
     * 切如 post 请求
     *
     * @param point point
     */
    @Around("requestMapping()")
    public Object logAop(ProceedingJoinPoint point) throws Throwable {
        // 计时器
        TimeInterval timer = DateUtil.timer();
        // 执行
        Exception exception = null;
        // 防止线程抛异常 线程变量不回收 导致oom
        Object returnValue;
        try {
            // 执行正常操作
            returnValue = point.proceed();
        } catch (Exception e) {
            exception = e;
            throw e;
        } finally {
            // 花费毫秒数
            long timerCount = timer.interval();
            //保存日志
            LogUtil.saveLog(point, exception, timerCount);
        }
        return returnValue;
    }

}

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 日志
 *
 * @author mac
 * @date 2022/1/13 11:15
 */
@Data
public class LogsDto implements Serializable {

    /**
     * ID
     */
    @ApiModelProperty(value = "ID")
    private Long id;

    /**
     * 日志类型(1:接入日志;2:错误日志)
     */
    @ApiModelProperty(value = "日志类型")
    private Integer type;

    /**
     * 日志标题
     */
    @ApiModelProperty(value = "日志标题")
    private String title;

    /**
     * 操作用户的IP地址
     */
    @ApiModelProperty(value = "操作用户的IP地址")
    private String remoteAddr;

    /**
     * 操作用户代理信息
     */
    @ApiModelProperty(value = "操作用户代理信息")
    private String userAgent;

    /**
     * 执行时间
     */
    @ApiModelProperty(value = "执行时间")
    private Long timeout;

    /**
     * 操作的URI
     */
    @ApiModelProperty(value = "操作的URI")
    private String requestUri;

    /**
     * 操作的方式
     */
    @ApiModelProperty(value = "操作的方式")
    private String method;

    /**
     * 操作提交的数据
     */
    @ApiModelProperty(value = "操作提交的数据")
    private String params;

    /**
     * 异常信息
     */
    @ApiModelProperty(value = "异常信息")
    private String exception;

    /**
     * 创建人
     */
    @ApiModelProperty(value = "创建人")
    private Long createBy;

    /**
     * 创建时间
     */
    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    /**
     * 更新人
     */
    @ApiModelProperty(value = "修改人")
    private Long updateBy;

    /**
     * 更新时间
     */
    @ApiModelProperty(value = "修改时间")
    private Date updateTime;
}

import com.baomidou.mybatisplus.annotation.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * @author mac
 * @date 2022/1/13 11:18
 */
public enum LogsType implements IEnum<Integer> {

    ACCESS(1, "接入日志"),
    EXCEPTION(2, "错误日志"),
    ;

    @JsonValue
    private final int value;

    private final String label;

    LogsType(final int value, final String label) {
        this.value = value;
        this.label = label;
    }

    @Override
    public Integer getValue() {
        return this.value;
    }

    public String getLabel() {
        return label;
    }
}

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.json.JSONUtil;
import com.traffic.admin.core.securities.SpringSecurity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 日志工具类
 *
 * @date 2020-09-22 11:17
 */
@Slf4j
public final class LogUtil {

    private LogUtil() {
    }

    /**
     * 保存日志
     *
     * @param point      point
     * @param e          异常
     * @param timerCount 花费毫秒数
     */
    public static void saveLog(ProceedingJoinPoint point, Exception e, long timerCount) {

        try {
            Object[] args = point.getArgs();
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();

            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            if (sra == null) {
                return;
            }
            HttpServletRequest request = sra.getRequest();

            // EnableLog 忽略 则直接退出
            EnableLog enableLog = method.getAnnotation(EnableLog.class);
            if (enableLog != null && BooleanUtil.isTrue(enableLog.ignore())) {
                return;
            }

            Long userId;
            try {
                userId = ContextHolder.getCurrUserId();
//                userId = Long.valueOf(SpringSecurity.getUserId());
            } catch (Exception ex) {
                return;
            }

            String argsStr = null;
            try {
                argsStr = JSONUtil.toJsonStr(args);
            } catch (Exception ignored) {
            }

            LogsDto logsDto = new LogsDto();
            // 操作方法
            String methodName = request.getMethod();
            // 获得IP
            String clientIpAddress = IPUtil.getClientIdBySingle(request);

            // 设置标题
            setTitle(point, method, logsDto);
            // 设置类型
            logsDto.setType(e == null ? LogsType.ACCESS.getValue() : LogsType.EXCEPTION.getValue());
            // 设置客户端代理
            logsDto.setUserAgent(request.getHeader("user-agent"));
            // 设置URI
            logsDto.setRequestUri(request.getRequestURI());
            // 设置IP
            logsDto.setRemoteAddr(clientIpAddress);
            // 设置参数
            logsDto.setParams(argsStr);
            // 设置方法
            logsDto.setMethod(methodName);
            // 设置执行时长
            logsDto.setTimeout(timerCount);
            // 设置异常信息
            if (e != null) {
                logsDto.setException(e.getMessage());
            }

            logsDto.setCreateBy(userId);
            logsDto.setUpdateBy(userId);

            // 保存日志
            log.info(logsDto.toString());
            LogsThreadPool.process(logsDto);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    /**
     * 设置日志标题
     *
     * @param point   point
     * @param method  方法
     * @param logsDto 日志模型
     */
    private static void setTitle(ProceedingJoinPoint point, Method method, LogsDto logsDto) {
        // 设置 title
        EnableLog enableLog = method.getAnnotation(EnableLog.class);
        if (enableLog != null) {
            //注解上的描述,操作日志内容
            String title = enableLog.title();
            if (StringUtils.isNotEmpty(title)) {
                logsDto.setTitle(title);
            }
        }
        // 如果title 还是为空 则系统自动赋class
        if (StringUtils.isEmpty(logsDto.getTitle())) {
            // 获取请求的类名
            String className = point.getTarget().getClass().getName();
            String methodName = method.getName();
            logsDto.setTitle(className + "." + methodName);
        }
    }
}

/**
 * 框架服务异常
 *
 * @date 2020-09-13 19:41
 */
public class ServiceException extends RuntimeException {

    private Integer code;

    private String errorMessage;

    public ServiceException() {
        this(CoreMsg.OTHER_EXCEPTION_SERVER_ERROR.getCode(), CoreMsg.OTHER_EXCEPTION_SERVER_ERROR.getMessage());
    }

    public ServiceException(Integer code, String errorMessage) {
        super(errorMessage);
        this.code = code;
        this.errorMessage = errorMessage;
    }

    public ServiceException(Integer code, String errorMessage, Throwable e) {
        super(errorMessage, e);
        this.code = code;
        this.errorMessage = errorMessage;
    }

    public ServiceException(BaseMsg msg) {
        super(msg.getMessage());
        this.code = msg.getCode();
        this.errorMessage = msg.getMessage();
    }

    public ServiceException(BaseMsg baseMsg, String message) {
        super(message);
        this.code = baseMsg.getCode();
        this.errorMessage = message;
    }

    public ServiceException(BaseMsg msg, Throwable e) {
        super(msg.getMessage(), e);
        this.code = msg.getCode();
        this.errorMessage = msg.getMessage();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }


}

/**
 * Token - 消息
 *
 * @date 2020-09-13 19:36
 */
public enum TokenMsg implements BaseMsg {

    /**
     * Token
     */
    EXCEPTION_TOKEN_CREATE_ERROR(12000, "生成Token失败"),
    EXCEPTION_TOKEN_CREATE_LIMIT_ERROR(12001, "您的账号已在其他设备登录"),
    EXCEPTION_TOKEN_LOSE_EFFICACY(401, "Token失效,请重新登录"),


    /**
     * 登陆
     */
    EXCEPTION_CAPTCHA_ERROR(12100, "验证码不正确!"),
    EXCEPTION_CAPTCHA_NULL(12201, "验证码已失效"),
    EXCEPTION_CAPTCHA_UUID_NULL(12202, "验证码UUID为空"),
    EXCEPTION_CAPTCHA_CODE_NULL(12203, "验证码为空"),
    EXCEPTION_LOGIN_ACCOUNT_NO(12101, "账号或密码不正确!"),
    EXCEPTION_LOGIN_ACCOUNT_LOCKED(12102, "账号已被锁定,请联系管理员!"),
    EXCEPTION_LOGOUT_ERROR(12103, "登出失败,没有授权Token!"),
    EXCEPTION_LOGOUT_SUCCESS(12104, "登出成功!"),
    EXCEPTION_LOGIN_ACCOUNT_LOCK(12104, "账号已锁定,请{}后,再次尝试"),
    EXCEPTION_LOGIN_TENANT_NOT_USABLE(12105, "租户未启用,请联系管理员"),
    EXCEPTION_LOGIN_NULL(12106, "请输入账号密码"),
    EXCEPTION_LOGIN_DECRYPT(12107, "登录账号密码解析失败"),
    EXCEPTION_USER_ROLE_NOT_NULL(12108, "用户暂无角色,请设置后登录"),
    EXCEPTION_USER_MENU_NOT_NULL(12109, "用户暂无角色菜单,请设置后登录"),
    EXCEPTION_USER_PERMS_NOT_NULL(12110, "用户暂无权限,请设置后登录"),
    /**
     * 其他
     */
    EXCEPTION_USER_NULL(12200, "用户为空"),
    EXCEPTION_NOT_AUTH(403, "无权访问该方法"),
    EXCEPTION_NOT_REALM(12202, "找不到认证授权器"),

    ;

    private final int code;
    private final String message;

    TokenMsg(int code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }
}

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * @author mac
 * @date 2022/1/11 01:42
 */
@Data
public class UserDto implements Serializable {

    /**
     * 主键
     */
    private Long id;

    /**
     * 身份(1普通用户 2管理员 3商家 4店员)
     *
     */
    private Integer identity;

    /**
     * 登录账户
     */
    @ApiModelProperty(value = "登录账户")
    private String username;

    /**
     * 登录密码
     */
    @ApiModelProperty(value = "登录密码")
    private String password;

    /**
     * 登录密码强度
     */
    @ApiModelProperty(value = "登录密码强度")
    private Integer passwordLevel;

    /**
     * 是否启用
     */
    @ApiModelProperty(value = "是否启用")
    private Boolean enable;

    /**
     * 真实姓名
     */
    @ApiModelProperty(value = "真实姓名")
    private String realName;

    /**
     * 手机
     */
    @ApiModelProperty(value = "手机")
    private String mobile;

    /**
     * 邮箱
     */
    @ApiModelProperty(value = "邮箱")
    private String email;

//    /**
//     * 工号
//     */
//    @ApiModelProperty(value = "工号")
//    private String no;

    /**
     * 头像
     */
    @ApiModelProperty(value = "头像")
    private String avatar;

    /**
     * 最后登陆IP
     */
    @ApiModelProperty(value = "最后登陆IP")
    private String loginIp;

    /**
     * 备注
     */
    @ApiModelProperty(value = "备注")
    private String remark;

    /**
     * 签名
     */
    @ApiModelProperty(value = "签名")
    private String sign;

    @ApiModelProperty(value = "小程序openId")
    private String openId;
    /**
     * 角色名称
     */
    @ApiModelProperty(value = "角色名称")
    private String roleNames;

    /**
     * 商家id
     */
    @ApiModelProperty(value = "商家id")
    private Long shopId;

    /**
     * 门店id
     */
    @ApiModelProperty(value = "门店id")
    private Long storeId;

    /**
     * 员工id
     */
    @ApiModelProperty(value = "员工id")
    private Long employeeId;

    /**
     * 是否商家所有者
     */
    @ApiModelProperty(value = "是否商家所有者")
    private Boolean isShopOwner;

    /**
     * 门店
     */
//    private StoreDto store;

    /**
     * 员工信息
     */
//    private EmployeeDto employeeDto;

    /**
     * 是否管理员
     *
     * @return
     */
//    public boolean isAdmin() {
//        return UserIdentity.ADMIN.getValue().equals(this.identity);
//    }
}

import com.ichurun.saas.common.thread.AsyncProcessExecutor;
import com.ichurun.saas.common.thread.AsyncProcessExecutorFactory;
import com.ichurun.saas.open.system.dto.LogsDto;
import com.ichurun.saas.open.system.rpc.LogsService;
import lombok.extern.slf4j.Slf4j;

/**
 * 日志保存线程
 *
 * @date 2020-09-16
 */
@Slf4j
public class LogsThreadPool {


    /**
     * 日志API
     */
    private static LogsService logsService;

    /**
     * 执行
     *
     * @param logsDto 日志模型
     */
    public static void process(LogsDto logsDto) {
        if (logsDto == null) {
            return;
        }

        AsyncProcessExecutor normalExecutor = AsyncProcessExecutorFactory.createNormalExecutor();
        normalExecutor.put(() -> {
            // 存储临时 token
            try {
                logsService.insert(logsDto);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        });
        normalExecutor.execute();
    }


    // ========================

    public void setLogsService(LogsService logsService) {
        LogsThreadPool.logsService = logsService;
    }

}

/**
 * 异步进程 执行器
 *
 * @date 2021年7月15日13:43:37
 */
public interface AsyncProcessExecutor {


    /**
     * 存放任务
     *
     * @param task 任务
     * @return AsyncProcessExecutor
     */
    AsyncProcessExecutor put(final Runnable task);

    /**
     * 执行
     *
     * @return boolean
     */
    boolean execute();

}

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 多线程锁执行器 正常处理
 *
 * @date 2020-12-10 10:36
 */
@Slf4j
public class AsyncProcessExecutorByNormal implements AsyncProcessExecutor {

    /**
     * 线程池字典
     */
    private static final Map<String, AsyncProcessor> EXECUTOR_MAP = Maps.newConcurrentMap();

    /**
     * 线程Key
     */
    private final String key;

    /**
     * 任务队列
     */
    private final List<Runnable> taskList;

    /**
     * 执行器
     */
    private final AsyncProcessor processor;

    /**
     * 构造函数
     */
    public AsyncProcessExecutorByNormal() {
        this.key = "def";
        taskList = new ArrayList<>();
        processor = AsyncProcessExecutorByNormal.getProcessor(this.key);
    }

    /**
     * 构造函数
     *
     * @param key 线程池唯一Key
     */
    public AsyncProcessExecutorByNormal(String key) {
        this.key = key;
        taskList = new ArrayList<>();
        processor = AsyncProcessExecutorByNormal.getProcessor(this.key);
    }

    /**
     * 获得执行器
     *
     * @param key Key
     * @return AsyncProcessor
     */
    private synchronized static AsyncProcessor getProcessor(String key) {
        AsyncProcessor asyncProcessor = EXECUTOR_MAP.get(key);
        if (null == asyncProcessor) {
            asyncProcessor = new AsyncProcessor();
            asyncProcessor.init(key);
            EXECUTOR_MAP.put(key, asyncProcessor);
        }
        return asyncProcessor;
    }

    /**
     * 执行
     *
     * @param task 任务
     */
    @Override
    public AsyncProcessExecutorByNormal put(final Runnable task) {
        taskList.add(task);
        return this;
    }

    // ====================================

    /**
     * 执行 线程锁 等待查询结果 结果完成后继续执行
     *
     * @return boolean 最终直接结果
     */
    @Override
    public boolean execute() {
        if (CollUtil.isEmpty(this.taskList)) {
            return true;
        }

        for (Runnable task : this.taskList) {
            // 多线程执行任务
            this.execute(task);
        }

        // 返回执行结果
        return true;
    }

    /**
     * 执行指定的任务
     *
     * @param task 任务
     * @return boolean
     */
    private boolean execute(final Runnable task) {
        return processor.executeTask(task);
    }

}

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 多线程锁执行器
 * 用于当前方法中复杂业务多线程处理,等待线程执行完毕后 获得统一结果
 * 2021年11月2日14:07:54 重构 多线程异步等待执行器
 *
 * @author 周鹏程
 * @date 2020-12-10 10:36
 */
@Slf4j
public class AsyncProcessExecutorByWait implements AsyncProcessExecutor {

    /**
     * 线程池字典
     */
    private static final Map<String, AsyncProcessor> EXECUTOR_MAP = Maps.newConcurrentMap();

    /**
     * 线程Key
     */
    private final String key;
    /**
     * 任务队列
     */
    private final List<Callable<Object>> taskList;
    /**
     * 执行器
     */
    private final AsyncProcessor processor;
    /**
     * 任务执行计数器
     */
    private AtomicInteger count;


    /**
     * 构造函数
     */
    public AsyncProcessExecutorByWait() {
        this.key = "def";
        taskList = new ArrayList<>();
        processor = getProcessor(this.key);
    }

    /**
     * 构造函数
     *
     * @param key 线程池唯一Key
     */
    public AsyncProcessExecutorByWait(String key) {
        this.key = key;
        taskList = new ArrayList<>();
        processor = getProcessor(this.key);
    }

    /**
     * 获得执行器
     *
     * @param key Key
     * @return AsyncProcessor
     */
    private synchronized static AsyncProcessor getProcessor(String key) {
        AsyncProcessor asyncProcessor = EXECUTOR_MAP.get(key);
        if (null == asyncProcessor) {
            asyncProcessor = new AsyncProcessor();
            asyncProcessor.init(key);
            EXECUTOR_MAP.put(key, asyncProcessor);
        }
        return asyncProcessor;
    }

    /**
     * 放入执行任务
     * 特殊处理 Runnable 转换为 Callable
     *
     * @param task 任务
     */
    @Override
    public AsyncProcessExecutor put(final Runnable task) {
        taskList.add(Executors.callable(task));
        return this;
    }

    /**
     * 执行 线程锁 等待查询结果 结果完成后继续执行
     */
    @Override
    public boolean execute() {
        if (CollUtil.isEmpty(this.taskList)) {
            return true;
        }

        // 初始化锁参数
        count = new AtomicInteger(this.taskList.size());
        // 门闩 线程锁
        CountDownLatch latch = new CountDownLatch(this.taskList.size());

        for (Callable<Object> task : this.taskList) {
            // 回调减 门闩
            processor.executeTaskAndCallback(task, (result) -> {
                if (result.getSuccess()) {
                    count.decrementAndGet();
                }
                latch.countDown();
                return null;
            });
        }

        // 线程锁 等待查询结果 结果完成后继续执行
        try {
            latch.await();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            this.taskList.clear();
        }

        // 返回执行结果
        return count.get() == 0;
    }

}

/**
 * 异步进程 执行器 工厂
 *
 * @date 2021年7月15日13:43:37
 */
public final class AsyncProcessExecutorFactory {

    private AsyncProcessExecutorFactory() {
    }

    /**
     * 创建等待执行器
     *
     * @return AsyncProcessExecutor
     */
    public static AsyncProcessExecutor createWaitExecutor() {
        return new AsyncProcessExecutorByWait();
    }

    /**
     * 创建等待执行器
     *
     * @param key KEY
     * @return AsyncProcessExecutor
     */
    public static AsyncProcessExecutor createWaitExecutor(String key) {
        return new AsyncProcessExecutorByWait(key);
    }

    /**
     * 创建正常执行器
     *
     * @return AsyncProcessExecutor
     */
    public static AsyncProcessExecutor createNormalExecutor() {
        return new AsyncProcessExecutorByNormal();
    }

    // =====================

    /**
     * 创建正常执行器
     *
     * @param key KEY
     * @return AsyncProcessExecutor
     */
    public static AsyncProcessExecutor createNormalExecutor(String key) {
        return new AsyncProcessExecutorByNormal(key);
    }

}

import cn.hutool.core.util.StrUtil;
import com.google.common.util.concurrent.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 自定义线程执行器 - 等待线程执行完毕不拒绝
 *
 * @author 周鹏程
 * @date 2020-10-08 10:24
 */
@Slf4j
public class AsyncProcessor {

    /**
     * 线程池名称格式
     */
    private static final String THREAD_POOL_NAME = "AsyncProcessorWaitPool-{}-%d";

    /**
     * 默认线程池关闭等待时间 秒
     */
    private static final int DEFAULT_WAIT_TIME = 10;

    /**
     * 线程池监听执行器
     */
    private ListeningExecutorService execute;

    /**
     * 初始化
     *
     * @param key 线程池标识
     */
    public void init(String key) {
        if (StringUtils.isBlank(key)) {
            return;
        }

        // 线程工厂名称
        String formatThreadPoolName = StrUtil.format(THREAD_POOL_NAME, key);

        // 创建 Executor
        // 此处默认最大值改为处理器数量的 4 倍
        try {
            // 监听执行器
            execute = MoreExecutors.listeningDecorator(
                    ThreadPoolFactory.createDefThreadPool(formatThreadPoolName));

            // 这里不会自动关闭线程, 当线程超过阈值时 抛异常
            // 关闭事件的挂钩
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                log.info("ProcessorWait 异步处理器关闭");

                execute.shutdown();

                try {
                    // 等待1秒执行关闭
                    if (!execute.awaitTermination(DEFAULT_WAIT_TIME, TimeUnit.SECONDS)) {
                        log.error("ProcessorWait 由于等待超时,异步处理器立即关闭");
                        execute.shutdownNow();
                    }
                } catch (InterruptedException e) {
                    log.error("ProcessorWait 异步处理器关闭中断");
                    execute.shutdownNow();
                }

                log.info("ProcessorWait 异步处理器关闭完成");
            }));
        } catch (Exception e) {
            log.error("ProcessorWait 异步处理器初始化错误", e);
            throw new ExceptionInInitializerError(e);
        }
    }


    /**
     * 执行任务,不管是否成功<br>
     * 其实也就是包装以后的 {@link } 方法
     *
     * @param task 任务
     * @return boolean
     */
    public boolean executeTask(Runnable task) {
        try {
            execute.execute(task);
        } catch (RejectedExecutionException e) {
            log.error("AsyncProcessorWait 执行任务被拒绝", e);
            return false;
        }
        return true;
    }

    /**
     * 提交任务,并可以在稍后获取其执行情况<br>
     * 当提交失败时,会抛出 {@link }
     *
     * @param task 任务
     */
    public <T> void executeTaskAndCallback(Callable<T> task, Function<CallbackResult<T>, Void> callback) {
        ListenableFuture<T> future = execute.submit(task);
        Futures.addCallback(future, new FutureCallback<T>() {
            @Override
            public void onSuccess(T result) {
                CallbackResult<T> callbackResult = new CallbackResult<>();
                callbackResult.setSuccess(true);
                callbackResult.setResult(result);
                // 线程池失败后 返回该 Runnable
                callback.apply(callbackResult);
            }

            @Override
            public void onFailure(Throwable t) {
                log.error("线程名称:{} - 执行异常信息:{}", Thread.currentThread().getName(), t.getMessage());
                CallbackResult<T> callbackResult = new CallbackResult<>();
                callbackResult.setSuccess(false);
                callback.apply(callbackResult);
            }
        }, execute);
    }

    // =================

    /**
     * 回调结果
     *
     * @param <T>
     */
    @Data
    public static class CallbackResult<T> {

        /**
         * 状态
         */
        private Boolean success;

        /**
         * 结果
         */
        private T result;

    }
}

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * 单线程池
 *
 * @author 周鹏程
 * @date 2021/8/27 17:00
 */
@Slf4j
public final class SyncProcessSingleExecutor {

    private static final Map<String, ExecutorService> EXECUTOR_MAP = Maps.newConcurrentMap();

    private static final String KEY = "def";

    private SyncProcessSingleExecutor() {
    }

    /**
     * 执行器
     *
     * @param r 任务
     */
    public static synchronized void execute(Runnable r) {
        execute(KEY, r);
    }

    /**
     * 执行器
     *
     * @param key 唯一Key
     * @param r   任务
     */
    public static synchronized void execute(String key, Runnable r) {
        if (null == r) {
            return;
        }

        ExecutorService executorService = EXECUTOR_MAP.get(key);
        if (null == executorService) {
            executorService = ThreadUtil.newSingleExecutor();
            EXECUTOR_MAP.put(key, executorService);
        }

        executorService.execute(new TaskWrapper(r));
    }

    /**
     * Task 包装类<br>
     * 此类型的意义是记录可能会被 Executor 吃掉的异常<br>
     */
    private static class TaskWrapper implements Runnable {

        private final Runnable gift;

        public TaskWrapper(final Runnable target) {
            this.gift = target;
        }

        @Override
        public void run() {
            // 捕获异常,避免在 Executor 里面被吞掉了
            if (gift != null) {
                try {
                    gift.run();
                } catch (Exception e) {
                    String errMsg = StrUtil.format("线程池-包装的目标执行异常: {}", e.getMessage());
                    log.error(errMsg, e);
                }
            }
        }
    }
}

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 线程池工厂
 *
 * @date 2021/11/2 10:48
 */
public final class ThreadPoolFactory {

    /**
     * 默认最大并发数<br>
     */
    private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;

    /**
     * 默认线程存活时间
     */
    private static final long DEFAULT_KEEP_ALIVE = 60L;

    /**
     * 默认队列大小
     */
    private static final int DEFAULT_SIZE = 1024;

    /**
     * 线程池名称格式
     */
    private static final String DEFAULT_THREAD_POOL_NAME = "ProcessPool-{}-%d";


    private ThreadPoolFactory() {
    }

    /**
     * 创建默认的线程池
     *
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor createDefThreadPool() {
        return createInitThreadPool(DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT * 4, DEFAULT_KEEP_ALIVE,
                TimeUnit.SECONDS, DEFAULT_SIZE, DEFAULT_THREAD_POOL_NAME, new ThreadPoolExecutor.CallerRunsPolicy());
    }

    /**
     * 创建默认的线程池
     *
     * @param poolName 线程池名称
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor createDefThreadPool(String poolName) {
        return createInitThreadPool(DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT * 4, DEFAULT_KEEP_ALIVE,
                TimeUnit.SECONDS, DEFAULT_SIZE, poolName, new ThreadPoolExecutor.CallerRunsPolicy());
    }

    /**
     * 创建默认的线程池
     *
     * @param maxConcurrent 最大线程数
     * @param poolName      线程池名称
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor createDefThreadPool(int maxConcurrent, String poolName) {
        return createInitThreadPool(maxConcurrent, maxConcurrent * 4, DEFAULT_KEEP_ALIVE,
                TimeUnit.SECONDS, DEFAULT_SIZE, poolName, new ThreadPoolExecutor.CallerRunsPolicy());
    }

    /**
     * 创建线程池
     *
     * @param coreConcurrent 核心线程数
     * @param maxConcurrent  最大线程数
     * @param keepAlive      线程存活时效
     * @param timeUnit       线程存活单位
     * @param queueSize      队列大小
     * @param poolName       线程池名称
     * @param handler        拒绝处理策略
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor createInitThreadPool(final int coreConcurrent,
                                                          final int maxConcurrent,
                                                          final long keepAlive,
                                                          final TimeUnit timeUnit,
                                                          final int queueSize,
                                                          final String poolName,
                                                          final RejectedExecutionHandler handler
    ) {
        return new ThreadPoolExecutor(coreConcurrent, maxConcurrent, keepAlive, timeUnit,
                new LinkedBlockingDeque<>(queueSize),
                new ThreadFactoryBuilder().setNameFormat(poolName).build(),
                handler
        );
    }

}

结果

LogsDto(id=null, type=1, title=com.traffic.admin.api.controller.screen.ScreenController.getSocietyPerson, remoteAddr=, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36, timeout=43926, requestUri=/screen/getSocietyPerson, method=GET, params=[], exception=null, createBy=1, createTime=null, updateBy=1, updateTime=null)

数据库脚本

CREATE TABLE `sys_logs` (
  `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT COMMENT '唯一主键',
  `type` char(1) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '1' COMMENT '日志类型',
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '日志标题',
  `remote_addr` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '操作IP地址',
  `user_agent` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '用户代理',
  `request_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '请求URI',
  `method` varchar(5) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '操作方式',
  `timeout` bigint(20) DEFAULT NULL COMMENT '执行时间',
  `params` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '操作提交的数据',
  `exception` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '异常信息',
  `create_by` bigint(19) DEFAULT NULL COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` bigint(19) DEFAULT NULL COMMENT '修改人',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `update_by_name` varchar(80) DEFAULT NULL COMMENT '修改人',
  `create_by_name` varchar(80) DEFAULT NULL COMMENT '创建人',
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '时间戳',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `sys_log_create_by` (`create_by`) USING BTREE,
  KEY `sys_log_request_uri` (`request_uri`) USING BTREE,
  KEY `sys_log_type` (`type`) USING BTREE,
  KEY `sys_log_create_date` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=147392 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='日志表';

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值