mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
✨ 添加新特性。 扩展spring cache 支持ttl 和自动租户管理
This commit is contained in:
1 parent
1d2d8f6e32
commit
668c62bc73
17 files changed
+717
-88
No files matched your search
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ 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)
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-cache</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>pigx 缓存工具类</description>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!--工具类核心包 要从线程中获取租户ID-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-security</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<!--缓存依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+275
@@ -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}. <br />
|
||||
* {@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> T execute(String name, Function<RedisConnection, T> callback) {
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
try {
|
||||
|
||||
checkAndPotentiallyWaitUntilUnlocked(name, connection);
|
||||
return callback.apply(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeLockFree(Consumer<RedisConnection> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+72
@@ -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
|
||||
* <p>
|
||||
* cachename = xx#ttl
|
||||
*/
|
||||
@Slf4j
|
||||
public class RedisAutoCacheManager extends RedisCacheManager {
|
||||
|
||||
public static final String SPLIT_FLAG = "#";
|
||||
|
||||
public RedisAutoCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Map<String, RedisCacheConfiguration> 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());
|
||||
}
|
||||
}
|
||||
+112
@@ -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> 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<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
|
||||
if (!cacheNames.isEmpty()) {
|
||||
Map<String, RedisCacheConfiguration> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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<List<CacheManagerCustomizer<?>>> customizers) {
|
||||
return new CacheManagerCustomizers(customizers.getIfAvailable());
|
||||
}
|
||||
}
|
||||
Vendored
+74
@@ -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序列化为字符串
|
||||
*
|
||||
* <p>
|
||||
* spring cache中的简单基本类型直接使用 StringRedisSerializer 会有问题
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class RedisKeySerializer implements RedisSerializer<Object> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
pigx-common/pigx-common-cache/src/main/java/com/pig4cloud/pigx/common/cache/RedisTemplateConfig.java
Vendored
+54
@@ -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<String, Object> redisTemplate() {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new RedisKeySerializer());
|
||||
redisTemplate.setHashKeySerializer(new RedisKeySerializer());
|
||||
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
|
||||
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
restart.include.pigx-common-log=/pigx-common-cache[\\w-]+\.jar
|
||||
@@ -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
|
||||
|
||||
-77
@@ -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<String, Object> redisTemplate() {
|
||||
RedisTemplate<String, Object> 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<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
|
||||
return redisTemplate.opsForHash();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
|
||||
return redisTemplate.opsForValue();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
|
||||
return redisTemplate.opsForList();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
|
||||
return redisTemplate.opsForSet();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
|
||||
return redisTemplate.opsForZSet();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -33,6 +33,7 @@
|
||||
<description>pigx 公共聚合模块</description>
|
||||
|
||||
<modules>
|
||||
<module>pigx-common-cache</module>
|
||||
<module>pigx-common-core</module>
|
||||
<module>pigx-common-job</module>
|
||||
<module>pigx-common-log</module>
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
<artifactId>pigx-common-log</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<!--缓存依赖-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-cache</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<!--swagger-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
|
||||
+14
-8
@@ -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<LeaveBill> {
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 主键值
|
||||
*/
|
||||
@Override
|
||||
protected Serializable pkVal() {
|
||||
return this.leaveId;
|
||||
}
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Integer tenantId;
|
||||
|
||||
/**
|
||||
* 主键值
|
||||
*/
|
||||
@Override
|
||||
protected Serializable pkVal() {
|
||||
return this.leaveId;
|
||||
}
|
||||
}
|
||||
+3
@@ -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();
|
||||
|
||||
+6
-1
@@ -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<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
|
||||
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
|
||||
.deploymentId(deployment.getId())
|
||||
.list();
|
||||
|
||||
list.stream().forEach(processDefinition ->
|
||||
repositoryService.setProcessDefinitionCategory(processDefinition.getId(), model.getCategory()));
|
||||
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user