feat: initial iShare project code

This commit is contained in:
purovps
2026-02-16 23:20:59 +08:00
parent 8c83a6fd46
commit 6f270a972e
1910 changed files with 218015 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>5.2.0</version>
</parent>
<artifactId>pigx-common-audit</artifactId>
<packaging>jar</packaging>
<description>pigx 数据审计相关工具类</description>
<dependencies>
<!-- 数据对比实现 -->
<dependency>
<groupId>org.javers</groupId>
<artifactId>javers-core</artifactId>
</dependency>
<!-- 切面依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 提供日志插入 API -->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>as-upms-api</artifactId>
</dependency>
<!-- 获取上下文的操作用户 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,51 @@
package com.pig4cloud.pigx.common.audit;
import com.pig4cloud.pigx.admin.api.feign.RemoteAuditLogService;
import com.pig4cloud.pigx.common.audit.aop.AuditAspect;
import com.pig4cloud.pigx.common.audit.handle.DefaultAuditLogHandle;
import com.pig4cloud.pigx.common.audit.handle.IAuditLogHandle;
import com.pig4cloud.pigx.common.audit.handle.ICompareHandle;
import com.pig4cloud.pigx.common.audit.handle.JavesCompareHandle;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.Optional;
/**
* 审计自动配置类
*
* @author lengleng
* @date 2023/2/26
*/
@EnableAsync
@AutoConfiguration
@Import({ AuditAspect.class, SpelParser.class })
public class AuditAutoConfiguration {
/**
* 默认注入 javers 的比较器实现
* @param auditNameHandleOptional 注入审计用户来源
* @return ICompareHandle
*/
@Bean
@ConditionalOnMissingBean
public ICompareHandle compareHandle(Optional<IAuditLogHandle> auditNameHandleOptional) {
return new JavesCompareHandle(auditNameHandleOptional);
}
/**
* 默认的审计日志存储策略
* @return DefaultAuditLogHandle
*/
@Bean
@ConditionalOnMissingBean
public IAuditLogHandle auditLogHandle(RemoteAuditLogService logService, KeyStrResolver tenantKeyStrResolver) {
return new DefaultAuditLogHandle(logService, tenantKeyStrResolver);
}
}

View File

@@ -0,0 +1,47 @@
package com.pig4cloud.pigx.common.audit.annotation;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author lengleng
*
* 记需要进行审计的方法
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
/**
* 此审计的名称
* @return 审计名称
*/
String name();
@AliasFor("spel")
String value() default "";
/**
* 默认查询的表达式
* @return string
*/
@AliasFor("value")
String spel() default "";
/**
* 查询原有结果的表达式,如果为空取 spel()
* @return string
*/
String oldVal() default "";
/**
* 查询编辑后的表达式,如果为空取 spel()
* @return string
*/
String newVal() default "";
}

View File

@@ -0,0 +1,36 @@
package com.pig4cloud.pigx.common.audit.aop;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import com.pig4cloud.pigx.common.audit.handle.ICompareHandle;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.StringUtils;
/**
* @author lengleng
* @date 2023/2/26
*
* 拦截被@Audit注解标记的方法并记录前后变化值
*/
@Aspect
@RequiredArgsConstructor
public class AuditAspect {
private final ICompareHandle compareHandle;
@Around("@annotation(audit)")
public Object auditLog(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
// 获取变更之前的结果
Object oldVal = SpelParser.parser(joinPoint,
StringUtils.hasText(audit.oldVal()) ? audit.oldVal() : audit.spel());
Object result = joinPoint.proceed();
compareHandle.compare(oldVal, joinPoint, audit);
return result;
}
}

View File

@@ -0,0 +1,83 @@
package com.pig4cloud.pigx.common.audit.handle;
import com.pig4cloud.pigx.admin.api.entity.SysAuditLog;
import com.pig4cloud.pigx.admin.api.feign.RemoteAuditLogService;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.javers.core.Changes;
import org.javers.core.diff.Change;
import org.javers.core.diff.changetype.ValueChange;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 默认的审计日志存储
*
* @author lengleng
* @date 2023/2/27
*/
@Slf4j
@RequiredArgsConstructor
public class DefaultAuditLogHandle implements IAuditLogHandle {
private final RemoteAuditLogService remoteLogService;
private final KeyStrResolver tenantKeyStrResolver;
@Override
public void handle(Audit audit, Changes changes) {
// 如果变更项为空则不进行审计
if (changes.isEmpty()) {
return;
}
// 获取当前操作人
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 如果获取不到授权信息、或者没有身份信息的接口 直接跳过处理
if (Objects.isNull(authentication) || !authentication.isAuthenticated()) {
return;
}
List<SysAuditLog> auditLogList = new ArrayList<>();
for (Change change : changes) {
ValueChange valueChange = (ValueChange) change;
SysAuditLog auditLog = new SysAuditLog();
auditLog.setAuditName(audit.name());
auditLog.setAuditField(valueChange.getPropertyName()); // 修改的字段名称
if (Objects.nonNull(valueChange.getLeft())) {
auditLog.setBeforeVal(valueChange.getLeft().toString()); // 更改前的值
}
if (Objects.nonNull(valueChange.getRight())) {
auditLog.setAfterVal(valueChange.getRight().toString()); // getRight
}
auditLog.setCreateBy(authentication.getName()); // 操作人
auditLog.setCreateTime(LocalDateTime.now()); // 操作时间
auditLog.setTenantId(Long.parseLong(tenantKeyStrResolver.key())); // 设置操作所属租户
auditLogList.add(auditLog);
}
// 异步保存日志,提升性能
IAuditLogHandle auditLogHandle = SpringContextHolder.getBean(IAuditLogHandle.class);
auditLogHandle.asyncSend(auditLogList);
}
@Async
public void asyncSend(List<SysAuditLog> auditLogList) {
remoteLogService.saveLog(auditLogList, SecurityConstants.FROM_IN);
}
}

View File

@@ -0,0 +1,21 @@
package com.pig4cloud.pigx.common.audit.handle;
import com.pig4cloud.pigx.admin.api.entity.SysAuditLog;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import org.javers.core.Changes;
import java.util.List;
/**
* @author lengleng
* @date 2023/2/26
*
* 审计日志处理器
*/
public interface IAuditLogHandle {
void handle(Audit audit, Changes changes);
void asyncSend(List<SysAuditLog> auditLogList);
}

View File

@@ -0,0 +1,22 @@
package com.pig4cloud.pigx.common.audit.handle;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.javers.core.Changes;
/**
* 比较器抽象类
*
* @author lengleng
* @date 2023/2/26
*/
public interface ICompareHandle {
/**
* 比较两个对象是否变更,及其变更后如何审计
* @param oldVal 原有值
* @param newVal 变更后值
*/
Changes compare(Object oldVal, ProceedingJoinPoint joinPoint, Audit audit);
}

View File

@@ -0,0 +1,37 @@
package com.pig4cloud.pigx.common.audit.handle;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import com.pig4cloud.pigx.common.audit.support.DataAuditor;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.javers.core.Changes;
import org.springframework.util.StringUtils;
import java.util.Optional;
/**
* javers 比较实现
*
* @author lengleng
* @date 2023/2/26
*/
@RequiredArgsConstructor
public class JavesCompareHandle implements ICompareHandle {
private final Optional<IAuditLogHandle> auditLogHandleOptional;
/**
* 比较两个对象是否变更,及其变更后如何审计
* @param oldVal 原有值
*/
@Override
public Changes compare(Object oldVal, ProceedingJoinPoint joinPoint, Audit audit) {
Object newVal = SpelParser.parser(joinPoint,
StringUtils.hasText(audit.newVal()) ? audit.newVal() : audit.spel());
Changes compare = DataAuditor.compare(oldVal, newVal);
auditLogHandleOptional.ifPresent(handle -> handle.handle(audit, compare));
return compare;
}
}

View File

@@ -0,0 +1,27 @@
package com.pig4cloud.pigx.common.audit.support;
import lombok.experimental.UtilityClass;
import org.javers.core.Changes;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Diff;
import static org.javers.core.diff.ListCompareAlgorithm.LEVENSHTEIN_DISTANCE;
/**
* javers 审计工具
*
* @author lengleng
* @date 2023/2/26
*/
@UtilityClass
public class DataAuditor {
private final Javers javers = JaversBuilder.javers().withListCompareAlgorithm(LEVENSHTEIN_DISTANCE).build();
public Changes compare(Object newObj, Object oldObj) {
Diff compare = javers.compare(newObj, oldObj);
return compare.getChanges();
}
}

View File

@@ -0,0 +1,39 @@
package com.pig4cloud.pigx.common.audit.support;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* 表达式处理器
*
* @author lengleng
* @date 2023/2/26
*/
public class SpelParser implements ApplicationContextAware {
private final static SpelExpressionParser parser = new SpelExpressionParser();
private static ApplicationContext applicationContext;
public static Object parser(ProceedingJoinPoint joinPoint, String spel) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
for (int i = 0; i < joinPoint.getArgs().length; i++) {
String paramName = ((MethodSignature) joinPoint.getSignature()).getParameterNames()[i];
context.setVariable(paramName, joinPoint.getArgs()[i]);
}
return parser.parseExpression(spel).getValue(context);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpelParser.applicationContext = applicationContext;
}
}

View File

@@ -0,0 +1 @@
com.pig4cloud.pigx.common.audit.AuditAutoConfiguration