Browse Source

增加公共配置10秒缓存;优化上传接口

tsurumure 5 months ago
parent
commit
aba766771d

+ 9 - 7
db/sys_common.sql

@@ -15,18 +15,20 @@ CREATE TABLE `sys_common` (
     `value_type` VARCHAR(10) DEFAULT 'String' COMMENT '配置值类型 (String, Integer, Float)',
     `value_component` VARCHAR(20) DEFAULT 'text' COMMENT '配置组件 (Text, Textarea, Number, Radio, Checkbox, Select)',
     `value_option` TEXT COMMENT '配置值选项 (除文本框外必填)',
+    `max` INT COMMENT '最大值 (适用于数值类型)',
     `category` VARCHAR(20) NOT NULL COMMENT '分类',
     `sort` INT DEFAULT '1' COMMENT '排序',
     `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';
 
-INSERT INTO sys_common(name, description, tag, value, value_type, value_component, value_option, category, sort) VALUES
-    ('上传存储介质', '使用本地或云存储桶来存放文件', 'UPLOAD_TARGET',  3, 'Integer', 'Radio', '[{\"label\":\"本地\",\"value\":-1,\"disabled\":false},{\"label\":\"腾讯云\",\"value\":1,\"disabled\":false},{\"label\":\"阿里云\",\"value\":2,\"disabled\":true},{\"label\":\"抖音云\",\"value\":3,\"disabled\":false}]', 'UPLOAD', 3),
-    ('单文件上传大小限制(MB)', null, 'UPLOAD_MAX_SIZE_MB', 100, 'Integer', 'Number', null, 'UPLOAD', 2),
-    ('是否启用文件MD5查重', '已存在的文件不再重复上传,仅返回链接', 'UPLOAD_MD5_DUPLICATE', 1, 'Integer', 'Switch', null, 'UPLOAD', 1),
+INSERT INTO sys_common(name, description, tag, value, value_type, value_component, value_option, max, category, sort) VALUES
+    ('上传存储介质', '使用本地或云存储桶来存放文件', 'UPLOAD_TARGET',  3, 'Integer', 'Radio', '[{\"label\":\"本地\",\"value\":-1,\"disabled\":false},{\"label\":\"腾讯云\",\"value\":1,\"disabled\":false},{\"label\":\"阿里云\",\"value\":2,\"disabled\":true},{\"label\":\"抖音云\",\"value\":3,\"disabled\":false}]', null, 'UPLOAD', 4),
+    ('单文件上传大小限制 (MB)', null, 'UPLOAD_MAX_SIZE_MB', 100, 'Integer', 'Number', null, null, 'UPLOAD', 3),
+    ('图片的缩略图宽高尺寸 (px)', '尺寸小于或等于600px', 'UPLOAD_THUMB_SIZE', 40, 'Integer', 'Number', null, 600, 'UPLOAD', 2),
+    ('是否启用文件MD5查重', '已存在的文件不再重复上传,仅返回链接', 'UPLOAD_MD5_DUPLICATE', 1, 'Integer', 'Switch', null, null, 'UPLOAD', 1),
 
-    ('系统用户登录过期时间(小时)', null, 'SYSTEM_USER_LOGIN_DURATION_DEFAULT', 24, 'Integer', 'Number', null, 'SYSTEM_USER', 1),
-    ('是否允许系统用户注册', null, 'SYSTEM_USER_ALLOW_REGISTER', 1, 'Integer', 'Switch', null, 'SYSTEM_USER', 1),
+    ('系统用户登录过期时间(小时)', null, 'SYSTEM_USER_LOGIN_DURATION_DEFAULT', 24, 'Integer', 'Number', null, null, 'SYSTEM_USER', 1),
+    ('是否允许系统用户注册', null, 'SYSTEM_USER_ALLOW_REGISTER', 1, 'Integer', 'Switch', null, null, 'SYSTEM_USER', 1),
 
-    ('移动端用户登录过期时间(小时)', null, 'APP_USER_LOGIN_DURATION_DEFAULT', 168, 'Integer', 'Number', null, 'APP_USER', 1)
+    ('移动端用户登录过期时间(小时)', null, 'APP_USER_LOGIN_DURATION_DEFAULT', 168, 'Integer', 'Number', null, null, 'APP_USER', 1)
 ;

+ 4 - 0
pom.xml

@@ -125,6 +125,10 @@
         </dependency>
 
         <!-- Redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>

+ 44 - 0
src/main/java/com/backendsys/modules/common/config/cache/CacheAspect.java

@@ -0,0 +1,44 @@
+package com.backendsys.modules.common.config.cache;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+@Aspect
+@Component
+public class CacheAspect {
+
+    @Around("@annotation(org.springframework.cache.annotation.Cacheable)")
+    public Object logCacheableMethod(ProceedingJoinPoint joinPoint) throws Throwable {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+
+        // 获取 @Cacheable 注解
+        Cacheable cacheable = method.getAnnotation(Cacheable.class);
+        String cacheName = cacheable.value()[0]; // 获取缓存名称
+        String key = cacheable.key(); // 获取缓存键
+
+        // 获取方法参数
+        Object[] args = joinPoint.getArgs();
+        String keyExpression = key;
+        if (keyExpression.contains("#")) {
+            // 替换表达式中的参数占位符
+            for (int i = 0; i < args.length; i++) {
+                keyExpression = keyExpression.replace("#" + signature.getParameterNames()[i], args[i].toString());
+            }
+        }
+
+        // 执行方法
+        Object result = joinPoint.proceed();
+
+        // 输出日志
+        System.out.println("记录缓存 (" + cacheName + "): { key: " + keyExpression + ", value: " + result + " }");
+
+        return result;
+    }
+}

+ 82 - 0
src/main/java/com/backendsys/modules/common/config/cache/CacheConfig.java

@@ -0,0 +1,82 @@
+package com.backendsys.modules.common.config.cache;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import java.time.Duration;
+
+@Configuration
+@EnableCaching
+public class CacheConfig extends CachingConfigurerSupport {
+
+    @Autowired
+    private RedisConnectionFactory redisConnectionFactory;
+
+    /**
+     * 缓存管理器 (序列化)
+     * 缓存失效时间:10秒
+     */
+    @Bean
+    public CacheManager cacheManager() {
+
+        // 设置缓存时间
+        Duration duration = Duration.ofSeconds(10);
+
+        // 创建自定义的 ObjectMapper
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+
+        // 使用自定义的 ObjectMapper 初始化 Jackson2JsonRedisSerializer
+        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
+
+        // 配置 Redis 缓存管理器
+        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
+            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
+            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
+            .entryTtl(duration); // 设置缓存过期时间
+
+        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();
+    }
+
+//    // 缓存管理器 (未序列化)
+//    @Bean
+//    public CacheManager cacheManager() {
+//        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory);
+//        builder.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)));
+//        return builder.build();
+//    }
+
+    @Bean
+    @Override
+    public KeyGenerator keyGenerator() {
+        return (target, method, params) -> {
+            StringBuilder sb = new StringBuilder();
+            sb.append(target.getClass().getName());
+            sb.append(method.getName());
+            for (Object param : params) {
+                sb.append(param.toString());
+            }
+            return sb.toString();
+        };
+    }
+}

+ 2 - 0
src/main/java/com/backendsys/modules/system/entity/SysCommon.java

@@ -34,8 +34,10 @@ public class SysCommon {
     private String value_component;
 
     private String value_option;
+    private Integer max;
     @Size(max = 20, message = "分类长度不超过 {max} 个字符", groups = { Update.class })
     private String category;
+
     private Integer sort;
     private String create_time;
 

+ 6 - 2
src/main/java/com/backendsys/modules/system/service/impl/SysCommonServiceImpl.java

@@ -10,6 +10,7 @@ import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -54,10 +55,9 @@ public class SysCommonServiceImpl implements SysCommonService {
     }
 
 
-
-
     // 获取系统配置值
     @Override
+    @Cacheable(value = "catch::common", key = "'tag::' + #tag", unless = "#result == null")
     public Object getCommonByTag(String tag) {
         LambdaQueryWrapper<SysCommon> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SysCommon::getTag, tag);
@@ -67,10 +67,14 @@ public class SysCommonServiceImpl implements SysCommonService {
 
     // 获取系统配置值 (按分类)
     @Override
+    @Cacheable(value = "catch::common", key = "'category::' + #category", unless = "#result == null")
     public List<SysCommon> getCommonByCategory(String category) {
+
         LambdaQueryWrapper<SysCommon> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SysCommon::getCategory, category);
         List<SysCommon> sysCommon = sysCommonDao.selectList(wrapper);
+
+
         return sysCommon;
     }
 

+ 30 - 25
src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java

@@ -53,6 +53,29 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
     @Autowired
     private SysCommonService sysCommonService;
 
+    // [方法] 设置缩略图 (参数)
+    // 不同的云环境 (target),缩略图配置也不一样
+    //   -1:本地:
+    //   1:腾讯云: https://cloud.tencent.com/document/product/436/113295
+    //   2:阿里云:
+    //   3:抖音云: https://www.volcengine.com/docs/6349/153626
+    private SysFile setThumbUrl(SysFile sysFile, Integer width, Integer height, String backgroundColor) {
+        // 本地上传
+        if (sysFile.getTarget() == -1) {
+            sysFile.setUrl_thumb(sysFile.getUrl() + "?w=" + width + "&h=" + height);
+        }
+        // 腾讯云 (color值通过base64加密, #f8f8f8)
+        if (sysFile.getTarget() == 1) {
+            System.out.println("base64 encode: " + Base64.encode(backgroundColor));
+            sysFile.setUrl_thumb(sysFile.getUrl() + "?imageMogr2/thumbnail/" + width + "x" + height + "/pad/1/color/" + Base64.encode(backgroundColor));
+        }
+        // 抖音云
+        if (sysFile.getTarget() == 3) {
+            sysFile.setUrl_thumb(sysFile.getUrl() + "?x-tos-process=image/resize,w_" + width + ",h_" + height + ",m_pad,color_" + backgroundColor);
+        }
+        return sysFile;
+    }
+
     /**
      * 获取文件列表
      */
@@ -61,32 +84,9 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
         PageUtils.startPage();  // 分页
         List<SysFile> sysFileList = sysFileDao.selectUploadFileList(sysFile);
 
-        Integer width = 100;
-        Integer height = 100;
-        String backgroundColor = "#f8f8f8";
-
-        // 不同的云环境 (target),缩略图配置也不一样
-        // - -1:本地:
-        // - 1:腾讯云: https://cloud.tencent.com/document/product/436/113295
-        // - 2:阿里云:
-        // - 3:抖音云: https://www.volcengine.com/docs/6349/153626
+        Integer UPLOAD_THUMB_SIZE = Convert.toInt(sysCommonService.getCommonByTag("UPLOAD_THUMB_SIZE"));
         sysFileList = sysFileList.stream().map(item -> {
-
-//            // 本地上传
-//            if (item.getTarget() == -1) {
-//                item.set
-//            }
-
-            // 腾讯云 (color值通过base64加密, #f8f8f8)
-            if (item.getTarget() == 1) {
-                System.out.println("base64 encode: " + Base64.encode(backgroundColor));
-                item.setUrl_thumb(item.getUrl() + "?imageMogr2/thumbnail/" + width + "x" + height + "/pad/1/color/" + Base64.encode(backgroundColor));
-            }
-            // 抖音云
-            if (item.getTarget() == 3) {
-                item.setUrl_thumb(item.getUrl() + "?x-tos-process=image/resize,w_" + width + ",h_" + height + ",m_pad,color_" + backgroundColor);
-            }
-
+            setThumbUrl(item, UPLOAD_THUMB_SIZE, UPLOAD_THUMB_SIZE, "#f8f8f8");
             return item;
         }).collect(Collectors.toList());
 
@@ -178,16 +178,19 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
 
         AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
         AtomicReference<Long> UPLOAD_MAX_SIZE_MB = new AtomicReference<>();
+        AtomicReference<Integer> UPLOAD_THUMB_SIZE = new AtomicReference<>();
         AtomicReference<Boolean> UPLOAD_MD5_DUPLICATE = new AtomicReference<>();
 
         sysCommonList.stream().forEach(sysCommon -> {
             if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
             if (sysCommon.getTag().equals("UPLOAD_MAX_SIZE_MB")) UPLOAD_MAX_SIZE_MB.set(Convert.toLong(sysCommon.getValue()));
+            if (sysCommon.getTag().equals("UPLOAD_THUMB_SIZE")) UPLOAD_THUMB_SIZE.set(Convert.toInt(sysCommon.getValue()));
             if (sysCommon.getTag().equals("UPLOAD_MD5_DUPLICATE")) UPLOAD_MD5_DUPLICATE.set(Convert.toBool(sysCommon.getValue()));
         });
 
         System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
         System.out.println("[系统配置] 单文件上传大小限制(MB): " + UPLOAD_MAX_SIZE_MB);
+        System.out.println("[系统配置] 图片的缩略图宽高尺寸 (px): " + UPLOAD_THUMB_SIZE);
         System.out.println("[系统配置] 是否启用文件MD5查重: " + UPLOAD_MD5_DUPLICATE);
 
         // 判断文件是否超过大小
@@ -220,12 +223,14 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
                     sysFileEntity.setUpdate_time(DateUtil.now());
                     sysFileDao.updateById(sysFileEntity);
                 }
+                sysFileEntity = setThumbUrl(sysFileEntity, UPLOAD_THUMB_SIZE.get(), UPLOAD_THUMB_SIZE.get(), "#f8f8f8");
                 return sysFileEntity;
 
             } else {
                 // - 否
                 // [方法] 上传事件
                 SysFile sysFileEntity = uploadEvent(multipartFile, category_id, UPLOAD_TARGET.get());
+                sysFileEntity = setThumbUrl(sysFileEntity, UPLOAD_THUMB_SIZE.get(), UPLOAD_THUMB_SIZE.get(), "#f8f8f8");
                 return sysFileEntity;
             }
 

+ 4 - 0
src/main/resources/application-local.yml

@@ -46,6 +46,10 @@ spring:
       host: 127.0.0.1
       port: 6388
       password: 123456
+#    cache:
+#      type: redis
+#      redis:
+#        time-to-live: 10000 # 缓存的默认过期时间(毫秒)(与 @Cacheable 缓存时间关联)
 
 
 springdoc:

+ 4 - 0
src/main/resources/application-prod.yml

@@ -46,6 +46,10 @@ spring:
       host: 172.19.0.7
       port: 6388
       password: p1FM!fkfPdBQ%@5o
+#    cache:
+#      type: redis
+#      redis:
+#        time-to-live: 10000 # 缓存的默认过期时间(毫秒)(与 @Cacheable 缓存时间关联)
 
 
 springdoc:

+ 1 - 0
src/main/resources/mapper/system/SysCommonDao.xml

@@ -42,6 +42,7 @@
             <if test="value_type != null and value_type != ''">value_type = #{value_type},</if>
             <if test="value_option != null and value_option != ''">value_option = #{value_option},</if>
             <if test="category != null and category != ''">category = #{category},</if>
+            <if test="max != null and max != ''">max = #{max},</if>
             <if test="sort != null and sort != ''">sort = #{sort},</if>
         </trim>
         WHERE id = #{id};