疯狂的狮子Li
2021-06-04 2027dae30b82365a84988d057c2a9240d233971c
!49 同步dev分支
Merge pull request !49 from 疯狂的狮子Li/dev
已修改6个文件
已添加3个文件
259 ■■■■■ 文件已修改
pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/vo.java.vm 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/package.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -13,7 +13,7 @@
    <description>RuoYi-Vue-Plus后台管理系统</description>
    <properties>
        <ruoyi-vue-plus.version>2.3.0</ruoyi-vue-plus.version>
        <ruoyi-vue-plus.version>2.3.1</ruoyi-vue-plus.version>
        <spring-boot.version>2.3.11.RELEASE</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
ruoyi-admin/src/main/resources/application.yml
@@ -8,8 +8,8 @@
  copyrightYear: 2021
  # å®žä¾‹æ¼”示开关
  demoEnabled: true
  # æ–‡ä»¶è·¯å¾„,使用jvm系统变量,兼容windows和linux;
  profile: ${user.dir}/ruoyi/uploadPath
  # æ–‡ä»¶è·¯å¾„
  profile: ./ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: false
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * åˆ†å¸ƒå¼é”ï¼ˆæ³¨è§£æ¨¡å¼ï¼Œä¸æŽ¨èä½¿ç”¨ï¼Œæœ€å¥½ç”¨é”çš„工具类)
 *
 * @author shenxinquan
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
    /**
     * é”è¿‡æœŸæ—¶é—´ é»˜è®¤30秒
     */
    int expireTime() default 30;
    /**
     * é”key值
     */
    String key() default "redisLockKey";
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
@@ -43,9 +43,9 @@
    public static final int DEFAULT_PAGE_NUM = 1;
    /**
     * æ¯é¡µæ˜¾ç¤ºè®°å½•æ•° é»˜è®¤å€¼
     * æ¯é¡µæ˜¾ç¤ºè®°å½•æ•° é»˜è®¤å€¼ é»˜è®¤æŸ¥å…¨éƒ¨
     */
    public static final int DEFAULT_PAGE_SIZE = 10;
    public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
    /**
     * æž„建 plus åˆ†é¡µå¯¹è±¡
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.RedisLock;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æµ‹è¯•分布式锁的样例
 *
 * @author shenxinquan
 */
@RestController
@RequestMapping("/demo/redisLock")
public class RedisLockController {
    /**
     * #p0 æ ‡è¯†å–第一个参数为redis锁的key
     */
    @GetMapping("/getLock")
    @RedisLock(expireTime = 10, key = "#p0")
    public AjaxResult<String> getLock(String key, String value) {
        try {
            // åŒæ—¶è¯·æ±‚排队
//            Thread.sleep(5000);
            // é”è¶…时测试
            Thread.sleep(11000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return AjaxResult.success("操作成功",value);
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.RedisLock;
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.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * åˆ†å¸ƒå¼é”ï¼ˆæ³¨è§£å®žçŽ°ç‰ˆæœ¬ï¼‰
 *
 * @author shenxinquan
 */
@Slf4j
@Aspect
@Order(9)
@Component
public class RedisLockAspect {
    @Autowired
    private RedissonClient redissonClient;
    private static final String LOCK_TITLE = "RedisLock_";
    @Pointcut("@annotation(com.ruoyi.common.annotation.RedisLock)")
    public void annotationPointcut() {
    }
    @Around("annotationPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // èŽ·å¾—å½“å‰è®¿é—®çš„class
        Class<?> className = joinPoint.getTarget().getClass();
        // èŽ·å¾—è®¿é—®çš„æ–¹æ³•å
        String methodName = joinPoint.getSignature().getName();
        // å¾—到方法的参数的类型
        Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Object[] args = joinPoint.getArgs();
        String key = "";
        // é»˜è®¤30秒过期时间
        int expireTime = 30;
        try {
            // å¾—到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            method.setAccessible(true);
            // åˆ¤æ–­æ˜¯å¦å­˜åœ¨@RedisLock注解
            if (method.isAnnotationPresent(RedisLock.class)) {
                RedisLock annotation = method.getAnnotation(RedisLock.class);
                key = getRedisKey(args, annotation.key());
                expireTime = getExpireTime(annotation);
            }
        } catch (Exception e) {
            throw new RuntimeException("redis分布式锁注解参数异常", e);
        }
        Object res;
        try {
            if (acquire(key, expireTime, TimeUnit.SECONDS)) {
                try {
                    res = joinPoint.proceed();
                    return res;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    release(key);
                }
            } else {
                throw new RuntimeException("redis分布式锁注解参数异常");
            }
        } catch (IllegalMonitorStateException e) {
            log.error("lock timeout => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
            throw new RuntimeException("lock timeout => key : " + key);
        } catch (Exception e) {
            throw new Exception("redis分布式未知异常", e);
        }
    }
    private int getExpireTime(RedisLock annotation) {
        return annotation.expireTime();
    }
    private String getRedisKey(Object[] args, String primalKey) {
        if (args.length == 0) {
            return primalKey;
        }
        // èŽ·å–#p0...集合
        List<String> keyList = getKeyParsList(primalKey);
        for (String keyName : keyList) {
            int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
            Object parValue = args[keyIndex];
            primalKey = primalKey.replace(keyName, String.valueOf(parValue));
        }
        return primalKey.replace("+", "").replace("'", "");
    }
    /**
     * èŽ·å–key中#p0中的参数名称
     */
    private static List<String> getKeyParsList(String key) {
        List<String> listPar = new ArrayList<>();
        if (key.contains("#")) {
            int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
            int indexNext = 0;
            String parName;
            int indexPre = key.indexOf("#");
            if (plusIndex > 0) {
                indexNext = key.indexOf("#") + plusIndex;
                parName = key.substring(indexPre, indexNext);
            } else {
                parName = key.substring(indexPre);
            }
            listPar.add(parName.trim());
            key = key.substring(indexNext + 1);
            if (key.contains("#")) {
                listPar.addAll(getKeyParsList(key));
            }
        }
        return listPar;
    }
    /**
     * åŠ é”ï¼ˆRLock)带超时时间的
     */
    private boolean acquire(String key, long expire, TimeUnit expireUnit) {
        //声明key对象
        key = LOCK_TITLE + key;
        try {
            //获取锁对象
            RLock mylock = redissonClient.getLock(key);
            //加锁,并且设置锁过期时间,防止死锁的产生
            mylock.tryLock(expire, expire, expireUnit);
        } catch (InterruptedException e) {
            return false;
        }
        log.info("lock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
        //加锁成功
        return true;
    }
    /**
     * é”çš„释放
     */
    private void release(String lockName) {
        //必须是和加锁时的同一个key
        String key = LOCK_TITLE + lockName;
        //获取所对象
        RLock mylock = redissonClient.getLock(key);
        //释放锁(解锁)
        mylock.unlock();
        log.info("unlock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
    }
}
ruoyi-generator/src/main/resources/vm/java/vo.java.vm
@@ -29,7 +29,7 @@
    private ${pkColumn.javaType} ${pkColumn.javaField};
#foreach ($column in $columns)
#if($column.isList)
#if($column.isList && $column.isPk!=1)
    /** $column.columnComment */
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
ruoyi-ui/package.json
@@ -1,6 +1,6 @@
{
  "name": "ruoyi-vue-plus",
  "version": "2.3.0",
  "version": "2.3.1",
  "description": "RuoYi-Vue-Plus后台管理系统",
  "author": "LionLi",
  "license": "MIT",
ruoyi-ui/src/views/index.vue
@@ -80,6 +80,18 @@
            <span>更新日志</span>
          </div>
          <el-collapse accordion>
            <el-collapse-item title="v2.3.1 - 2021-6-4">
              <ol>
                <li>add å¢žåŠ  redisson åˆ†å¸ƒå¼é” æ³¨è§£ä¸Ždemo案例</li>
                <li>add å¢žåŠ  Oracle åˆ†æ”¯</li>
                <li>update ä¼˜åŒ– redis ç©ºå¯†ç å…¼å®¹æ€§</li>
                <li>update ä¼˜åŒ–前端代码生成按钮增加 loading</li>
                <li>fix ä¿®å¤ redisson ä¸èƒ½æ‰¹é‡åˆ é™¤çš„bug</li>
                <li>fix ä¿®å¤è¡¨å•构建选择下拉选择控制台报错问题</li>
                <li>fix ä¿®å¤ vo ä»£ç ç”Ÿæˆ ä¸»é”®åˆ—表显示 é‡å¤ç”Ÿæˆbug</li>
                <li>fix ä¿®å¤ä¸Šä¼ è·¯å¾„ win æ‰“包编译为 win è·¯å¾„, linux æŠ¥é”™bug</li>
              </ol>
            </el-collapse-item>
            <el-collapse-item title="v2.3.0 - 2021-6-1">
              <ol>
                <li>add å‡çº§ luttuce ä¸º redisson æ€§èƒ½æ›´å¼º å·¥å…·æ›´å…¨</li>
@@ -195,12 +207,14 @@
</template>
<script>
import config from '../../package.json'
export default {
  name: "index",
  data() {
    return {
      // ç‰ˆæœ¬å·
      version: "2.3.0",
      version: config.version,
    };
  },
  methods: {