diff --git a/.run/ruoyi-auth.run.xml b/.run/ruoyi-auth.run.xml index abeeb774c..811a91f0a 100644 --- a/.run/ruoyi-auth.run.xml +++ b/.run/ruoyi-auth.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-gateway.run.xml b/.run/ruoyi-gateway.run.xml index ff85d4e04..9242a2e37 100644 --- a/.run/ruoyi-gateway.run.xml +++ b/.run/ruoyi-gateway.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-gen.run.xml b/.run/ruoyi-gen.run.xml index 6c509c119..7be88e136 100644 --- a/.run/ruoyi-gen.run.xml +++ b/.run/ruoyi-gen.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-job.run.xml b/.run/ruoyi-job.run.xml index 13a2f21ed..e2b0bcfc1 100644 --- a/.run/ruoyi-job.run.xml +++ b/.run/ruoyi-job.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-monitor.run.xml b/.run/ruoyi-monitor.run.xml index a4b4213a3..a96a86aa4 100644 --- a/.run/ruoyi-monitor.run.xml +++ b/.run/ruoyi-monitor.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-nacos.run.xml b/.run/ruoyi-nacos.run.xml index 90d065f5e..b24a5755e 100644 --- a/.run/ruoyi-nacos.run.xml +++ b/.run/ruoyi-nacos.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-resource.run.xml b/.run/ruoyi-resource.run.xml index fe1f9d96d..989cb4152 100644 --- a/.run/ruoyi-resource.run.xml +++ b/.run/ruoyi-resource.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-seata-server.run.xml b/.run/ruoyi-seata-server.run.xml index f72e085a7..83a878ad0 100644 --- a/.run/ruoyi-seata-server.run.xml +++ b/.run/ruoyi-seata-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-snailjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml index 228d9a006..8508475d0 100644 --- a/.run/ruoyi-snailjob-server.run.xml +++ b/.run/ruoyi-snailjob-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-system.run.xml b/.run/ruoyi-system.run.xml index 82f4435a6..8a62955cb 100644 --- a/.run/ruoyi-system.run.xml +++ b/.run/ruoyi-system.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-workflow.run.xml b/.run/ruoyi-workflow.run.xml index 0ed84f62a..4ac4a1092 100644 --- a/.run/ruoyi-workflow.run.xml +++ b/.run/ruoyi-workflow.run.xml @@ -2,7 +2,7 @@ - diff --git a/README.md b/README.md index 8e95a6984..7c2b534b3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Star](https://gitcode.com/dromara/RuoYi-Cloud-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Cloud-Plus) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/2.X/LICENSE)
-[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.6.0-success.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus) +[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.6.1-success.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() diff --git a/pom.xml b/pom.xml index dd7befa8e..323c740a4 100644 --- a/pom.xml +++ b/pom.xml @@ -13,28 +13,28 @@ Dromara RuoYi-Cloud-Plus微服务系统 - 2.6.0 + 2.6.1 UTF-8 UTF-8 17 - 3.5.12 - 2025.0.1 - 3.5.6 + 3.5.14 + 2025.0.2 + 3.5.8 3.5.19 3.5.16 3.9.1 4.3.1 2.3 - 2.2.41 - 2.8.15 + 2.2.47 + 2.8.17 0.15.0 1.3.0 5.8.43 3.52.0 2.2.7 - 1.9.0 - 1.44.0 - 1.18.42 + 1.10.0 + 1.45.0 + 1.18.44 7.4 3.0.1 7.17.28 @@ -44,19 +44,19 @@ 0.2.0 1.16.7 - 3.3.4 + 3.3.7 1.2.83 2.28.22 - 3.3.4 + 3.3.5 - 8.7.3-20251210 + 8.7.3-20260319 - 1.8.4 + 1.8.5 - 2.3.4 + 2.3.5 3.14.0 @@ -350,13 +350,6 @@ ${fastjson.version} - - - redis.clients - jedis - 5.1.0 - - io.github.linpeilie mapstruct-plus-spring-boot-starter diff --git a/ruoyi-api/ruoyi-api-bom/pom.xml b/ruoyi-api/ruoyi-api-bom/pom.xml index 8f465ca5f..460e766ad 100644 --- a/ruoyi-api/ruoyi-api-bom/pom.xml +++ b/ruoyi-api/ruoyi-api-bom/pom.xml @@ -15,7 +15,7 @@ - 2.6.0 + 2.6.1 diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java b/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java index 471539993..32b483de5 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java @@ -14,6 +14,7 @@ import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.redis.utils.RedisUtils; @@ -77,7 +78,7 @@ public class UserActionListener implements SaTokenListener { SpringUtils.context().publishEvent(logininforEvent); // 更新登录信息 remoteUserService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); - log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue); + log.info("user doLogin, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8)); } /** @@ -89,7 +90,7 @@ public class UserActionListener implements SaTokenListener { TenantHelper.dynamic(tenantId, () -> { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); }); - log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue); + log.info("user doLogout, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8)); } /** @@ -101,7 +102,7 @@ public class UserActionListener implements SaTokenListener { TenantHelper.dynamic(tenantId, () -> { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); }); - log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue); + log.info("user doLogoutByLoginId, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8)); } /** @@ -113,7 +114,7 @@ public class UserActionListener implements SaTokenListener { TenantHelper.dynamic(tenantId, () -> { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); }); - log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue); + log.info("user doReplaced, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8)); } /** diff --git a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml index 096cfbde5..54c93fc5b 100644 --- a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml @@ -14,7 +14,7 @@ - 2.6.0 + 2.6.1 2025.0.0.0 2.5.0 2.5.1 diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 151b39725..dc1ee4f20 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 2.6.0 + 2.6.1 diff --git a/ruoyi-common/ruoyi-common-dubbo/pom.xml b/ruoyi-common/ruoyi-common-dubbo/pom.xml index 344e3a1ae..87dcae628 100644 --- a/ruoyi-common/ruoyi-common-dubbo/pom.xml +++ b/ruoyi-common/ruoyi-common-dubbo/pom.xml @@ -37,20 +37,10 @@ - org.apache.dubbo.extensions - dubbo-metadata-report-redis - - - redis.clients - jedis - - - - - redis.clients - jedis - 5.2.0 + org.dromara + ruoyi-common-redis + org.projectlombok lombok diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java deleted file mode 100644 index fa47ed1fe..000000000 --- a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.metadata.store.redis; - -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.config.configcenter.ConfigItem; -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.*; -import org.apache.dubbo.metadata.MappingChangedEvent; -import org.apache.dubbo.metadata.MappingListener; -import org.apache.dubbo.metadata.MetadataInfo; -import org.apache.dubbo.metadata.ServiceNameMapping; -import org.apache.dubbo.metadata.report.identifier.*; -import org.apache.dubbo.metadata.report.support.AbstractMetadataReport; -import org.apache.dubbo.rpc.RpcException; -import redis.clients.jedis.*; -import redis.clients.jedis.params.SetParams; -import redis.clients.jedis.util.JedisClusterCRC16; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import static org.apache.dubbo.common.constants.CommonConstants.*; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; -import static org.apache.dubbo.metadata.MetadataConstants.META_DATA_STORE_TAG; -import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP; -import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames; -import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT; - -/** - * RedisMetadataReport - */ -public class RedisMetadataReport extends AbstractMetadataReport { - - private static final String REDIS_DATABASE_KEY = "database"; - private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RedisMetadataReport.class); - - // protected , for test - protected JedisPool pool; - private Set jedisClusterNodes; - private int timeout; - private String username; - private String password; - private final String root; - private final ConcurrentHashMap mappingDataListenerMap = new ConcurrentHashMap<>(); - private SetParams jedisParams = SetParams.setParams(); - - public RedisMetadataReport(URL url) { - super(url); - timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT); - username = url.getUsername(); - password = url.getPassword(); - this.root = url.getGroup(DEFAULT_ROOT); - if (url.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) { - // ttl default is twice the cycle-report time - jedisParams.px(ONE_DAY_IN_MILLISECONDS * 2); - } - if (url.getParameter(CLUSTER_KEY, false)) { - jedisClusterNodes = new HashSet<>(); - List urls = url.getBackupUrls(); - for (URL tmpUrl : urls) { - jedisClusterNodes.add(new HostAndPort(tmpUrl.getHost(), tmpUrl.getPort())); - } - } else { - int database = url.getParameter(REDIS_DATABASE_KEY, 0); - pool = new JedisPool(new JedisPoolConfig(), url.getHost(), url.getPort(), timeout, username, password, database); - } - } - - @Override - protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { - this.storeMetadata(providerMetadataIdentifier, serviceDefinitions, true); - } - - @Override - protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) { - this.storeMetadata(consumerMetadataIdentifier, value, true); - } - - @Override - protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) { - this.storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()), false); - } - - @Override - protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) { - this.deleteMetadata(serviceMetadataIdentifier); - } - - @Override - protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { - String content = getMetadata(metadataIdentifier); - if (StringUtils.isEmpty(content)) { - return Collections.emptyList(); - } - return new ArrayList<>(Arrays.asList(URL.decode(content))); - } - - @Override - protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) { - this.storeMetadata(subscriberMetadataIdentifier, urlListStr, false); - } - - @Override - protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { - return this.getMetadata(subscriberMetadataIdentifier); - } - - @Override - public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { - return this.getMetadata(metadataIdentifier); - } - - private void storeMetadata(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) { - if (pool != null) { - storeMetadataStandalone(metadataIdentifier, v, ephemeral); - } else { - storeMetadataInCluster(metadataIdentifier, v, ephemeral); - } - } - - private void storeMetadataInCluster(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) { - try (JedisCluster jedisCluster = - new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - if (ephemeral) { - jedisCluster.set(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG, v, jedisParams); - } else { - jedisCluster.set(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG, v); - } - } catch (Throwable e) { - String msg = - "Failed to put " + metadataIdentifier + " to redis cluster " + v + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private void storeMetadataStandalone(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) { - try (Jedis jedis = pool.getResource()) { - if (ephemeral) { - jedis.set(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v, jedisParams); - } else { - jedis.set(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v); - } - } catch (Throwable e) { - String msg = "Failed to put " + metadataIdentifier + " to redis " + v + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private void deleteMetadata(BaseMetadataIdentifier metadataIdentifier) { - if (pool != null) { - deleteMetadataStandalone(metadataIdentifier); - } else { - deleteMetadataInCluster(metadataIdentifier); - } - } - - private void deleteMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) { - try (JedisCluster jedisCluster = - new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - jedisCluster.del(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG); - } catch (Throwable e) { - String msg = "Failed to delete " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private void deleteMetadataStandalone(BaseMetadataIdentifier metadataIdentifier) { - try (Jedis jedis = pool.getResource()) { - jedis.del(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); - } catch (Throwable e) { - String msg = "Failed to delete " + metadataIdentifier + " from redis , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private String getMetadata(BaseMetadataIdentifier metadataIdentifier) { - if (pool != null) { - return getMetadataStandalone(metadataIdentifier); - } else { - return getMetadataInCluster(metadataIdentifier); - } - } - - private String getMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) { - try (JedisCluster jedisCluster = - new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - return jedisCluster.get(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG); - } catch (Throwable e) { - String msg = "Failed to get " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private String getMetadataStandalone(BaseMetadataIdentifier metadataIdentifier) { - try (Jedis jedis = pool.getResource()) { - return jedis.get(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); - } catch (Throwable e) { - String msg = "Failed to get " + metadataIdentifier + " from redis , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - /** - * Store class and application names using Redis hashes - * key: default 'dubbo:mapping' - * field: class (serviceInterface) - * value: application_names - * @param serviceInterface field(class) - * @param defaultMappingGroup {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP} - * @param newConfigContent new application_names - * @param ticket previous application_names - * @return - */ - @Override - public boolean registerServiceAppMapping( - String serviceInterface, String defaultMappingGroup, String newConfigContent, Object ticket) { - try { - if (null != ticket && !(ticket instanceof String)) { - throw new IllegalArgumentException("redis publishConfigCas requires stat type ticket"); - } - String pathKey = buildMappingKey(defaultMappingGroup); - - return storeMapping(pathKey, serviceInterface, newConfigContent, (String) ticket); - } catch (Exception e) { - logger.warn(TRANSPORT_FAILED_RESPONSE, "", "", "redis publishConfigCas failed.", e); - return false; - } - } - - private boolean storeMapping(String key, String field, String value, String ticket) { - if (pool != null) { - return storeMappingStandalone(key, field, value, ticket); - } else { - return storeMappingInCluster(key, field, value, ticket); - } - } - - /** - * use 'watch' to implement cas. - * Find information about slot distribution by key. - */ - private boolean storeMappingInCluster(String key, String field, String value, String ticket) { - try (JedisCluster jedisCluster = - new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - Jedis jedis = new Jedis(jedisCluster.getConnectionFromSlot(JedisClusterCRC16.getSlot(key))); - jedis.watch(key); - String oldValue = jedis.hget(key, field); - if (null == oldValue || null == ticket || oldValue.equals(ticket)) { - Transaction transaction = jedis.multi(); - transaction.hset(key, field, value); - List result = transaction.exec(); - if (null != result) { - jedisCluster.publish(buildPubSubKey(), field); - return true; - } - } else { - jedis.unwatch(); - } - jedis.close(); - } catch (Throwable e) { - String msg = "Failed to put " + key + ":" + field + " to redis " + value + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - return false; - } - - /** - * use 'watch' to implement cas. - * Find information about slot distribution by key. - */ - private boolean storeMappingStandalone(String key, String field, String value, String ticket) { - try (Jedis jedis = pool.getResource()) { - jedis.watch(key); - String oldValue = jedis.hget(key, field); - if (null == oldValue || null == ticket || oldValue.equals(ticket)) { - Transaction transaction = jedis.multi(); - transaction.hset(key, field, value); - List result = transaction.exec(); - if (null != result) { - jedis.publish(buildPubSubKey(), field); - return true; - } - } - jedis.unwatch(); - } catch (Throwable e) { - String msg = "Failed to put " + key + ":" + field + " to redis " + value + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - return false; - } - - /** - * build mapping key - * @param defaultMappingGroup {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP} - * @return - */ - private String buildMappingKey(String defaultMappingGroup) { - return this.root + GROUP_CHAR_SEPARATOR + defaultMappingGroup; - } - - /** - * build pub/sub key - */ - private String buildPubSubKey() { - return buildMappingKey(DEFAULT_MAPPING_GROUP) + GROUP_CHAR_SEPARATOR + QUEUES_KEY; - } - - /** - * get content and use content to complete cas - * @param serviceKey class - * @param group {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP} - */ - @Override - public ConfigItem getConfigItem(String serviceKey, String group) { - String key = buildMappingKey(group); - String content = getMappingData(key, serviceKey); - - return new ConfigItem(content, content); - } - - /** - * get current application_names - */ - private String getMappingData(String key, String field) { - if (pool != null) { - return getMappingDataStandalone(key, field); - } else { - return getMappingDataInCluster(key, field); - } - } - - private String getMappingDataInCluster(String key, String field) { - try (JedisCluster jedisCluster = - new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - return jedisCluster.hget(key, field); - } catch (Throwable e) { - String msg = "Failed to get " + key + ":" + field + " from redis cluster , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - private String getMappingDataStandalone(String key, String field) { - try (Jedis jedis = pool.getResource()) { - return jedis.hget(key, field); - } catch (Throwable e) { - String msg = "Failed to get " + key + ":" + field + " from redis , cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - - /** - * remove listener. If have no listener,thread will dead - */ - @Override - public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { - MappingDataListener mappingDataListener = mappingDataListenerMap.get(buildPubSubKey()); - if (null != mappingDataListener) { - NotifySub notifySub = mappingDataListener.getNotifySub(); - notifySub.removeListener(serviceKey, listener); - if (notifySub.isEmpty()) { - mappingDataListener.shutdown(); - } - } - } - - /** - * Start a thread and subscribe to {@link this#buildPubSubKey()}. - * Notify {@link MappingListener} if there is a change in the 'application_names' message. - */ - @Override - public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { - MappingDataListener mappingDataListener = - ConcurrentHashMapUtils.computeIfAbsent(mappingDataListenerMap, buildPubSubKey(), k -> { - MappingDataListener dataListener = new MappingDataListener(buildPubSubKey()); - dataListener.start(); - return dataListener; - }); - mappingDataListener.getNotifySub().addListener(serviceKey, listener); - return this.getServiceAppMapping(serviceKey, url); - } - - @Override - public Set getServiceAppMapping(String serviceKey, URL url) { - String key = buildMappingKey(DEFAULT_MAPPING_GROUP); - return getAppNames(getMappingData(key, serviceKey)); - } - - @Override - public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { - String content = this.getMetadata(identifier); - return JsonUtils.toJavaObject(content, MetadataInfo.class); - } - - @Override - public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { - this.storeMetadata(identifier, metadataInfo.getContent(), false); - } - - @Override - public void unPublishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { - this.deleteMetadata(identifier); - } - - // for test - public MappingDataListener getMappingDataListener() { - return mappingDataListenerMap.get(buildPubSubKey()); - } - - /** - * Listen for changes in the 'application_names' message and notify the listener. - */ - class NotifySub extends JedisPubSub { - - private final Map> listeners = new ConcurrentHashMap<>(); - - public void addListener(String key, MappingListener listener) { - Set listenerSet = listeners.computeIfAbsent(key, k -> new ConcurrentHashSet<>()); - listenerSet.add(listener); - } - - public void removeListener(String serviceKey, MappingListener listener) { - Set listenerSet = this.listeners.get(serviceKey); - if (listenerSet != null) { - listenerSet.remove(listener); - if (listenerSet.isEmpty()) { - this.listeners.remove(serviceKey); - } - } - } - - public Boolean isEmpty() { - return this.listeners.isEmpty(); - } - - @Override - public void onMessage(String key, String msg) { - logger.info("sub from redis:" + key + " message:" + msg); - String applicationNames = getMappingData(buildMappingKey(DEFAULT_MAPPING_GROUP), msg); - MappingChangedEvent mappingChangedEvent = new MappingChangedEvent(msg, getAppNames(applicationNames)); - if (!CollectionUtils.isEmpty(listeners.get(msg))) { - for (MappingListener mappingListener : listeners.get(msg)) { - mappingListener.onEvent(mappingChangedEvent); - } - } - } - - @Override - public void onPMessage(String pattern, String key, String msg) { - onMessage(key, msg); - } - - @Override - public void onPSubscribe(String pattern, int subscribedChannels) { - super.onPSubscribe(pattern, subscribedChannels); - } - } - - /** - * Subscribe application names change message. - */ - class MappingDataListener extends Thread { - - private String path; - - private final NotifySub notifySub = new NotifySub(); - // for test - protected volatile boolean running = true; - - public MappingDataListener(String path) { - this.path = path; - } - - public NotifySub getNotifySub() { - return notifySub; - } - - @Override - public void run() { - while (running) { - if (pool != null) { - try (Jedis jedis = pool.getResource()) { - jedis.subscribe(notifySub, path); - } catch (Throwable e) { - String msg = "Failed to subscribe " + path + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } else { - try (JedisCluster jedisCluster = new JedisCluster( - jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) { - jedisCluster.subscribe(notifySub, path); - } catch (Throwable e) { - String msg = "Failed to subscribe " + path + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - throw new RpcException(msg, e); - } - } - } - } - - public void shutdown() { - try { - running = false; - notifySub.unsubscribe(path); - } catch (Throwable e) { - String msg = "Failed to unsubscribe " + path + ", cause: " + e.getMessage(); - logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e); - } - } - } -} diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReport.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReport.java new file mode 100644 index 000000000..834ef63f7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReport.java @@ -0,0 +1,301 @@ +package org.apache.dubbo.metadata.store.redis; + +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.config.configcenter.ConfigItem; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.metadata.MappingChangedEvent; +import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.metadata.MetadataInfo; +import org.apache.dubbo.metadata.report.identifier.*; +import org.apache.dubbo.metadata.report.support.AbstractMetadataReport; +import org.apache.dubbo.rpc.RpcException; +import org.dromara.common.core.utils.SpringUtils; +import org.redisson.api.RScript; +import org.redisson.api.RTopic; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.StringCodec; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import static org.apache.dubbo.common.constants.CommonConstants.*; +import static org.apache.dubbo.metadata.MetadataConstants.META_DATA_STORE_TAG; +import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP; +import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames; +import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT; + +/** + * 使用 Redisson 重新实现元数据中心 + */ +@Slf4j +public class RedissonMetadataReport extends AbstractMetadataReport { + + // Lua script for atomic CAS on a hash field: + // If the current value equals ticket (or field is absent, or ticket is empty), update and return 1; else return 0. + private static final String CAS_LUA =""" + local old = redis.call('HGET', KEYS[1], ARGV[1]) + if old == false or ARGV[3] == '' or old == ARGV[3] then + redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]) + return 1 + end + return 0"""; + + private final String root; + private final long ttlMs; + + // Lazily initialized — Dubbo SPI creates this class before Spring is fully ready + private volatile RedissonClient redissonClient; + + // topic key → RTopic (keeps the subscription alive) + private final ConcurrentHashMap topicMap = new ConcurrentHashMap<>(); + // serviceKey → listeners (for dispatching mapping change events) + private final ConcurrentHashMap> listenerMap = new ConcurrentHashMap<>(); + + public RedissonMetadataReport(URL url) { + super(url); + this.root = url.getGroup(DEFAULT_ROOT); + this.ttlMs = url.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT) + ? ONE_DAY_IN_MILLISECONDS * 2L + : 0L; + } + + // ------------------------------------------------------------------------- + // Lazy RedissonClient accessor + // ------------------------------------------------------------------------- + + private RedissonClient getRedisson() { + if (redissonClient == null) { + synchronized (this) { + if (redissonClient == null) { + redissonClient = SpringUtils.getBean(RedissonClient.class); + } + } + } + return redissonClient; + } + + // ------------------------------------------------------------------------- + // AbstractMetadataReport — provider / consumer metadata + // ------------------------------------------------------------------------- + + @Override + protected void doStoreProviderMetadata(MetadataIdentifier id, String serviceDefinitions) { + storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceDefinitions, true); + } + + @Override + protected void doStoreConsumerMetadata(MetadataIdentifier id, String value) { + storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), value, true); + } + + @Override + protected void doSaveMetadata(ServiceMetadataIdentifier id, URL url) { + storeMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG, URL.encode(url.toFullString()), false); + } + + @Override + protected void doRemoveMetadata(ServiceMetadataIdentifier id) { + deleteMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG); + } + + @Override + protected List doGetExportedURLs(ServiceMetadataIdentifier id) { + String content = getMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG); + if (content == null || content.isEmpty()) { + return Collections.emptyList(); + } + return Collections.singletonList(URL.decode(content)); + } + + @Override + protected void doSaveSubscriberData(SubscriberMetadataIdentifier id, String urlListStr) { + storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), urlListStr, false); + } + + @Override + protected String doGetSubscribedURLs(SubscriberMetadataIdentifier id) { + return getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); + } + + @Override + public String getServiceDefinition(MetadataIdentifier id) { + return getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); + } + + // ------------------------------------------------------------------------- + // App-level metadata (Dubbo 3.x application-level service discovery) + // ------------------------------------------------------------------------- + + @Override + public void publishAppMetadata(SubscriberMetadataIdentifier id, MetadataInfo metadataInfo) { + storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), metadataInfo.getContent(), false); + } + + @Override + public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier id, Map instanceMetadata) { + String content = getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); + return org.apache.dubbo.common.utils.JsonUtils.toJavaObject(content, MetadataInfo.class); + } + + @Override + public void unPublishAppMetadata(SubscriberMetadataIdentifier id, MetadataInfo metadataInfo) { + deleteMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); + } + + // ------------------------------------------------------------------------- + // Service-to-application mapping + // ------------------------------------------------------------------------- + + @Override + public boolean registerServiceAppMapping( + String serviceInterface, String defaultMappingGroup, String newConfigContent, Object ticket) { + try { + if (ticket != null && !(ticket instanceof String)) { + throw new IllegalArgumentException("Redis CAS requires a String ticket"); + } + return storeMappingWithCas( + buildMappingKey(defaultMappingGroup), + serviceInterface, + newConfigContent, + (String) ticket); + } catch (Exception e) { + log.warn("registerServiceAppMapping failed.", e); + return false; + } + } + + @Override + public ConfigItem getConfigItem(String serviceKey, String group) { + String key = buildMappingKey(group); + String content = getMappingField(key, serviceKey); + return new ConfigItem(content, content); + } + + @Override + public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { + String pubSubKey = buildPubSubKey(); + // Register the RTopic listener once per pubSubKey + topicMap.computeIfAbsent(pubSubKey, k -> { + RTopic topic = getRedisson().getTopic(k, StringCodec.INSTANCE); + topic.addListener(String.class, (channel, msg) -> { + String applicationNames = getMappingField(buildMappingKey(DEFAULT_MAPPING_GROUP), msg); + MappingChangedEvent event = new MappingChangedEvent(msg, getAppNames(applicationNames)); + Set ls = listenerMap.get(msg); + if (!CollectionUtils.isEmpty(ls)) { + ls.forEach(l -> l.onEvent(event)); + } + }); + return topic; + }); + listenerMap.computeIfAbsent(serviceKey, k -> new ConcurrentHashSet<>()).add(listener); + return getServiceAppMapping(serviceKey, url); + } + + @Override + public Set getServiceAppMapping(String serviceKey, URL url) { + return getAppNames(getMappingField(buildMappingKey(DEFAULT_MAPPING_GROUP), serviceKey)); + } + + @Override + public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { + Set ls = listenerMap.get(serviceKey); + if (ls != null) { + ls.remove(listener); + if (ls.isEmpty()) { + listenerMap.remove(serviceKey); + // If no listeners remain for any key, remove the topic subscription + if (listenerMap.isEmpty()) { + RTopic topic = topicMap.remove(buildPubSubKey()); + if (topic != null) { + topic.removeAllListeners(); + } + } + } + } + } + + // ------------------------------------------------------------------------- + // Internal Redis helpers + // ------------------------------------------------------------------------- + + private void storeMetadata(String key, String value, boolean ephemeral) { + try { + if (ephemeral && ttlMs > 0) { + getRedisson().getBucket(key, StringCodec.INSTANCE).set(value, Duration.ofMillis(ttlMs)); + } else { + getRedisson().getBucket(key, StringCodec.INSTANCE).set(value); + } + } catch (Exception e) { + String msg = "Failed to store metadata key=" + key + ", cause: " + e.getMessage(); + log.error(msg, e); + throw new RpcException(msg, e); + } + } + + private String getMetadata(String key) { + try { + return getRedisson().getBucket(key, StringCodec.INSTANCE).get(); + } catch (Exception e) { + String msg = "Failed to get metadata key=" + key + ", cause: " + e.getMessage(); + log.error(msg, e); + throw new RpcException(msg, e); + } + } + + private void deleteMetadata(String key) { + try { + getRedisson().getBucket(key, StringCodec.INSTANCE).delete(); + } catch (Exception e) { + String msg = "Failed to delete metadata key=" + key + ", cause: " + e.getMessage(); + log.error(msg, e); + throw new RpcException(msg, e); + } + } + + private String getMappingField(String key, String field) { + try { + return getRedisson().getMap(key, StringCodec.INSTANCE).get(field); + } catch (Exception e) { + String msg = "Failed to get mapping key=" + key + " field=" + field + ", cause: " + e.getMessage(); + log.error(msg, e); + throw new RpcException(msg, e); + } + } + + /** + * Atomic CAS on a hash field via Lua script. + * Updates field to newValue only when the current value equals ticket (or field is absent / ticket is null). + * On success, publishes a change notification to the pub/sub channel. + */ + private boolean storeMappingWithCas(String key, String field, String newValue, String ticket) { + try { + Long result = getRedisson().getScript(StringCodec.INSTANCE).eval( + RScript.Mode.READ_WRITE, + CAS_LUA, + RScript.ReturnType.INTEGER, + Collections.singletonList(key), + field, newValue, ticket == null ? "" : ticket + ); + if (Long.valueOf(1L).equals(result)) { + getRedisson().getTopic(buildPubSubKey(), StringCodec.INSTANCE).publish(field); + return true; + } + return false; + } catch (Exception e) { + String msg = "Failed to store mapping key=" + key + " field=" + field + ", cause: " + e.getMessage(); + log.error(msg, e); + throw new RpcException(msg, e); + } + } + + private String buildMappingKey(String mappingGroup) { + return this.root + GROUP_CHAR_SEPARATOR + mappingGroup; + } + + private String buildPubSubKey() { + return buildMappingKey(DEFAULT_MAPPING_GROUP) + GROUP_CHAR_SEPARATOR + QUEUES_KEY; + } +} diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReportFactory.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReportFactory.java new file mode 100644 index 000000000..51aa80f03 --- /dev/null +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedissonMetadataReportFactory.java @@ -0,0 +1,17 @@ +package org.apache.dubbo.metadata.store.redis; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; + +/** + * RedisMetadataReportFactory. + */ +public class RedissonMetadataReportFactory extends AbstractMetadataReportFactory { + + @Override + public MetadataReport createMetadataReport(URL url) { + return new RedissonMetadataReport(url); + } + +} diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory new file mode 100644 index 000000000..c69ece412 --- /dev/null +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory @@ -0,0 +1 @@ +redis=org.apache.dubbo.metadata.store.redis.RedissonMetadataReportFactory diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml index ddf7f6980..3000f2b3a 100644 --- a/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml @@ -19,6 +19,8 @@ dubbo: password: ${spring.cloud.nacos.password} parameters: namespace: ${spring.profiles.active} + # 已经采用框架内 Redisson 实现元数据中心 + # 以下配置不用管占位用 不然 dubbo 会直接用 nacos 当注册中心 直接配好框架自带的 Redisson 即可 metadata-report: address: redis://${spring.data.redis.host:localhost}:${spring.data.redis.port:6379} group: DUBBO_GROUP diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java index b88c3e429..c8038623c 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java @@ -33,7 +33,7 @@ public class ExcelBigNumberConvert implements Converter { @Override public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return Convert.toLong(cellData.getData()); + return Convert.toLong(cellData.getStringValue()); } @Override diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 05c79c410..9123e5e9a 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -130,8 +130,9 @@ public class ExcelDownHandler implements SheetWriteHandler { } if (ObjectUtil.isNotEmpty(options)) { // 仅当下拉可选项不为空时执行 - if (options.size() > 20) { - // 这里限制如果可选项大于20,则使用额外表形式 + int totalCharacter = options.stream().mapToInt(String::length).sum() + options.size(); + if (options.size() > 20 || totalCharacter > 255) { + // 这里限制如果可选项大于20 或 总字符数超过255,则使用额外表形式 dropDownWithSheet(helper, workbook, sheet, index, options); } else { // 否则使用固定值形式 diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java index 2afc85af3..084267b56 100644 --- a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -12,6 +12,7 @@ import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.domain.R; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MessageUtils; @@ -76,16 +77,16 @@ public class RepeatSubmitAspect { */ @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { - if (jsonResult instanceof R r) { - try { + try { + if (jsonResult instanceof R r) { // 成功则不删除redis数据 保证在有效时间内无法重复提交 - if (r.getCode() == R.SUCCESS) { + if (r.getCode() == HttpStatus.SUCCESS) { return; } RedisUtils.deleteObject(KEY_CACHE.get()); - } finally { - KEY_CACHE.remove(); } + } finally { + KEY_CACHE.remove(); } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java index 84a8a5c2a..4e410736d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java @@ -46,6 +46,24 @@ public class DataBaseHelper { } } + /** + * 获取指定数据源对应的数据库类型 + * + * @param dsName 数据源名称 + * @return 指定数据库对应的 DataBaseType 枚举,找不到时默认返回 MY_SQL + * @throws ServiceException 当获取数据库连接或元数据出现异常时抛出业务异常 + */ + public static DataBaseType getDataBaseType(String dsName) { + DataSource dataSource = DS.getDataSource(dsName); + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new RuntimeException("获取数据库类型失败", e); + } + } + /** * 根据当前数据库类型,生成兼容的 FIND_IN_SET 语句片段 *

diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfiguration.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfiguration.java index d68283628..28a770d55 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfiguration.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfiguration.java @@ -65,9 +65,10 @@ public class RedisConfiguration { // 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现) .setUseScriptCache(true) .setCodec(codec); - if (SpringUtils.isVirtual()) { - config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-")); - } + // netty 对虚拟线程适配有问题 暂时禁止使用 + //if (SpringUtils.isVirtual()) { + // config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-")); + //} RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { // 使用单机模式 diff --git a/ruoyi-common/ruoyi-common-satoken/pom.xml b/ruoyi-common/ruoyi-common-satoken/pom.xml index 5643acb85..eaa809f93 100644 --- a/ruoyi-common/ruoyi-common-satoken/pom.xml +++ b/ruoyi-common/ruoyi-common-satoken/pom.xml @@ -46,6 +46,11 @@ caffeine + + org.springframework + spring-webmvc + + diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java index 7a5d82c3e..22c1448a9 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java @@ -2,7 +2,6 @@ package org.dromara.common.web.handler; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; -import com.fasterxml.jackson.core.JsonParseException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; @@ -12,8 +11,10 @@ import org.dromara.common.core.domain.R; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.SseException; import org.dromara.common.core.exception.base.BaseException; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.json.utils.JsonUtils; +import org.springframework.boot.json.JsonParseException; import org.springframework.context.MessageSourceResolvable; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.expression.ExpressionException; @@ -80,7 +81,7 @@ public class GlobalExceptionHandler { public R handleServletException(ServletException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生未知异常.", requestURI, e); - return R.fail(e.getMessage()); + return R.fail("发生未知异常,请联系管理员"); } /** @@ -119,7 +120,7 @@ public class GlobalExceptionHandler { public R handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}'不存在.", requestURI); - return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage()); + return R.fail(HttpStatus.HTTP_NOT_FOUND, "请求地址不存在"); } /** @@ -150,7 +151,7 @@ public class GlobalExceptionHandler { public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生未知异常.", requestURI, e); - return R.fail(e.getMessage()); + return R.fail("发生未知异常,请联系管理员"); } /** @@ -160,7 +161,7 @@ public class GlobalExceptionHandler { public R handleException(Exception e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生系统异常.", requestURI, e); - return R.fail(e.getMessage()); + return R.fail("发生系统异常,请联系管理员"); } /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/resources/ip2region_v4.xdb b/ruoyi-common/ruoyi-common-web/src/main/resources/ip2region_v4.xdb index 707ea3d45..6b53b1f61 100644 Binary files a/ruoyi-common/ruoyi-common-web/src/main/resources/ip2region_v4.xdb and b/ruoyi-common/ruoyi-common-web/src/main/resources/ip2region_v4.xdb differ diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java index 3fc1682fe..0711cc5a5 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java @@ -331,7 +331,7 @@ public class GenTableServiceImpl implements IGenTableService { VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName()); for (String template : templates) { // 渲染模板 StringWriter sw = new StringWriter(); @@ -374,7 +374,7 @@ public class GenTableServiceImpl implements IGenTableService { VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName()); for (String template : templates) { if (!StringUtils.containsAny(template, "sql.vm", "api.ts.vm", "types.ts.vm", "index.vue.vm", "index-tree.vue.vm")) { // 渲染模板 @@ -478,7 +478,7 @@ public class GenTableServiceImpl implements IGenTableService { VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName()); for (String template : templates) { // 渲染模板 StringWriter sw = new StringWriter(); diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/VelocityUtils.java b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/VelocityUtils.java index 5f4eecc71..98fbb533c 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/VelocityUtils.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/VelocityUtils.java @@ -109,7 +109,7 @@ public class VelocityUtils { * * @return 模板列表 */ - public static List getTemplateList(String tplCategory) { + public static List getTemplateList(String tplCategory, String dsName) { List templates = new ArrayList<>(); templates.add("vm/java/domain.java.vm"); templates.add("vm/java/vo.java.vm"); @@ -119,7 +119,7 @@ public class VelocityUtils { templates.add("vm/java/serviceImpl.java.vm"); templates.add("vm/java/controller.java.vm"); templates.add("vm/xml/mapper.xml.vm"); - DataBaseType dataBaseType = DataBaseHelper.getDataBaseType(); + DataBaseType dataBaseType = DataBaseHelper.getDataBaseType(dsName); if (dataBaseType.isOracle()) { templates.add("vm/sql/oracle/sql.vm"); } else if (dataBaseType.isPostgreSql()) { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java index 5163cbf23..5b1b9e7d7 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java @@ -104,6 +104,6 @@ public class SysDeptVo implements Serializable { /** * 子菜单 */ - private List children = new ArrayList<>(); + private List children = new ArrayList<>(); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java index 47aec6c92..3efb03f5e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java @@ -404,6 +404,7 @@ public class RemoteUserServiceImpl implements RemoteUserService { * @param userIds 用户 ID 列表 * @return Map,其中 key 为用户 ID,value 为对应的用户昵称 */ + @Override public Map selectUserNicksByIds(List userIds) { if (CollUtil.isEmpty(userIds)) { return Collections.emptyMap(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index b28574cac..bce884823 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -235,8 +235,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { variables.put(SUBMIT, true); } + Map insVariableMap = ins.getVariableMap(); // 设置弹窗处理人 - Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); + Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), insVariableMap); if (CollUtil.isNotEmpty(assigneeMap)) { variables.putAll(assigneeMap); } @@ -252,7 +253,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { .flowStatus(BusinessStatusEnum.WAITING.getStatus()) .hisStatus(TaskStatusEnum.PASS.getStatus()) .hisTaskExt(completeTaskBo.getFileId()); - Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false)); + Boolean autoPass = Convert.toBool(insVariableMap.getOrDefault(AUTO_PASS, false)); skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass); return true; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml index 1a244c794..81dc37c44 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml @@ -88,6 +88,7 @@ and b.del_flag = '0' and c.del_flag = '0' and a.node_type in ('1','3','4') + and a.flow_status ]]> 'copy' ) t ${ew.getCustomSqlSegment} diff --git a/script/config/nacos/application-common.yml b/script/config/nacos/application-common.yml index 053113150..5e5a8023d 100644 --- a/script/config/nacos/application-common.yml +++ b/script/config/nacos/application-common.yml @@ -32,13 +32,6 @@ dubbo: consumer: # 超时时间 timeout: 3000 - metadata-report: - # Redis集群开关 - cluster: false - parameters: - # 集群地址 cluster 为 true 生效 - # 集群把所有Redis集群节点写到这里就行了 - backup: 127.0.0.1:6379,127.0.0.1:6381 # 自定义配置 custom: # 全局请求log diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index ebfc2ab76..6954bf458 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -27,7 +27,7 @@ services: network_mode: "host" nacos: - image: ruoyi/ruoyi-nacos:2.6.0 + image: ruoyi/ruoyi-nacos:2.6.1 container_name: nacos ports: - "8848:8848" @@ -95,7 +95,7 @@ services: network_mode: "host" seata-server: - image: ruoyi/ruoyi-seata-server:2.6.0 + image: ruoyi/ruoyi-seata-server:2.6.1 container_name: seata-server ports: - "7091:7091" @@ -134,7 +134,7 @@ services: network_mode: "host" ruoyi-monitor: - image: ruoyi/ruoyi-monitor:2.6.0 + image: ruoyi/ruoyi-monitor:2.6.1 container_name: ruoyi-monitor environment: # 时区上海 @@ -150,7 +150,7 @@ services: network_mode: "host" ruoyi-snailjob-server: - image: ruoyi/ruoyi-snailjob-server:2.6.0 + image: ruoyi/ruoyi-snailjob-server:2.6.1 container_name: ruoyi-snailjob-server environment: # 时区上海 @@ -164,7 +164,7 @@ services: network_mode: "host" ruoyi-gateway: - image: ruoyi/ruoyi-gateway:2.6.0 + image: ruoyi/ruoyi-gateway:2.6.1 container_name: ruoyi-gateway environment: # 时区上海 @@ -180,7 +180,7 @@ services: network_mode: "host" ruoyi-auth: - image: ruoyi/ruoyi-auth:2.6.0 + image: ruoyi/ruoyi-auth:2.6.1 container_name: ruoyi-auth environment: # 时区上海 @@ -196,7 +196,7 @@ services: network_mode: "host" ruoyi-system: - image: ruoyi/ruoyi-system:2.6.0 + image: ruoyi/ruoyi-system:2.6.1 container_name: ruoyi-system environment: # 时区上海 @@ -212,7 +212,7 @@ services: network_mode: "host" ruoyi-gen: - image: ruoyi/ruoyi-gen:2.6.0 + image: ruoyi/ruoyi-gen:2.6.1 container_name: ruoyi-gen environment: # 时区上海 @@ -228,7 +228,7 @@ services: network_mode: "host" ruoyi-job: - image: ruoyi/ruoyi-job:2.6.0 + image: ruoyi/ruoyi-job:2.6.1 container_name: ruoyi-job environment: # 时区上海 @@ -246,7 +246,7 @@ services: network_mode: "host" ruoyi-resource: - image: ruoyi/ruoyi-resource:2.6.0 + image: ruoyi/ruoyi-resource:2.6.1 container_name: ruoyi-resource environment: # 时区上海 @@ -262,7 +262,7 @@ services: network_mode: "host" ruoyi-workflow: - image: ruoyi/ruoyi-workflow:2.6.0 + image: ruoyi/ruoyi-workflow:2.6.1 container_name: ruoyi-workflow environment: # 时区上海 diff --git a/script/sql/oracle/oracle_ry_job.sql b/script/sql/oracle/oracle_ry_job.sql index 2a7f11f9a..7a9080666 100644 --- a/script/sql/oracle/oracle_ry_job.sql +++ b/script/sql/oracle/oracle_ry_job.sql @@ -484,6 +484,7 @@ CREATE TABLE sj_job ( id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + biz_id varchar2(64) NOT NULL, group_name varchar2(64) NULL, job_name varchar2(64) NULL, args_str clob DEFAULT NULL NULL, @@ -519,9 +520,11 @@ ALTER TABLE sj_job CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); +CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id); COMMENT ON COLUMN sj_job.id IS '主键'; COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.biz_id IS '业务ID'; COMMENT ON COLUMN sj_job.group_name IS '组名称'; COMMENT ON COLUMN sj_job.job_name IS '名称'; COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; @@ -551,7 +554,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; COMMENT ON TABLE sj_job IS '任务信息'; -INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate); +INSERT INTO sj_job(namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'demo-job', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate); -- sj_job_log_message CREATE TABLE sj_job_log_message @@ -738,7 +741,7 @@ CREATE TABLE sj_retry_summary id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, group_name varchar2(64) DEFAULT '' NULL, - scene_name varchar2(64) DEFAULT '' NULL, + scene_name varchar2(50) DEFAULT '' NULL, trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL, running_num number DEFAULT 0 NOT NULL, finish_num number DEFAULT 0 NOT NULL, @@ -774,6 +777,7 @@ CREATE TABLE sj_workflow id number GENERATED ALWAYS AS IDENTITY, workflow_name varchar2(64) NULL, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + biz_id varchar2(64) NOT NULL, group_name varchar2(64) NULL, workflow_status smallint DEFAULT 1 NOT NULL, trigger_type smallint NOT NULL, @@ -799,10 +803,12 @@ ALTER TABLE sj_workflow CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); +CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id); COMMENT ON COLUMN sj_workflow.id IS '主键'; COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID'; COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; diff --git a/script/sql/postgres/postgres_ry_job.sql b/script/sql/postgres/postgres_ry_job.sql index 9980c3b43..f355632c0 100644 --- a/script/sql/postgres/postgres_ry_job.sql +++ b/script/sql/postgres/postgres_ry_job.sql @@ -445,6 +445,7 @@ CREATE TABLE sj_job ( id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id varchar(64) NOT NULL, group_name varchar(64) NOT NULL, job_name varchar(64) NOT NULL, args_str text NULL DEFAULT NULL, @@ -477,9 +478,11 @@ CREATE TABLE sj_job CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); +CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id); COMMENT ON COLUMN sj_job.id IS '主键'; COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.biz_id IS '业务ID'; COMMENT ON COLUMN sj_job.group_name IS '组名称'; COMMENT ON COLUMN sj_job.job_name IS '名称'; COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; @@ -509,7 +512,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; COMMENT ON TABLE sj_job IS '任务信息'; -INSERT INTO sj_job VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now()); +INSERT INTO sj_job VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now()); -- sj_job_log_message CREATE TABLE sj_job_log_message @@ -684,7 +687,7 @@ CREATE TABLE sj_retry_summary id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name varchar(64) NOT NULL DEFAULT '', - scene_name varchar(64) NOT NULL DEFAULT '', + scene_name varchar(50) NOT NULL DEFAULT '', trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, running_num int NOT NULL DEFAULT 0, finish_num int NOT NULL DEFAULT 0, @@ -717,6 +720,7 @@ CREATE TABLE sj_workflow id bigserial PRIMARY KEY, workflow_name varchar(64) NOT NULL, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id varchar(64) NOT NULL, group_name varchar(64) NOT NULL, workflow_status smallint NOT NULL DEFAULT 1, trigger_type smallint NOT NULL, @@ -739,10 +743,12 @@ CREATE TABLE sj_workflow CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); +CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id); COMMENT ON COLUMN sj_workflow.id IS '主键'; COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID'; COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; diff --git a/script/sql/ry-job.sql b/script/sql/ry-job.sql index dd8124cf5..e7b627f49 100644 --- a/script/sql/ry-job.sql +++ b/script/sql/ry-job.sql @@ -280,6 +280,7 @@ CREATE TABLE `sj_job` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `biz_id` varchar(64) NOT NULL COMMENT '业务ID', `group_name` varchar(64) NOT NULL COMMENT '组名称', `job_name` varchar(64) NOT NULL COMMENT '名称', `args_str` text DEFAULT NULL COMMENT '执行方法参数', @@ -310,12 +311,13 @@ CREATE TABLE `sj_job` PRIMARY KEY (`id`), KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`), - KEY `idx_create_dt` (`create_dt`) + KEY `idx_create_dt` (`create_dt`), + UNIQUE KEY `uk_sj_job_01` (`namespace_id`, `biz_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息'; -INSERT INTO `sj_job` VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now()); +INSERT INTO `sj_job` VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now()); CREATE TABLE `sj_job_log_message` ( @@ -443,6 +445,7 @@ CREATE TABLE `sj_workflow` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `workflow_name` varchar(64) NOT NULL COMMENT '工作流名称', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `biz_id` varchar(64) NOT NULL COMMENT '业务ID', `group_name` varchar(64) NOT NULL COMMENT '组名称', `workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启', `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', @@ -463,7 +466,8 @@ CREATE TABLE `sj_workflow` `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), KEY `idx_create_dt` (`create_dt`), - KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + UNIQUE KEY `uk_sj_workflow_01` (`namespace_id`, `biz_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='工作流';