package com.ruoyi.framework.aspectj;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.ruoyi.common.annotation.UniqueCheck;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
import org.aspectj.lang.JoinPoint;
|
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Before;
|
import org.aspectj.lang.annotation.Pointcut;
|
import org.aspectj.lang.reflect.MethodSignature;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.core.annotation.Order;
|
import org.springframework.stereotype.Component;
|
|
import java.lang.reflect.Field;
|
import java.lang.reflect.Method;
|
import java.util.List;
|
|
/**
|
* 唯一性校验切面
|
* 用于在新增操作前进行字段唯一性检查
|
*
|
* @author ruoyi
|
*/
|
@Aspect
|
@Order(2)
|
@Component
|
public class UniqueCheckAspect
|
{
|
protected Logger logger = LoggerFactory.getLogger(getClass());
|
|
@Pointcut("@annotation(com.ruoyi.common.annotation.UniqueCheck)")
|
public void uniqueCheckPointCut()
|
{
|
}
|
|
@Before("uniqueCheckPointCut()")
|
public void doBefore(JoinPoint point) throws Throwable
|
{
|
handleUniqueCheck(point);
|
}
|
|
protected void handleUniqueCheck(JoinPoint joinPoint) throws Exception
|
{
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
Method method = signature.getMethod();
|
UniqueCheck uniqueCheck = method.getAnnotation(UniqueCheck.class);
|
|
if (uniqueCheck == null)
|
{
|
return;
|
}
|
|
// 获取方法参数
|
Object[] args = joinPoint.getArgs();
|
if (args == null || args.length == 0)
|
{
|
return;
|
}
|
|
// 获取第一个参数(通常是实体对象)
|
Object entity = args[0];
|
if (entity == null)
|
{
|
return;
|
}
|
|
// 校验实体类型
|
Class<?> entityClass = uniqueCheck.entityClass();
|
if (!entityClass.isInstance(entity))
|
{
|
logger.warn("实体类型不匹配, 期望: {}, 实际: {}", entityClass.getName(), entity.getClass().getName());
|
return;
|
}
|
|
// 获取Service实例
|
Class<?> serviceClass = uniqueCheck.serviceClass();
|
IService service = (IService) SpringUtils.getBean(serviceClass);
|
|
// 获取需要检查的字段
|
String[] fields = uniqueCheck.fields();
|
if (fields == null || fields.length == 0)
|
{
|
throw new ServiceException("UniqueCheck注解的fields属性不能为空");
|
}
|
|
// 构建查询条件
|
QueryWrapper queryWrapper = Wrappers.query();
|
StringBuilder messageBuilder = new StringBuilder();
|
boolean hasCondition = false;
|
|
for (int i = 0; i < fields.length; i++)
|
{
|
String fieldName = fields[i];
|
Object fieldValue = getFieldValue(entity, fieldName);
|
|
// 只有字段值不为空时才添加查询条件
|
if (fieldValue != null && !"".equals(fieldValue))
|
{
|
// 使用字符串字段名,MyBatis-Plus会自动转换为数据库列名
|
queryWrapper.eq(StringUtils.toUnderScoreCase(fieldName), fieldValue);
|
hasCondition = true;
|
|
if (messageBuilder.length() > 0)
|
{
|
messageBuilder.append(",");
|
}
|
messageBuilder.append(fieldValue);
|
}
|
}
|
|
// 如果没有有效的查询条件,直接返回
|
if (!hasCondition)
|
{
|
return;
|
}
|
|
// 执行查询
|
List list = service.list(queryWrapper);
|
if (list != null && !list.isEmpty())
|
{
|
String message = uniqueCheck.message();
|
// 简单的消息格式化
|
if (message.contains("{value0}"))
|
{
|
message = message.replace("{value0}", messageBuilder.toString());
|
}
|
|
logger.warn("唯一性校验失败: {}", message);
|
throw new ServiceException(message);
|
}
|
}
|
|
/**
|
* 通过反射获取实体字段值
|
*/
|
private Object getFieldValue(Object entity, String fieldName) throws Exception
|
{
|
Class<?> clazz = entity.getClass();
|
|
// 尝试获取字段
|
Field field = null;
|
while (clazz != null && field == null)
|
{
|
try
|
{
|
field = clazz.getDeclaredField(fieldName);
|
}
|
catch (NoSuchFieldException e)
|
{
|
clazz = clazz.getSuperclass();
|
}
|
}
|
|
if (field == null)
|
{
|
throw new ServiceException("实体类中不存在字段: " + fieldName);
|
}
|
|
field.setAccessible(true);
|
return field.get(entity);
|
}
|
}
|