12 changed files with 305 additions and 0 deletions
@ -0,0 +1,16 @@ |
|||
package com.project.operation.annotation; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 自定义操作日志注解 |
|||
*/ |
|||
@Target({ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface OperationLog { |
|||
//操作模块
|
|||
String module() default ""; |
|||
//操作描述
|
|||
String description() default ""; |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
package com.project.operation.aop; |
|||
|
|||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; |
|||
import com.project.base.domain.result.Result; |
|||
import com.project.ding.domain.dto.LoginDTO; |
|||
import com.project.ding.utils.JwtUtils; |
|||
import com.project.operation.annotation.OperationLog; |
|||
import com.project.operation.application.impl.OperationLogApplicationService; |
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.aspectj.lang.ProceedingJoinPoint; |
|||
import org.aspectj.lang.annotation.AfterReturning; |
|||
import org.aspectj.lang.annotation.Around; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Pointcut; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import java.lang.reflect.Method; |
|||
|
|||
/** |
|||
* 操作日志AOP切面 |
|||
*/ |
|||
@Slf4j |
|||
@Aspect |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class OperationLogAspect { |
|||
|
|||
@Autowired |
|||
private JwtUtils jwtUtils; |
|||
@Autowired |
|||
private OperationLogApplicationService operationLogApplicationService; |
|||
|
|||
private final ThreadLocal<OperationLogDTO> operationLogDTOThreadLocal = new ThreadLocal<>(); |
|||
|
|||
//定义切点,拦截所有添加@OperationLog注解的方法
|
|||
@Pointcut("@annotation(com.project.operation.annotation.OperationLog)") |
|||
public void pointcutOperationLog() {} |
|||
|
|||
//环绕通知,在方法执行前后拦截,收集日志
|
|||
@Around("pointcutOperationLog()") |
|||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable { |
|||
//获取开始时间
|
|||
long startTime = System.currentTimeMillis(); |
|||
|
|||
OperationLogDTO operationLogDTO = new OperationLogDTO(); |
|||
//获取客户端IP和操作用户
|
|||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
|||
if (requestAttributes != null) { |
|||
HttpServletRequest request = requestAttributes.getRequest(); |
|||
operationLogDTO.setClientIp(request.getRemoteAddr()); |
|||
|
|||
String header = request.getHeader("Authorization"); |
|||
if (StringUtils.isNotBlank(header) && header.startsWith("Bearer ")) { |
|||
String token = header.substring(7); |
|||
String userId = jwtUtils.parseToken(token).getSubject(); |
|||
operationLogDTO.setCreatorId(Long.parseLong(userId)); |
|||
} |
|||
} |
|||
|
|||
//获取方法信息
|
|||
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
|||
Method method = signature.getMethod(); |
|||
// 方法全路径:包名+类名+方法名
|
|||
operationLogDTO.setMethod(method.getDeclaringClass().getName() + "." + method.getName()); |
|||
|
|||
//获取操作信息
|
|||
OperationLog annotation = method.getAnnotation(OperationLog.class); |
|||
operationLogDTO.setModule(annotation.module()); |
|||
operationLogDTO.setDescription(annotation.description()); |
|||
|
|||
|
|||
Object businessResult; |
|||
try { |
|||
operationLogDTOThreadLocal.set(operationLogDTO); |
|||
businessResult = joinPoint.proceed(); |
|||
operationLogDTO.setResult(0); |
|||
}catch (Exception e){ |
|||
operationLogDTO.setResult(1); |
|||
operationLogDTO.setException(e.getMessage()); |
|||
throw e; |
|||
}finally { |
|||
operationLogDTO.setCostTime(System.currentTimeMillis() - startTime); |
|||
//保存日志
|
|||
operationLogApplicationService.saveOperationLog(operationLogDTO); |
|||
} |
|||
|
|||
return businessResult; |
|||
} |
|||
|
|||
@AfterReturning(returning = "result",pointcut = "pointcutOperationLog()") |
|||
public void afterReturning(Object result) throws Throwable { |
|||
OperationLogDTO operationLogDTO = operationLogDTOThreadLocal.get(); |
|||
if (operationLogDTO == null) { |
|||
return; // 无上下文,直接返回
|
|||
} |
|||
try { |
|||
//获取当前登陆用户
|
|||
if(operationLogDTO.getMethod().contains("getToken") && result instanceof Result){ |
|||
LoginDTO data = ((Result<LoginDTO>) result).getData(); |
|||
String userId = jwtUtils.parseToken(data.getToken()).getSubject(); |
|||
operationLogDTO.setCreatorId(Long.parseLong(userId)); |
|||
} |
|||
}finally { |
|||
operationLogDTOThreadLocal.remove(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package com.project.operation.application; |
|||
|
|||
import com.project.operation.application.impl.OperationLogApplicationService; |
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
import com.project.operation.domain.service.OperationLogBaseService; |
|||
import com.project.operation.domain.service.SaveOperationLogDomainService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class OperationLogApplicationServiceImpl implements OperationLogApplicationService { |
|||
|
|||
@Autowired |
|||
private SaveOperationLogDomainService saveOperationLogDomainService; |
|||
|
|||
/** |
|||
* 保存日志 |
|||
*/ |
|||
@Override |
|||
public void saveOperationLog(OperationLogDTO operationLogDTO) { |
|||
saveOperationLogDomainService.saveOperationLog(operationLogDTO); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
package com.project.operation.application.impl; |
|||
|
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
|
|||
public interface OperationLogApplicationService { |
|||
/** |
|||
* 保存操作日志 |
|||
*/ |
|||
void saveOperationLog(OperationLogDTO operationLogDTO); |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
package com.project.operation.domain.dto; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.project.base.domain.dto.BaseDTO; |
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Id; |
|||
import lombok.Data; |
|||
import org.hibernate.annotations.Comment; |
|||
|
|||
@Data |
|||
public class OperationLogDTO extends BaseDTO { |
|||
private Long id; |
|||
|
|||
private String module; |
|||
|
|||
private String description; |
|||
|
|||
private Integer result; |
|||
|
|||
private String method; |
|||
|
|||
private String exception; |
|||
|
|||
private Long costTime; |
|||
|
|||
private String clientIp; |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
package com.project.operation.domain.entity; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import com.project.base.domain.entity.BaseEntity; |
|||
import jakarta.persistence.*; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.Comment; |
|||
|
|||
@Data |
|||
@Table(name = "evaluator_operation_log") |
|||
@Entity |
|||
@TableName(value = "evaluator_operation_log") |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class OperationLogEntity extends BaseEntity { |
|||
@TableId(value = "id" , type = IdType.ASSIGN_ID) |
|||
@Id |
|||
private Long id; |
|||
|
|||
@Column(name = "module", columnDefinition = "varchar(255) comment '操作模块'") |
|||
private String module; |
|||
|
|||
@Column(name = "description" , columnDefinition="varchar(255) comment '操作描述'") |
|||
private String description; |
|||
|
|||
/** 操作结果 */ |
|||
@Column(name = "result") |
|||
@Comment("操作结果,0-成功, 1-失败") |
|||
private Integer result; |
|||
|
|||
@Column(name = "method", columnDefinition="varchar(255) comment '操作方法路径'") |
|||
private String method; |
|||
|
|||
@Column(name = "exception", columnDefinition="varchar(255) comment '异常信息(失败时记录)'") |
|||
private String exception; |
|||
|
|||
/** 操作耗时(毫秒) */ |
|||
@Column(name = "cost_time") |
|||
@Comment("操作耗时(毫秒)") |
|||
@TableField("cost_time") |
|||
private Long costTime; |
|||
|
|||
@Column(name = "client_ip", columnDefinition="varchar(100) comment '客户端IP'") |
|||
@TableField("client_ip") |
|||
private String clientIp; |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
package com.project.operation.domain.service; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
import com.project.operation.domain.entity.OperationLogEntity; |
|||
|
|||
public interface OperationLogBaseService extends IService<OperationLogEntity> { |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
package com.project.operation.domain.service; |
|||
|
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
|
|||
public interface SaveOperationLogDomainService { |
|||
/** |
|||
* 保存操作日志 |
|||
*/ |
|||
void saveOperationLog(OperationLogDTO operationLogDTO); |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package com.project.operation.domain.service.impl; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
import com.project.operation.domain.entity.OperationLogEntity; |
|||
import com.project.operation.domain.service.OperationLogBaseService; |
|||
import com.project.operation.mapper.OperationLogMapper; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class OperationLogBaseServiceImpl extends ServiceImpl<OperationLogMapper, OperationLogEntity> implements OperationLogBaseService { |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
package com.project.operation.domain.service.impl; |
|||
|
|||
import com.project.operation.domain.dto.OperationLogDTO; |
|||
import com.project.operation.domain.entity.OperationLogEntity; |
|||
import com.project.operation.domain.service.OperationLogBaseService; |
|||
import com.project.operation.domain.service.SaveOperationLogDomainService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class SaveOperationLogDomainServiceImpl implements SaveOperationLogDomainService { |
|||
|
|||
@Autowired |
|||
private OperationLogBaseService operationLogBaseService; |
|||
|
|||
@Override |
|||
public void saveOperationLog(OperationLogDTO operationLogDTO) { |
|||
OperationLogEntity entity = operationLogDTO.toEntity(OperationLogEntity::new); |
|||
operationLogBaseService.save(entity); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
package com.project.operation.mapper; |
|||
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.project.operation.domain.entity.OperationLogEntity; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
@Mapper |
|||
public interface OperationLogMapper extends BaseMapper<OperationLogEntity> { |
|||
} |
|||
Loading…
Reference in new issue