From 668c62bc73ca587d669bffa01e3fd2f6caedd663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B7=E5=86=B7?= Date: Wed, 3 Oct 2018 23:36:43 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=B7=BB=E5=8A=A0=E6=96=B0?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E3=80=82=20=E6=89=A9=E5=B1=95spring=20cache?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81ttl=20=E5=92=8C=E8=87=AA=E5=8A=A8=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pigx-common/pigx-common-cache/pom.xml | 49 ++++ .../common/cache/DefaultRedisCacheWriter.java | 275 ++++++++++++++++++ .../common/cache/RedisAutoCacheManager.java | 72 +++++ .../cache/RedisCacheAutoConfiguration.java | 112 +++++++ .../common/cache/RedisCacheManagerConfig.java | 43 +++ .../pigx/common/cache/RedisKeySerializer.java | 74 +++++ .../common/cache/RedisTemplateConfig.java | 54 ++++ .../META-INF/spring-devtools.properties | 1 + .../main/resources/META-INF/spring.factories | 5 + .../pigx/common/core/config/RedisConfig.java | 77 ----- .../main/resources/META-INF/spring.factories | 1 - pigx-common/pom.xml | 1 + pigx-upms/pigx-upms-biz/pom.xml | 6 + .../pig4cloud/pigx/act/entity/LeaveBill.java | 22 +- .../act/service/impl/EditorServiceImpl.java | 3 + .../act/service/impl/ModelServiceImpl.java | 7 +- .../act/service/impl/ProcessServiceImpl.java | 3 +- 17 files changed, 717 insertions(+), 88 deletions(-) create mode 100644 pigx-common/pigx-common-cache/pom.xml create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/DefaultRedisCacheWriter.java create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisAutoCacheManager.java create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheAutoConfiguration.java create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheManagerConfig.java create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisKeySerializer.java create mode 100644 pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisTemplateConfig.java create mode 100644 pigx-common/pigx-common-cache/src/main/resources/META-INF/spring-devtools.properties create mode 100644 pigx-common/pigx-common-cache/src/main/resources/META-INF/spring.factories delete mode 100644 pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/RedisConfig.java diff --git a/pigx-common/pigx-common-cache/pom.xml b/pigx-common/pigx-common-cache/pom.xml new file mode 100644 index 000000000..db9e4e73b --- /dev/null +++ b/pigx-common/pigx-common-cache/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + com.pig4cloud + pigx-common + ${pigx.version} + + + pigx-common-cache + jar + + pigx 缓存工具类 + + + + + + com.pig4cloud + pigx-common-security + ${pigx.version} + + + + org.springframework.boot + spring-boot-starter-data-redis + + + diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/DefaultRedisCacheWriter.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/DefaultRedisCacheWriter.java new file mode 100644 index 000000000..b8be1f8b6 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/DefaultRedisCacheWriter.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; + +import org.springframework.dao.PessimisticLockingFailureException; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStringCommands.SetOption; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * {@link RedisCacheWriter} implementation capable of reading/writing binary data from/to Redis in {@literal standalone} + * and {@literal cluster} environments. Works upon a given {@link RedisConnectionFactory} to obtain the actual + * {@link RedisConnection}.
+ * {@link DefaultRedisCacheWriter} can be used in + * {@link RedisCacheWriter#lockingRedisCacheWriter(RedisConnectionFactory) locking} or + * {@link RedisCacheWriter#nonLockingRedisCacheWriter(RedisConnectionFactory) non-locking} mode. While + * {@literal non-locking} aims for maximum performance it may result in overlapping, non atomic, command execution for + * operations spanning multiple Redis interactions like {@code putIfAbsent}. The {@literal locking} counterpart prevents + * command overlap by setting an explicit lock key and checking against presence of this key which leads to additional + * requests and potential command wait times. + * + * @author Christoph Strobl + * @author Mark Paluch + * @since 2.0 + */ +class DefaultRedisCacheWriter implements RedisCacheWriter { + + private final RedisConnectionFactory connectionFactory; + private final Duration sleepTime; + + /** + * @param connectionFactory must not be {@literal null}. + */ + DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory) { + this(connectionFactory, Duration.ZERO); + } + + /** + * @param connectionFactory must not be {@literal null}. + * @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO} + * to disable locking. + */ + private DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) { + + Assert.notNull(connectionFactory, "ConnectionFactory must not be null!"); + Assert.notNull(sleepTime, "SleepTime must not be null!"); + + this.connectionFactory = connectionFactory; + this.sleepTime = sleepTime; + } + + @Override + public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) { + + Assert.notNull(name, "Name must not be null!"); + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(value, "Value must not be null!"); + + execute(name, connection -> { + + if (shouldExpireWithin(ttl)) { + connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert()); + } else { + connection.set(key, value); + } + + return "OK"; + }); + } + + + @Override + public byte[] get(String name, byte[] key) { + + Assert.notNull(name, "Name must not be null!"); + Assert.notNull(key, "Key must not be null!"); + + return execute(name, connection -> connection.get(key)); + } + + + @Override + public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) { + + Assert.notNull(name, "Name must not be null!"); + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(value, "Value must not be null!"); + + return execute(name, connection -> { + + if (isLockingCacheWriter()) { + doLock(name, connection); + } + + try { + if (connection.setNX(key, value)) { + + if (shouldExpireWithin(ttl)) { + connection.pExpire(key, ttl.toMillis()); + } + return null; + } + + return connection.get(key); + } finally { + + if (isLockingCacheWriter()) { + doUnlock(name, connection); + } + } + }); + } + + + @Override + public void remove(String name, byte[] key) { + + Assert.notNull(name, "Name must not be null!"); + Assert.notNull(key, "Key must not be null!"); + + execute(name, connection -> connection.del(key)); + } + + + @Override + public void clean(String name, byte[] pattern) { + + Assert.notNull(name, "Name must not be null!"); + Assert.notNull(pattern, "Pattern must not be null!"); + + execute(name, connection -> { + + boolean wasLocked = false; + + try { + + if (isLockingCacheWriter()) { + doLock(name, connection); + wasLocked = true; + } + + byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet()) + .toArray(new byte[0][]); + + if (keys.length > 0) { + connection.del(keys); + } + } finally { + + if (wasLocked && isLockingCacheWriter()) { + doUnlock(name, connection); + } + } + + return "OK"; + }); + } + + /** + * Explicitly set a write lock on a cache. + * + * @param name the name of the cache to lock. + */ + void lock(String name) { + execute(name, connection -> doLock(name, connection)); + } + + /** + * Explicitly remove a write lock from a cache. + * + * @param name the name of the cache to unlock. + */ + void unlock(String name) { + executeLockFree(connection -> doUnlock(name, connection)); + } + + private Boolean doLock(String name, RedisConnection connection) { + return connection.setNX(createCacheLockKey(name), new byte[0]); + } + + private Long doUnlock(String name, RedisConnection connection) { + return connection.del(createCacheLockKey(name)); + } + + boolean doCheckLock(String name, RedisConnection connection) { + return connection.exists(createCacheLockKey(name)); + } + + /** + * @return {@literal true} if {@link RedisCacheWriter} uses locks. + */ + private boolean isLockingCacheWriter() { + return !sleepTime.isZero() && !sleepTime.isNegative(); + } + + private T execute(String name, Function callback) { + + RedisConnection connection = connectionFactory.getConnection(); + try { + + checkAndPotentiallyWaitUntilUnlocked(name, connection); + return callback.apply(connection); + } finally { + connection.close(); + } + } + + private void executeLockFree(Consumer callback) { + + RedisConnection connection = connectionFactory.getConnection(); + + try { + callback.accept(connection); + } finally { + connection.close(); + } + } + + private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) { + + if (!isLockingCacheWriter()) { + return; + } + + try { + + while (doCheckLock(name, connection)) { + Thread.sleep(sleepTime.toMillis()); + } + } catch (InterruptedException ex) { + + // Re-interrupt current thread, to allow other participants to react. + Thread.currentThread().interrupt(); + + throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name), + ex); + } + } + + private static boolean shouldExpireWithin(@Nullable Duration ttl) { + return ttl != null && !ttl.isZero() && !ttl.isNegative(); + } + + private static byte[] createCacheLockKey(String name) { + return (name + "~lock").getBytes(StandardCharsets.UTF_8); + } +} + diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisAutoCacheManager.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisAutoCacheManager.java new file mode 100644 index 000000000..6117dc19b --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisAutoCacheManager.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.Cache; +import org.springframework.data.redis.cache.RedisCache; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.lang.Nullable; + +import java.time.Duration; +import java.util.Map; + +/** + * redis cache 扩展cache name自动化配置 + * + * @author L.cm + * @author lengleng + *

+ * cachename = xx#ttl + */ +@Slf4j +public class RedisAutoCacheManager extends RedisCacheManager { + + public static final String SPLIT_FLAG = "#"; + + public RedisAutoCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, + Map initialCacheConfigurations, boolean allowInFlightCacheCreation) { + super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation); + } + + @Override + protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) { + if (StrUtil.isBlank(name) || !name.contains(SPLIT_FLAG)) { + return super.createRedisCache(name, cacheConfig); + } + String[] cacheArray = name.split(SPLIT_FLAG); + if (cacheArray.length < 2) { + return super.createRedisCache(name, cacheConfig); + } + String cacheName = cacheArray[0] + ":" + SecurityUtils.getTenantId(); + if (cacheConfig != null) { + long cacheAge = Long.getLong(cacheArray[1], -1); + cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(cacheAge)); + } + return super.createRedisCache(cacheName, cacheConfig); + } + + @Override + public Cache getCache(String name) { + return super.getCache(name + ":" + SecurityUtils.getTenantId()); + } +} diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheAutoConfiguration.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheAutoConfiguration.java new file mode 100644 index 000000000..4de9ec321 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheAutoConfiguration.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers; +import org.springframework.boot.autoconfigure.cache.CacheProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; +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.JdkSerializationRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.lang.Nullable; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 扩展redis-cache支持注解cacheName添加超时时间 + * + * @author L.cm + */ +@Configuration +@AutoConfigureAfter({RedisAutoConfiguration.class}) +@ConditionalOnBean({RedisConnectionFactory.class}) +@ConditionalOnMissingBean({CacheManager.class}) +@EnableConfigurationProperties(CacheProperties.class) +public class RedisCacheAutoConfiguration { + private final CacheProperties cacheProperties; + private final CacheManagerCustomizers customizerInvoker; + @Nullable + private final RedisCacheConfiguration redisCacheConfiguration; + + RedisCacheAutoConfiguration(CacheProperties cacheProperties, + CacheManagerCustomizers customizerInvoker, + ObjectProvider redisCacheConfiguration) { + this.cacheProperties = cacheProperties; + this.customizerInvoker = customizerInvoker; + this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable(); + } + + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, ResourceLoader resourceLoader) { + DefaultRedisCacheWriter redisCacheWriter = new DefaultRedisCacheWriter(connectionFactory); + RedisCacheConfiguration cacheConfiguration = this.determineConfiguration(resourceLoader.getClassLoader()); + List cacheNames = this.cacheProperties.getCacheNames(); + Map initialCaches = new LinkedHashMap<>(); + if (!cacheNames.isEmpty()) { + Map cacheConfigMap = new LinkedHashMap<>(cacheNames.size()); + cacheNames.forEach(it -> cacheConfigMap.put(it, cacheConfiguration)); + initialCaches.putAll(cacheConfigMap); + } + boolean allowInFlightCacheCreation = true; + boolean enableTransactions = false; + RedisAutoCacheManager cacheManager = new RedisAutoCacheManager(redisCacheWriter, cacheConfiguration, + initialCaches, allowInFlightCacheCreation); + cacheManager.setTransactionAware(enableTransactions); + return this.customizerInvoker.customize(cacheManager); + } + + private RedisCacheConfiguration determineConfiguration(ClassLoader classLoader) { + if (this.redisCacheConfiguration != null) { + return this.redisCacheConfiguration; + } else { + CacheProperties.Redis redisProperties = this.cacheProperties.getRedis(); + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); + config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader))); + if (redisProperties.getTimeToLive() != null) { + config = config.entryTtl(redisProperties.getTimeToLive()); + } + + if (redisProperties.getKeyPrefix() != null) { + config = config.prefixKeysWith(redisProperties.getKeyPrefix()); + } + + if (!redisProperties.isCacheNullValues()) { + config = config.disableCachingNullValues(); + } + + if (!redisProperties.isUseKeyPrefix()) { + config = config.disableKeyPrefix(); + } + + return config; + } + } +} diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheManagerConfig.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheManagerConfig.java new file mode 100644 index 000000000..8a020cbd7 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisCacheManagerConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer; +import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * CacheManagerCustomizers配置 + * + * @author L.cm + */ +@Configuration +@ConditionalOnMissingBean(CacheManagerCustomizers.class) +public class RedisCacheManagerConfig { + + @Bean + public CacheManagerCustomizers cacheManagerCustomizers( + ObjectProvider>> customizers) { + return new CacheManagerCustomizers(customizers.getIfAvailable()); + } +} diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisKeySerializer.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisKeySerializer.java new file mode 100644 index 000000000..ad914e488 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisKeySerializer.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; +import org.springframework.cache.interceptor.SimpleKey; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 将redis key序列化为字符串 + * + *

+ * spring cache中的简单基本类型直接使用 StringRedisSerializer 会有问题 + *

+ * + * @author L.cm + */ +public class RedisKeySerializer implements RedisSerializer { + private final Charset charset; + private final ConversionService converter; + + public RedisKeySerializer() { + this(StandardCharsets.UTF_8); + } + + public RedisKeySerializer(Charset charset) { + Assert.notNull(charset, "Charset must not be null!"); + this.charset = charset; + this.converter = DefaultConversionService.getSharedInstance(); + } + + @Override + public Object deserialize(byte[] bytes) { + throw new RuntimeException("Used only for serializing key, not for deserialization."); + } + + @Override + @Nullable + public byte[] serialize(@Nullable Object object) { + if (object == null) { + return null; + } + String key; + if (object instanceof SimpleKey) { + key = ""; + } else if (object instanceof String) { + key = (String) object; + } else { + key = converter.convert(object, String.class); + } + return key.getBytes(this.charset); + } + +} diff --git a/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisTemplateConfig.java b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisTemplateConfig.java new file mode 100644 index 000000000..4d62b16fd --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisTemplateConfig.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.cache; + +import lombok.AllArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; + +/** + * RedisTemplate 配置 + * + * @author L.cm + */ +@EnableCaching +@Configuration +@ConditionalOnBean(RedisConnectionFactory.class) +@AllArgsConstructor +public class RedisTemplateConfig { + private final RedisConnectionFactory redisConnectionFactory; + + @Bean + @ConditionalOnMissingBean(RedisTemplate.class) + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new RedisKeySerializer()); + redisTemplate.setHashKeySerializer(new RedisKeySerializer()); + redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } + +} diff --git a/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring-devtools.properties b/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 000000000..2daeb3c03 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.pigx-common-log=/pigx-common-cache[\\w-]+\.jar diff --git a/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring.factories b/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..c34166bd2 --- /dev/null +++ b/pigx-common/pigx-common-cache/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.pig4cloud.pigx.common.cache.RedisTemplateConfig,\ + com.pig4cloud.pigx.common.cache.RedisCacheManagerConfig,\ + com.pig4cloud.pigx.common.cache.RedisCacheAutoConfiguration + diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/RedisConfig.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/RedisConfig.java deleted file mode 100644 index 2890d8c28..000000000 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/RedisConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright (c) 2018-2025, lengleng All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * Neither the name of the pig4cloud.com developer nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * Author: lengleng (wangiegie@gmail.com) - * - */ - -package com.pig4cloud.pigx.common.core.config; - -import lombok.AllArgsConstructor; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.*; -import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -/** - * @author lengleng - * @date 2018/6/23 - * Redis 配置类 - */ -@EnableCaching -@Configuration -@AllArgsConstructor -public class RedisConfig { - private final RedisConnectionFactory factory; - - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setConnectionFactory(factory); - return redisTemplate; - } - - @Bean - public HashOperations hashOperations(RedisTemplate redisTemplate) { - return redisTemplate.opsForHash(); - } - - @Bean - public ValueOperations valueOperations(RedisTemplate redisTemplate) { - return redisTemplate.opsForValue(); - } - - @Bean - public ListOperations listOperations(RedisTemplate redisTemplate) { - return redisTemplate.opsForList(); - } - - @Bean - public SetOperations setOperations(RedisTemplate redisTemplate) { - return redisTemplate.opsForSet(); - } - - @Bean - public ZSetOperations zSetOperations(RedisTemplate redisTemplate) { - return redisTemplate.opsForZSet(); - } -} diff --git a/pigx-common/pigx-common-core/src/main/resources/META-INF/spring.factories b/pigx-common/pigx-common-core/src/main/resources/META-INF/spring.factories index 403ef275d..5299ddf64 100644 --- a/pigx-common/pigx-common-core/src/main/resources/META-INF/spring.factories +++ b/pigx-common/pigx-common-core/src/main/resources/META-INF/spring.factories @@ -1,6 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.pig4cloud.pigx.common.core.config.JacksonConfig,\ - com.pig4cloud.pigx.common.core.config.RedisConfig,\ com.pig4cloud.pigx.common.core.config.RestTemplateConfig,\ com.pig4cloud.pigx.common.core.exception.GlobalExceptionHandler,\ com.pig4cloud.pigx.common.core.util.SpringContextHolder diff --git a/pigx-common/pom.xml b/pigx-common/pom.xml index 99bc4a584..46dd898d2 100644 --- a/pigx-common/pom.xml +++ b/pigx-common/pom.xml @@ -33,6 +33,7 @@ pigx 公共聚合模块 + pigx-common-cache pigx-common-core pigx-common-job pigx-common-log diff --git a/pigx-upms/pigx-upms-biz/pom.xml b/pigx-upms/pigx-upms-biz/pom.xml index 939ffbf1e..1a3a87bb2 100644 --- a/pigx-upms/pigx-upms-biz/pom.xml +++ b/pigx-upms/pigx-upms-biz/pom.xml @@ -45,6 +45,12 @@ pigx-common-log ${pigx.version} + + + com.pig4cloud + pigx-common-cache + ${pigx.version} + com.pig4cloud diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/entity/LeaveBill.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/entity/LeaveBill.java index 6c4bcac18..13804ce28 100755 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/entity/LeaveBill.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/entity/LeaveBill.java @@ -24,7 +24,8 @@ import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import lombok.Data; import lombok.EqualsAndHashCode; - import java.io.Serializable; + +import java.io.Serializable; import java.time.LocalDateTime; /** @@ -74,11 +75,16 @@ public class LeaveBill extends Model { @TableLogic private String delFlag; - /** - * 主键值 - */ - @Override - protected Serializable pkVal() { - return this.leaveId; - } + /** + * 租户ID + */ + private Integer tenantId; + + /** + * 主键值 + */ + @Override + protected Serializable pkVal() { + return this.leaveId; + } } diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/EditorServiceImpl.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/EditorServiceImpl.java index cd558a762..24a195661 100644 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/EditorServiceImpl.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/EditorServiceImpl.java @@ -21,6 +21,7 @@ import cn.hutool.core.util.CharsetUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.pig4cloud.pigx.act.service.EditorService; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.AllArgsConstructor; import org.activiti.engine.ActivitiException; import org.activiti.engine.RepositoryService; @@ -110,6 +111,8 @@ public class EditorServiceImpl implements EditorService { modelJson.put(MODEL_DESCRIPTION, description); model.setMetaInfo(modelJson.toString()); model.setName(name); + model.setTenantId(String.valueOf(SecurityUtils.getTenantId())); + repositoryService.saveModel(model); repositoryService.addModelEditorSource(model.getId(), jsonXml.getBytes(CharsetUtil.UTF_8)); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ModelServiceImpl.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ModelServiceImpl.java index 2a772827f..4690e538d 100644 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ModelServiceImpl.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ModelServiceImpl.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.pig4cloud.pigx.act.service.ModelService; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.activiti.bpmn.model.BpmnModel; @@ -87,6 +88,7 @@ public class ModelServiceImpl implements ModelService { modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion()); modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, desc); model.setMetaInfo(modelObjectNode.toString()); + model.setTenantId(String.valueOf(SecurityUtils.getTenantId())); repositoryService.saveModel(model); repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8")); @@ -154,10 +156,13 @@ public class ModelServiceImpl implements ModelService { Deployment deployment = repositoryService .createDeployment().name(model.getName()) .addBpmnModel(processName, bpmnModel) + .tenantId(String.valueOf(SecurityUtils.getTenantId())) .deploy(); // 设置流程分类 - List list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list(); + List list = repositoryService.createProcessDefinitionQuery() + .deploymentId(deployment.getId()) + .list(); list.stream().forEach(processDefinition -> repositoryService.setProcessDefinitionCategory(processDefinition.getId(), model.getCategory())); diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ProcessServiceImpl.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ProcessServiceImpl.java index 4902354a8..43541f784 100644 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ProcessServiceImpl.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/service/impl/ProcessServiceImpl.java @@ -27,6 +27,7 @@ import com.pig4cloud.pigx.act.service.ProcessService; import com.pig4cloud.pigx.common.core.constant.enums.EnumProcessStatus; import com.pig4cloud.pigx.common.core.constant.enums.EnumResourceType; import com.pig4cloud.pigx.common.core.constant.enums.EnumTaskStatus; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.AllArgsConstructor; import org.activiti.engine.RepositoryService; import org.activiti.engine.RuntimeService; @@ -164,7 +165,7 @@ public class ProcessServiceImpl implements ProcessService { //3: 格式:LeaveBill_id的形式(使用流程变量) String businessKey = key + "_" + leaveBill.getLeaveId(); //4:使用流程定义的key,启动流程实例,正在执行的执行对象表中的字段BUSINESS_KEY添加业务数据,同时让流程关联业务 - runtimeService.startProcessInstanceByKey(key, businessKey); + runtimeService.startProcessInstanceByKeyAndTenantId(key, businessKey, String.valueOf(SecurityUtils.getTenantId())); leaveBillMapper.updateById(leaveBill); return Boolean.TRUE; }