liusheng
7 天以前 896d14b328059863b5cc668dfc6c1d375f59de59
代码提交
已添加2个文件
209 ■■■■■ 文件已修改
ruoyi-common/src/main/java/com/ruoyi/common/annotation/UniqueCheck.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/UniqueCheckAspect.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/UniqueCheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
 * è‡ªå®šä¹‰æ³¨è§£ -- å”¯ä¸€æ€§æ ¡éªŒ(去重)
 * ç”¨äºŽåœ¨æ–°å¢žæ“ä½œå‰æ£€æŸ¥æŒ‡å®šå­—段是否已存在
 *
 * @author ruoyi
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UniqueCheck
{
    /**
     * éœ€è¦æ£€æŸ¥å”¯ä¸€æ€§çš„字段名称
     * æ”¯æŒå¤šä¸ªå­—段联合去重,如: {"infoid", "itemNo"}
     */
    String[] fields();
    /**
     * å®žä½“类的Class对象
     * ç”¨äºŽåå°„获取字段值和调用查询方法
     */
    Class<?> entityClass();
    /**
     * Service接口的Class对象
     * ç”¨äºŽè°ƒç”¨queryList或list方法进行数据库查询
     */
    Class<?> serviceClass();
    /**
     * é”™è¯¯æç¤ºæ¶ˆæ¯
     * æ”¯æŒå ä½ç¬¦ {0}, {1}... å¯¹åº”fields数组中的字段名
     * æ”¯æŒå ä½ç¬¦ {value0}, {value1}... å¯¹åº”fields数组中的字段值
     */
    String message() default "数据已存在,不能重复添加!";
}
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/UniqueCheckAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
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);
    }
}