liuchuan 7 месяцев назад
Сommit
2c444e4bbc
100 измененных файлов с 9283 добавлено и 0 удалено
  1. 67 0
      components/component-redis/pom.xml
  2. 34 0
      components/component-redis/src/main/java/com/crunii/micro/component/redis/cache/CacheNodeExpire.java
  3. 94 0
      components/component-redis/src/main/java/com/crunii/micro/component/redis/config/RedisCacheAutoConfiguration.java
  4. 46 0
      components/component-redis/src/main/java/com/crunii/micro/component/redis/config/RedissonReactorAutoConfiguration.java
  5. 173 0
      components/component-redis/src/main/java/com/crunii/micro/component/redis/lock/DistributeLock.java
  6. 42 0
      components/component-redis/src/main/java/com/crunii/micro/component/redis/properties/MicroCacheProperties.java
  7. 3 0
      components/component-redis/src/main/resources/META-INF/spring.factories
  8. 50 0
      components/pom.xml
  9. 44 0
      micro-common-component/micro-common-web/pom.xml
  10. 24 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/I18nConfig.java
  11. 38 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/NacosConfig.java
  12. 30 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/UndertowConfig.java
  13. 31 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/core/I18nLocaleResolver.java
  14. 25 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/nacos/CustomNacosWatch.java
  15. 4 0
      micro-common-component/micro-common-web/src/main/resources/META-INF/spring.factories
  16. 19 0
      micro-common-component/pom.xml
  17. 378 0
      micro-common/pom.xml
  18. 54 0
      micro-common/src/main/java/com/crunii/micro/common/annotations/LimitUploadFileTypeCheck.java
  19. 176 0
      micro-common/src/main/java/com/crunii/micro/common/captcha/MicroCaptcha.java
  20. 28 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/BlockingRejectedExecutionHandler.java
  21. 46 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityCallable.java
  22. 123 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityExecutor.java
  23. 37 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityRunnable.java
  24. 34 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityTask.java
  25. 33 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeList.java
  26. 34 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeSortedSet.java
  27. 17 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/RemoveId.java
  28. 77 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeLinkedList.java
  29. 153 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeTreeSet.java
  30. 48 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/SimpleRemoveId.java
  31. 81 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMap.java
  32. 176 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMapImpl.java
  33. 42 0
      micro-common/src/main/java/com/crunii/micro/common/config/CommConfiguration.java
  34. 41 0
      micro-common/src/main/java/com/crunii/micro/common/config/LocalDateTimeConverter.java
  35. 24 0
      micro-common/src/main/java/com/crunii/micro/common/config/NacosConfiguration.java
  36. 176 0
      micro-common/src/main/java/com/crunii/micro/common/config/RestTemplateConfiguration.java
  37. 42 0
      micro-common/src/main/java/com/crunii/micro/common/config/StartTimeConfiguration.java
  38. 182 0
      micro-common/src/main/java/com/crunii/micro/common/config/SwaggerConfiguration.java
  39. 61 0
      micro-common/src/main/java/com/crunii/micro/common/config/SwaggerConflictFixConfiguration.java
  40. 43 0
      micro-common/src/main/java/com/crunii/micro/common/constants/BNConstants.java
  41. 199 0
      micro-common/src/main/java/com/crunii/micro/common/constants/CommonConstants.java
  42. 88 0
      micro-common/src/main/java/com/crunii/micro/common/constants/DSConstants.java
  43. 11 0
      micro-common/src/main/java/com/crunii/micro/common/constants/FontConstants.java
  44. 17 0
      micro-common/src/main/java/com/crunii/micro/common/constants/LockConstants.java
  45. 39 0
      micro-common/src/main/java/com/crunii/micro/common/constants/LogConstants.java
  46. 141 0
      micro-common/src/main/java/com/crunii/micro/common/constants/RedisKeys.java
  47. 426 0
      micro-common/src/main/java/com/crunii/micro/common/converter/ConvertUtilBean.java
  48. 41 0
      micro-common/src/main/java/com/crunii/micro/common/converter/DateConverter.java
  49. 33 0
      micro-common/src/main/java/com/crunii/micro/common/dto/CBTree.java
  50. 31 0
      micro-common/src/main/java/com/crunii/micro/common/dto/CaptchaInfo.java
  51. 39 0
      micro-common/src/main/java/com/crunii/micro/common/dto/FileResponse.java
  52. 19 0
      micro-common/src/main/java/com/crunii/micro/common/dto/GeoCoordinate.java
  53. 20 0
      micro-common/src/main/java/com/crunii/micro/common/dto/NoModelWriteData.java
  54. 40 0
      micro-common/src/main/java/com/crunii/micro/common/dto/OrderProp.java
  55. 68 0
      micro-common/src/main/java/com/crunii/micro/common/dto/PageReq.java
  56. 55 0
      micro-common/src/main/java/com/crunii/micro/common/dto/PageRsp.java
  57. 237 0
      micro-common/src/main/java/com/crunii/micro/common/dto/Result.java
  58. 27 0
      micro-common/src/main/java/com/crunii/micro/common/dto/SSHHost.java
  59. 64 0
      micro-common/src/main/java/com/crunii/micro/common/dto/ServiceInst.java
  60. 70 0
      micro-common/src/main/java/com/crunii/micro/common/dto/TreeNode.java
  61. 23 0
      micro-common/src/main/java/com/crunii/micro/common/dto/UserTest.java
  62. 61 0
      micro-common/src/main/java/com/crunii/micro/common/dto/WorkerC.java
  63. 12 0
      micro-common/src/main/java/com/crunii/micro/common/dto/WorkerId.java
  64. 47 0
      micro-common/src/main/java/com/crunii/micro/common/enums/AuditStatusEnums.java
  65. 47 0
      micro-common/src/main/java/com/crunii/micro/common/enums/GovInstitutionLevel.java
  66. 145 0
      micro-common/src/main/java/com/crunii/micro/common/enums/LimitUploadFileType.java
  67. 45 0
      micro-common/src/main/java/com/crunii/micro/common/enums/PolicyLevelEnums.java
  68. 56 0
      micro-common/src/main/java/com/crunii/micro/common/enums/PolicyTypeEnums.java
  69. 46 0
      micro-common/src/main/java/com/crunii/micro/common/enums/ResultEnums.java
  70. 24 0
      micro-common/src/main/java/com/crunii/micro/common/enums/TaskTypeEnums.java
  71. 97 0
      micro-common/src/main/java/com/crunii/micro/common/excel/ExcelCellStyle.java
  72. 60 0
      micro-common/src/main/java/com/crunii/micro/common/excel/ExcelListenerUtil.java
  73. 144 0
      micro-common/src/main/java/com/crunii/micro/common/excel/ExcelOperFieldConfig.java
  74. 49 0
      micro-common/src/main/java/com/crunii/micro/common/excel/ExcelSheetWriteHandler.java
  75. 288 0
      micro-common/src/main/java/com/crunii/micro/common/excel/ExcelUtil.java
  76. 358 0
      micro-common/src/main/java/com/crunii/micro/common/excel/excel/ExcelExportUtil.java
  77. 174 0
      micro-common/src/main/java/com/crunii/micro/common/excel/excel/ExcelImportUtil.java
  78. 50 0
      micro-common/src/main/java/com/crunii/micro/common/excel/excel/annotation/ExcelField.java
  79. 31 0
      micro-common/src/main/java/com/crunii/micro/common/excel/excel/annotation/ExcelSheet.java
  80. 179 0
      micro-common/src/main/java/com/crunii/micro/common/excel/excel/util/FieldReflectionUtil.java
  81. 42 0
      micro-common/src/main/java/com/crunii/micro/common/exception/BusinessException.java
  82. 29 0
      micro-common/src/main/java/com/crunii/micro/common/exception/ErrorCode.java
  83. 112 0
      micro-common/src/main/java/com/crunii/micro/common/exception/MicroException.java
  84. 51 0
      micro-common/src/main/java/com/crunii/micro/common/exception/enums/GlobalErrorCodeConstants.java
  85. 12 0
      micro-common/src/main/java/com/crunii/micro/common/file/LineHandler.java
  86. 19 0
      micro-common/src/main/java/com/crunii/micro/common/file/LineHandlerImpl.java
  87. 195 0
      micro-common/src/main/java/com/crunii/micro/common/file/MultReadFile.java
  88. 90 0
      micro-common/src/main/java/com/crunii/micro/common/file/Range.java
  89. 153 0
      micro-common/src/main/java/com/crunii/micro/common/file/RangeReader.java
  90. 193 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/ChannelSftpFactory.java
  91. 122 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/ChannelSftpPoolFactory.java
  92. 254 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/FTPClientFactory.java
  93. 124 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/FTPClientPoolFactory.java
  94. 245 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/FtpRemoteFile.java
  95. 84 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/RemoteFile.java
  96. 231 0
      micro-common/src/main/java/com/crunii/micro/common/ftp/SftpRemoteFile.java
  97. 72 0
      micro-common/src/main/java/com/crunii/micro/common/http/HttpClientHolder.java
  98. 617 0
      micro-common/src/main/java/com/crunii/micro/common/http/HttpClientUtil.java
  99. 154 0
      micro-common/src/main/java/com/crunii/micro/common/http/HttpClientUtilDemo.java
  100. 53 0
      micro-common/src/main/java/com/crunii/micro/common/http/HttpReqRsp.java

+ 67 - 0
components/component-redis/pom.xml

@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.crunii.microservice</groupId>
+		<artifactId>components</artifactId>
+		<version>${micro.version}</version>
+	</parent>
+
+	<artifactId>component-redis</artifactId>
+	<name>component-redis</name>
+	<packaging>jar</packaging>
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-dependencies</artifactId>
+				<version>${boot.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework.cloud</groupId>
+				<artifactId>spring-cloud-dependencies</artifactId>
+				<version>${cloud.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>com.alibaba.cloud</groupId>
+				<artifactId>spring-cloud-alibaba-dependencies</artifactId>
+				<version>${alibaba.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+	<dependencies>
+
+		<dependency>
+			<groupId>com.crunii.microservice</groupId>
+			<artifactId>micro-common</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.redisson</groupId>
+			<artifactId>redisson-spring-boot-starter</artifactId>
+			<exclusions>
+				<exclusion>
+					<artifactId>redisson-spring-data-30</artifactId>
+					<groupId>org.redisson</groupId>
+				</exclusion>
+				<exclusion>
+					<artifactId>spring-boot-starter-actuator</artifactId>
+					<groupId>org.springframework.boot</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
+		<dependency>
+			<groupId>org.redisson</groupId>
+			<artifactId>redisson-spring-data-26</artifactId>
+		</dependency>
+
+	</dependencies>
+</project>

+ 34 - 0
components/component-redis/src/main/java/com/crunii/micro/component/redis/cache/CacheNodeExpire.java

@@ -0,0 +1,34 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.component.redis.cache;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.Duration;
+
+/**
+ * @author 田平 create 2019年9月5日下午3:08:09
+ */
+@Getter
+@Setter
+@ToString
+public class CacheNodeExpire implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 应用名称
+	 */
+	private String appName;
+	/**
+	 * 缓存名称
+	 */
+	private String cacheName;
+	/**
+	 * 缓存过期时间,单位是秒
+	 */
+	private Duration timeToLive;
+}

+ 94 - 0
components/component-redis/src/main/java/com/crunii/micro/component/redis/config/RedisCacheAutoConfiguration.java

@@ -0,0 +1,94 @@
+package com.crunii.micro.component.redis.config;
+
+import com.crunii.micro.component.redis.properties.MicroCacheProperties;
+import org.redisson.spring.data.connection.RedissonConnectionFactory;
+import org.redisson.spring.starter.RedissonAutoConfiguration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+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.context.annotation.Primary;
+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.core.ReactiveRedisTemplate;
+import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author 田平 create 2020年1月22日上午10:44:32
+ */
+@Configuration
+@ConditionalOnProperty(name = "spring.redis.password")
+@AutoConfigureAfter(RedissonAutoConfiguration.class)
+@EnableConfigurationProperties(value = { MicroCacheProperties.class, CacheProperties.class })
+public class RedisCacheAutoConfiguration {
+
+	@Autowired
+	private MicroCacheProperties microCacheProperties;
+
+	@Autowired
+	private CacheProperties cacheProperties;
+
+	@Value("${spring.application.name}")
+	private String appName;
+
+
+	@Primary
+	@Bean(name = "cacheManager")
+	public CacheManager regCacheManager(RedisConnectionFactory connectionFactory) {
+
+		// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
+		// 设置缓存的默认过期时间,也是使用Duration设置 6小时
+		// 不缓存空值
+		RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
+				.entryTtl(cacheProperties.getRedis().getTimeToLive())
+				.prefixCacheNameWith(cacheProperties.getRedis().getKeyPrefix())
+				.disableCachingNullValues();
+
+		Map<String, RedisCacheConfiguration> initialCacheConfigurations = new HashMap<>(8);
+
+		// 单独设置某个缓存的配置,例如过期时间等
+		microCacheProperties.getExpires()
+				.stream()
+				.filter(expire -> expire.getAppName().equals(appName))
+				.forEach(expire -> {
+					RedisCacheConfiguration specialConfig = RedisCacheConfiguration.defaultCacheConfig();
+					specialConfig = specialConfig.entryTtl(expire.getTimeToLive())
+							.prefixCacheNameWith(cacheProperties.getRedis().getKeyPrefix());
+					initialCacheConfigurations.put(expire.getCacheName(), specialConfig);
+				});
+
+		// 全局缓存前缀,全局缓存单独的前缀设置,项目重启不会刷新全局缓存
+		microCacheProperties.getGlobalCacheNames().forEach(name -> {
+			RedisCacheConfiguration globalConfig = RedisCacheConfiguration.defaultCacheConfig();
+			globalConfig = globalConfig.entryTtl(microCacheProperties.getGlobalTimeToLive())
+					.prefixCacheNameWith(microCacheProperties.getGlobalPrefix());
+			initialCacheConfigurations.put(name, globalConfig);
+		});
+
+		RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
+				.cacheDefaults(defaultConfig)
+				.withInitialCacheConfigurations(initialCacheConfigurations)
+				.build();
+
+		//项目重启会情况缓存
+		String keyPrefix = appName + "*";
+		Set<byte[]> keys = connectionFactory.getConnection().keys(keyPrefix.getBytes());
+		if (keys.size() > 0) {
+			connectionFactory.getConnection().del(keys.toArray(new byte[keys.size()][]));
+		}
+		return cacheManager;
+	}
+}

+ 46 - 0
components/component-redis/src/main/java/com/crunii/micro/component/redis/config/RedissonReactorAutoConfiguration.java

@@ -0,0 +1,46 @@
+package com.crunii.micro.component.redis.config;
+
+import com.crunii.micro.component.redis.properties.MicroCacheProperties;
+import org.redisson.spring.data.connection.RedissonConnectionFactory;
+import org.redisson.spring.starter.RedissonAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
+import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+
+/**
+ * @author 田平 create 2020年1月22日上午10:44:32
+ */
+@Configuration
+@ConditionalOnProperty(name = "spring.redis.password")
+@AutoConfigureAfter(RedissonAutoConfiguration.class)
+@AutoConfigureBefore(RedisReactiveAutoConfiguration.class)
+@EnableConfigurationProperties(value = { MicroCacheProperties.class, CacheProperties.class })
+public class RedissonReactorAutoConfiguration {
+
+	@Bean(name = "reactiveRedisTemplate")
+	public ReactiveRedisTemplate<Object, Object> reactiveRedisTemplate(RedissonConnectionFactory redissonConnectionFactory,
+																	   ResourceLoader resourceLoader) {
+		JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(
+				resourceLoader.getClassLoader());
+		RedisSerializationContext<Object, Object> serializationContext = RedisSerializationContext
+				.newSerializationContext().key(jdkSerializer).value(jdkSerializer).hashKey(jdkSerializer)
+				.hashValue(jdkSerializer).build();
+		return new ReactiveRedisTemplate<>(redissonConnectionFactory, serializationContext);
+	}
+
+	@Bean(name = "reactiveStringRedisTemplate")
+	public ReactiveStringRedisTemplate reactiveStringRedisTemplate(RedissonConnectionFactory redissonConnectionFactory) {
+		return new ReactiveStringRedisTemplate(redissonConnectionFactory);
+	}
+
+}

+ 173 - 0
components/component-redis/src/main/java/com/crunii/micro/component/redis/lock/DistributeLock.java

@@ -0,0 +1,173 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
+ */
+package com.crunii.micro.component.redis.lock;
+
+import com.crunii.micro.common.exception.MicroException;
+import com.crunii.micro.common.utils.LogbackUtil;
+import com.crunii.micro.common.utils.RandomUtil;
+import com.crunii.micro.common.utils.Slf4jUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.Redisson;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author 田平 create 2016年4月22日下午2:50:22
+ */
+@Slf4j
+public class DistributeLock {
+	/**
+	 * 毫秒
+	 */
+	private static final int MIN_TIMEOUT = 5000;
+	/**
+	 * 毫秒
+	 */
+	private static final int MAX_TIMEOUT = 30000;
+
+	/**
+	 * 2分钟
+	 */
+	private static final int DEFAULT_EXPIRE_TIME = 2 * 60;
+
+	/**
+	 * 默认为lock
+	 */
+	private String namespace = "lock";
+
+	/**
+	 * 测试使用无意义
+	 */
+	private int testSum = 0;
+
+	private RedissonClient redisson;
+
+	private ThreadLocal<String> threadLocalLockId = new NamedThreadLocal<>("DistributeLock");
+
+	public DistributeLock(RedissonClient redisson) {
+		this.redisson = redisson;
+	}
+
+	public void lock(String id) {
+		lock(id, DEFAULT_EXPIRE_TIME);
+	}
+
+	/**
+	 * @param id
+	 * @param expiredTime 单位秒
+	 */
+	public void lock(String id, int expiredTime) {
+		Assert.hasText(id, "lockId not allowed null!");
+		String lockId = this.namespace + ":" + id;
+		//后面单位是毫秒所以乘1000
+		int leaseTime = expiredTime * 1000;
+		try {
+			String currLockId = threadLocalLockId.get();
+			if (lockId.equals(currLockId)) {
+				throw new MicroException(MicroException.LOCK_FAILURE, lockId, "current thread already locked this lockId!");
+			} else {
+				RLock rlock = redisson.getLock(lockId);
+				//随机等待时间,防止死锁出现
+				long waitTimeout = RandomUtil.getRandom(MIN_TIMEOUT, MAX_TIMEOUT);
+				boolean lockResult = rlock.tryLock(waitTimeout, leaseTime, TimeUnit.MILLISECONDS);
+				if (lockResult) {
+					threadLocalLockId.set(lockId);
+					log.info("lockId={} lock success!", lockId);
+				} else {
+					String errorMsg = Slf4jUtil.formatMsg("lock wait timeout!{} ms", waitTimeout);
+					throw new MicroException(MicroException.LOCK_FAILURE, lockId, errorMsg);
+				}
+			}
+		} catch (Exception e) {
+			throw new MicroException(MicroException.LOCK_FAILURE, e, lockId);
+		}
+	}
+
+	public void unlock() {
+		String currLockId = threadLocalLockId.get();
+		if (StringUtils.hasText(currLockId)) {
+			RLock rlock = redisson.getLock(currLockId);
+			if (rlock != null) {
+				threadLocalLockId.remove();
+				rlock.unlock();
+				log.info("lockId={} unlock success!", currLockId);
+			} else {
+				log.warn("rlock is null maybe expired!");
+			}
+		} else {
+			log.info("lockId={} not holder by current thread so not need unlock!", currLockId);
+		}
+	}
+
+	public RedissonClient getRedisson() {
+		return redisson;
+	}
+
+	public void setRedisson(RedissonClient redisson) {
+		this.redisson = redisson;
+	}
+
+	public String getNamespace() {
+		return namespace;
+	}
+
+	public void setNamespace(String namespace) {
+		this.namespace = namespace;
+	}
+
+	public static void main(String[] args) {
+		//main方法调试设置日志级别
+		LogbackUtil.setLogLevel("info");
+
+		Config config = new Config();
+		config.useSingleServer()
+				.setAddress("redis://81.68.133.219:4000")
+				.setPassword("@Redis,.123");
+		RedissonClient redisson = Redisson.create(config);
+		DistributeLock lock = new DistributeLock(redisson);
+
+		int doSize = 100;
+		ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(doSize));
+		CountDownLatch startSignal = new CountDownLatch(1);
+		CountDownLatch doneSignal = new CountDownLatch(doSize);
+
+		for (int i = 0; i < doSize; i++) {
+			executor.execute(() -> {
+				try {
+					startSignal.await();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+				try {
+					lock.lock("RRR");
+					try {
+						lock.testSum++;
+					} finally {
+						lock.unlock();
+					}
+				} finally {
+					doneSignal.countDown();
+				}
+			});
+		}
+		startSignal.countDown();
+		try {
+			doneSignal.await();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		executor.shutdown();
+		redisson.shutdown();
+		System.out.println(lock.testSum);
+	}
+}

+ 42 - 0
components/component-redis/src/main/java/com/crunii/micro/component/redis/properties/MicroCacheProperties.java

@@ -0,0 +1,42 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.component.redis.properties;
+
+import com.crunii.micro.component.redis.cache.CacheNodeExpire;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 田平 create 2019年7月16日下午6:43:11
+ */
+@Component
+@Setter
+@Getter
+@ToString
+@ConfigurationProperties(prefix = "micro.cache")
+public class MicroCacheProperties {
+
+	/**
+	 * 单独设置某个缓存的过期时间
+	 */
+	private List<CacheNodeExpire> expires = new ArrayList<>();
+
+	/**
+	 * 全局缓存名称
+	 */
+	private List<String> globalCacheNames = new ArrayList<>();
+
+	private String refreshPassword;
+
+	private String globalPrefix;
+
+	private Duration globalTimeToLive;
+}

+ 3 - 0
components/component-redis/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.crunii.micro.component.redis.config.RedisCacheAutoConfiguration,\
+com.crunii.micro.component.redis.config.RedissonReactorAutoConfiguration

+ 50 - 0
components/pom.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.crunii.microservice</groupId>
+		<artifactId>microservice</artifactId>
+		<version>${micro.version}</version>
+	</parent>
+
+	<groupId>com.crunii.microservice</groupId>
+	<artifactId>components</artifactId>
+	<packaging>pom</packaging>
+
+	<modules>
+		<module>component-redis</module>
+	</modules>
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-dependencies</artifactId>
+				<version>${boot.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework.cloud</groupId>
+				<artifactId>spring-cloud-dependencies</artifactId>
+				<version>${cloud.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>com.alibaba.cloud</groupId>
+				<artifactId>spring-cloud-alibaba-dependencies</artifactId>
+				<version>${alibaba.dependencies.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+
+			<!-- components -->
+			<dependency>
+				<groupId>com.crunii.microservice</groupId>
+				<artifactId>component-redis</artifactId>
+				<version>${micro.version}</version>
+			</dependency>
+
+		</dependencies>
+	</dependencyManagement>
+</project>

+ 44 - 0
micro-common-component/micro-common-web/pom.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+    <parent>
+        <groupId>com.crunii.microservice</groupId>
+        <artifactId>micro-common-component</artifactId>
+        <version>${micro.version}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>micro-common-web</artifactId>
+
+    <description>
+        micro-common-web web服务
+    </description>
+
+	<dependencies>
+
+        <!-- SpringBoot Web容器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                    <groupId>org.springframework.boot</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- web 容器使用 undertow 性能更强 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 24 - 0
micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/I18nConfig.java

@@ -0,0 +1,24 @@
+package com.crunii.micro.common.web.config;
+
+import com.crunii.micro.common.web.core.I18nLocaleResolver;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.LocaleResolver;
+
+/**
+ * 国际化配置
+ *
+ * @author lsa
+ */
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureBefore(WebMvcAutoConfiguration.class)
+public class I18nConfig {
+
+    @Bean
+    public LocaleResolver localeResolver() {
+        return new I18nLocaleResolver();
+    }
+
+}

+ 38 - 0
micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/NacosConfig.java

@@ -0,0 +1,38 @@
+package com.crunii.micro.common.web.config;
+
+import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.cloud.nacos.NacosServiceManager;
+import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
+import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
+import com.alibaba.cloud.nacos.discovery.NacosWatch;
+import com.crunii.micro.common.web.nacos.CustomNacosWatch;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
+import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自定义 nacos 监听 解决与 Undertow 整合报错问题
+ *
+ * @author lsa
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnDiscoveryEnabled
+@ConditionalOnBlockingDiscoveryEnabled
+@ConditionalOnNacosDiscoveryEnabled
+@AutoConfigureBefore(NacosDiscoveryClientConfiguration.class)
+@AutoConfigureAfter(NacosDiscoveryAutoConfiguration.class)
+public class NacosConfig {
+
+    @Bean
+    @ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", matchIfMissing = true)
+    public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
+                                 NacosDiscoveryProperties nacosDiscoveryProperties) {
+        return new CustomNacosWatch(nacosServiceManager, nacosDiscoveryProperties);
+    }
+
+}

+ 30 - 0
micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/UndertowConfig.java

@@ -0,0 +1,30 @@
+package com.crunii.micro.common.web.config;
+
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Undertow 自定义配置
+ *
+ * @author lsa
+ */
+@Configuration(proxyBeanMethods = false)
+public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
+
+    /**
+     * 设置 Undertow 的 websocket 缓冲池
+     */
+    @Override
+    public void customize(UndertowServletWebServerFactory factory) {
+        // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配
+        factory.addDeploymentInfoCustomizers(deploymentInfo -> {
+            WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
+            webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
+            deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
+        });
+    }
+
+}

+ 31 - 0
micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/core/I18nLocaleResolver.java

@@ -0,0 +1,31 @@
+package com.crunii.micro.common.web.core;
+
+import org.springframework.web.servlet.LocaleResolver;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Locale;
+
+/**
+ * 获取请求头国际化信息
+ *
+ * @author lsa
+ */
+public class I18nLocaleResolver implements LocaleResolver {
+
+    @Override
+    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
+        String language = httpServletRequest.getHeader("content-language");
+        Locale locale = Locale.getDefault();
+        if (language != null && language.length() > 0) {
+            String[] split = language.split("_");
+            locale = new Locale(split[0], split[1]);
+        }
+        return locale;
+    }
+
+    @Override
+    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
+
+    }
+}

+ 25 - 0
micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/nacos/CustomNacosWatch.java

@@ -0,0 +1,25 @@
+package com.crunii.micro.common.web.nacos;
+
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.cloud.nacos.NacosServiceManager;
+import com.alibaba.cloud.nacos.discovery.NacosWatch;
+
+/**
+ * 自定义 nacos 监听
+ *
+ * @author lsa
+ */
+public class CustomNacosWatch extends NacosWatch {
+
+    public CustomNacosWatch(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties properties) {
+        super(nacosServiceManager, properties);
+    }
+
+    /**
+     * 重写解决与 Undertow 关闭顺序冲突导致报错问题
+     */
+    @Override
+    public int getPhase() {
+        return Integer.MAX_VALUE;
+    }
+}

+ 4 - 0
micro-common-component/micro-common-web/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.crunii.micro.common.web.config.I18nConfig,\
+  com.crunii.micro.common.web.config.UndertowConfig,\
+  com.crunii.micro.common.web.config.NacosConfig

+ 19 - 0
micro-common-component/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+    <parent>
+        <groupId>com.crunii.microservice</groupId>
+        <artifactId>microservice</artifactId>
+        <version>${micro.version}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>micro-common-component</artifactId>
+    <packaging>pom</packaging>
+    <description>micro-common通用模块</description>
+    <modules>
+<!--        <module>micro-common-swagger</module>-->
+        <module>micro-common-web</module>
+<!--        <module>micro-common-excel</module>-->
+    </modules>
+
+</project>

+ 378 - 0
micro-common/pom.xml

@@ -0,0 +1,378 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.crunii.microservice</groupId>
+        <artifactId>microservice</artifactId>
+        <version>${micro.version}</version>
+    </parent>
+    <artifactId>micro-common</artifactId>
+    <name>micro-common</name>
+    <packaging>jar</packaging>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${boot.dependencies.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${cloud.dependencies.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>${alibaba.dependencies.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <dependencies>
+
+        <dependency>
+            <groupId>com.aventrix.jnanoid</groupId>
+            <artifactId>jnanoid</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+        </dependency>
+
+        <!-- dom4j支持xpath依赖包 -->
+        <dependency>
+            <groupId>jaxen</groupId>
+            <artifactId>jaxen</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>org.eclipse.persistence.moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jasypt</groupId>
+            <artifactId>jasypt-spring31</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>guava</artifactId>
+                    <groupId>com.google.guava</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jsr305</artifactId>
+                    <groupId>com.google.code.findbugs</groupId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-spring-context</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- 解决依赖警告 start-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+        <!-- 解决依赖警告 end-->
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>swagger-annotations</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>swagger-models</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>3.0.3</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-loadbalancer</artifactId>
+        </dependency>
+
+        <!--<dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+        </dependency>-->
+
+        <dependency>
+            <groupId>org.apache.groovy</groupId>
+            <artifactId>groovy-jsr223</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.lmax</groupId>
+            <artifactId>disruptor</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.dameng</groupId>
+            <artifactId>DmDialect-for-hibernate5.6</artifactId>
+        </dependency>
+
+        <!-- security -->
+        <dependency>
+            <groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
+            <artifactId>owasp-java-html-sanitizer</artifactId>
+            <version>20180219.1</version>
+        </dependency>
+        <!--end security -->
+
+        <dependency>
+            <groupId>com.github.bingoohuang</groupId>
+            <artifactId>patchca</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.55</version>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-compress</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aspects</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-hibernate5</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-parameter-names</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.xml.fastinfoset</groupId>
+            <artifactId>FastInfoset</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.albfernandez</groupId>
+            <artifactId>juniversalchardet</artifactId>
+            <version>2.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-cache</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-crypto</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-poi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>fr.opensagres.xdocreport</groupId>
+            <artifactId>xdocreport</artifactId>
+            <version>2.0.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-extra</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 54 - 0
micro-common/src/main/java/com/crunii/micro/common/annotations/LimitUploadFileTypeCheck.java

@@ -0,0 +1,54 @@
+package com.crunii.micro.common.annotations;
+
+import com.crunii.micro.common.enums.LimitUploadFileType;
+
+import java.lang.annotation.*;
+
+/**
+ * @description 文件校验
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface LimitUploadFileTypeCheck {
+    /**
+     * 校验不通过提示信息
+     *
+     * @return
+     */
+    String message() default "不支持的文件格式";
+
+    /**
+     * 校验方式
+     */
+    CheckType type() default CheckType.SUFFIX;
+
+    /**
+     * 支持的文件后缀
+     *
+     * @return
+     */
+    String[] supportedSuffixes() default {"ppt", "pptx", "docx", "pdf", "bmp", "jpg", "jpeg", "png", "gif", "zip", "doc", "txt", "xls", "xlsx", "rar", "cad", "dwg", "PPT", "PPTX", "DOCX", "PDF", "BMP", "JPG", "JPEG", "PNG", "GIF", "ZIP", "DOC", "TXT", "XLS", "XLSX", "RAR", "CAD", "DWG"};
+
+    /**
+     * 支持的文件类型
+     *
+     * @return
+     */
+    LimitUploadFileType[] supportedFileTypes() default {};
+
+    enum CheckType {
+        /**
+         * 仅校验后缀
+         */
+        SUFFIX,
+        /**
+         * 校验文件头(魔数)
+         */
+        MAGIC_NUMBER,
+        /**
+         * 同时校验后缀和文件头
+         */
+        SUFFIX_MAGIC_NUMBER
+    }
+}

+ 176 - 0
micro-common/src/main/java/com/crunii/micro/common/captcha/MicroCaptcha.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.captcha;
+
+import com.github.bingoohuang.patchca.background.BackgroundFactory;
+import com.github.bingoohuang.patchca.custom.ConfigurableCaptchaService;
+import com.github.bingoohuang.patchca.filter.ConfigurableFilterFactory;
+import com.github.bingoohuang.patchca.filter.library.AbstractImageOp;
+import com.github.bingoohuang.patchca.filter.library.WobbleImageOp;
+import com.github.bingoohuang.patchca.font.FontFactory;
+import com.github.bingoohuang.patchca.service.Captcha;
+import com.github.bingoohuang.patchca.text.renderer.BestFitTextRenderer;
+import com.github.bingoohuang.patchca.word.RandomWordFactory;
+import com.github.bingoohuang.patchca.word.WordFactory;
+import org.springframework.beans.factory.InitializingBean;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author 田平 create 2018年10月30日下午2:44:28
+ */
+public class MicroCaptcha extends ConfigurableCaptchaService implements InitializingBean {
+
+	/**
+	 * 取消0 o i 1
+	 */
+	private String words = "abcdefghkmnpqstwxyzABCDEFGHKMNPQSTWXYZ23456789";
+	private int wordLength = 4;
+
+	/**
+	 * @return the wordLength
+	 */
+	public int getWordLength() {
+		return wordLength;
+	}
+
+	/**
+	 * @param wordLength the wordLength to set
+	 */
+	public void setWordLength(int wordLength) {
+		this.wordLength = wordLength;
+	}
+
+	/**
+	 * @return the words
+	 */
+	public String getWords() {
+		return words;
+	}
+
+	/**
+	 * @param words the words to set
+	 */
+	public void setWords(String words) {
+		this.words = words;
+	}
+
+	public MicroCaptcha() {
+
+	}
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		RandomWordFactory wordFactory = new RandomWordFactory();
+		wordFactory.setCharacters(words);
+		wordFactory.setMaxLength(wordLength);
+		wordFactory.setMinLength(wordLength);
+		setWordFactory(wordFactory);
+		// 兼容linux的字体
+		FontFactory fontFactory = new FontFactory() {
+			@Override
+			public Font getFont(int arg0) {
+				return new Font("STIX", Font.PLAIN, 36);
+			}
+
+			@Override
+			public void setWord(String word) {
+			}
+
+			@Override
+			public void setWordFactory(WordFactory wordFactory) {
+			}
+		};
+		setFontFactory(fontFactory);
+
+		CustomBackgroundFactory backgroundFactory = new CustomBackgroundFactory();
+		setBackgroundFactory(backgroundFactory);
+
+		// 图片滤镜设置
+		ConfigurableFilterFactory filterFactory = new ConfigurableFilterFactory();
+		List<BufferedImageOp> filters = new ArrayList<BufferedImageOp>();
+		WobbleImageOp wobbleImageOp = new WobbleImageOp();
+		wobbleImageOp.setEdgeMode(AbstractImageOp.EDGE_MIRROR);
+		wobbleImageOp.setxAmplitude(2.0);
+		wobbleImageOp.setyAmplitude(1.0);
+		filters.add(wobbleImageOp);
+		filterFactory.setFilters(filters);
+		setFilterFactory(filterFactory);
+
+		// 文字渲染器设置
+		BestFitTextRenderer textRenderer = new BestFitTextRenderer();
+		textRenderer.setBottomMargin(3);
+		textRenderer.setTopMargin(3);
+		setTextRenderer(textRenderer);
+
+	}
+
+	private class CustomBackgroundFactory implements BackgroundFactory {
+		private Random random = new Random();
+
+		public void fillBackground(BufferedImage image) {
+			Graphics graphics = image.getGraphics();
+
+			// 验证码图片的宽高
+			int imgWidth = image.getWidth();
+			int imgHeight = image.getHeight();
+
+			// 填充为白色背景
+			graphics.setColor(Color.WHITE);
+			graphics.fillRect(0, 0, imgWidth, imgHeight);
+
+			// 画100个噪点(颜色及位置随机)
+			for (int i = 0; i < 100; i++) {
+				// 随机颜色
+				int rInt = random.nextInt(255);
+				int gInt = random.nextInt(255);
+				int bInt = random.nextInt(255);
+
+				graphics.setColor(new Color(rInt, gInt, bInt));
+
+				// 随机位置
+				int xInt = random.nextInt(imgWidth - 3);
+				int yInt = random.nextInt(imgHeight - 2);
+
+				// 随机旋转角度
+				int sAngleInt = random.nextInt(360);
+				int eAngleInt = random.nextInt(360);
+
+				// 随机大小
+				int wInt = random.nextInt(6);
+				int hInt = random.nextInt(6);
+
+				graphics.fillArc(xInt, yInt, wInt, hInt, sAngleInt, eAngleInt);
+
+				// 画5条干扰线
+				if (i % 20 == 0) {
+					int xInt2 = random.nextInt(imgWidth);
+					int yInt2 = random.nextInt(imgHeight);
+					graphics.drawLine(xInt, yInt, xInt2, yInt2);
+				}
+			}
+		}
+	}
+
+	public static void main(String[] args) throws Exception {
+		MicroCaptcha microCaptcha = new MicroCaptcha();
+		microCaptcha.afterPropertiesSet();
+		microCaptcha.setWidth(250);
+		microCaptcha.setHeight(60);
+		Captcha captcha = microCaptcha.getCaptcha();
+		System.out.println(captcha.getChallenge());
+		File file = new File("D:\\captcha.png");
+		try (FileOutputStream out = new FileOutputStream(file);) {
+			ImageIO.write(captcha.getImage(), "png", out);
+		}
+	}
+}

+ 28 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/BlockingRejectedExecutionHandler.java

@@ -0,0 +1,28 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author 田平 create 2021/9/28 16:37
+ */
+@Slf4j
+public class BlockingRejectedExecutionHandler implements RejectedExecutionHandler {
+
+	@Override
+	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
+		if (!executor.isShutdown()) {
+			try {
+				executor.getQueue().put(r);
+			} catch (InterruptedException e) {
+				throw new RuntimeException("Executor Interrupted", e);
+			}
+		}
+	}
+}

+ 46 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityCallable.java

@@ -0,0 +1,46 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+/**
+ * @author 田平 create 2021/12/21 14:11
+ */
+public class PriorityCallable<V> implements Callable<V>, Comparable<PriorityCallable> {
+
+	/**
+	 * 越小优先级越高
+	 */
+	private int priority;
+
+	private Callable<V> call;
+
+	public PriorityCallable(Callable<V> call, int priority) {
+		this.call = call;
+		this.priority = priority;
+	}
+
+	public PriorityCallable(Runnable run, V result, int priority) {
+		this.call = Executors.callable(run, result);
+		this.priority = priority;
+	}
+
+	public int getPriority() {
+		return priority;
+	}
+
+	@Override
+	public int compareTo(PriorityCallable o) {
+		return Integer.compare(priority, o.getPriority());
+	}
+
+	@Override
+	public V call() throws Exception {
+		return call.call();
+	}
+
+}

+ 123 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityExecutor.java

@@ -0,0 +1,123 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent;
+
+import com.crunii.micro.common.utils.StopUtil;
+
+import java.util.concurrent.*;
+
+/**
+ * @author 田平 create 2021/12/21 14:06
+ */
+public class PriorityExecutor extends ThreadPoolExecutor {
+
+	private static final int INIT_SIZE = 128;
+
+	public PriorityExecutor(int corePoolSize, int maximumPoolSize) {
+		super(corePoolSize, maximumPoolSize, 0, TimeUnit.MINUTES,
+				getPriorityQueue(INIT_SIZE));
+	}
+
+	public PriorityExecutor(int corePoolSize, int maximumPoolSize,
+							long keepAliveTime, TimeUnit unit) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+				getPriorityQueue(INIT_SIZE));
+	}
+
+	public PriorityExecutor(int queueSize, int corePoolSize, int maximumPoolSize,
+							long keepAliveTime, TimeUnit unit) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+				getPriorityQueue(queueSize));
+	}
+
+	public PriorityExecutor(int queueSize, int corePoolSize, int maximumPoolSize,
+							long keepAliveTime, TimeUnit unit,
+							ThreadFactory threadFactory) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+				getPriorityQueue(queueSize), threadFactory);
+	}
+
+	public PriorityExecutor(int queueSize, int corePoolSize, int maximumPoolSize,
+							long keepAliveTime, TimeUnit unit,
+							RejectedExecutionHandler handler) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+				getPriorityQueue(queueSize), handler);
+	}
+
+	public PriorityExecutor(int queueSize, int corePoolSize, int maximumPoolSize,
+							long keepAliveTime, TimeUnit unit,
+							ThreadFactory threadFactory,
+							RejectedExecutionHandler handler) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+				getPriorityQueue(queueSize), threadFactory, handler);
+	}
+
+	private static PriorityBlockingQueue getPriorityQueue(int size) {
+		return new PriorityBlockingQueue(size);
+	}
+
+	public void execute(PriorityRunnable run) {
+		super.execute(run);
+	}
+
+	public <T> Future<T> submit(PriorityRunnable run) {
+		PriorityTask task = new PriorityTask(run, null);
+		super.execute(task);
+		return task;
+	}
+
+	public <T> Future<T> submit(PriorityRunnable run, T result) {
+		PriorityTask task = new PriorityTask(run, result);
+		super.execute(task);
+		return task;
+	}
+
+	public <T> Future<T> submit(PriorityCallable<T> call) {
+		PriorityTask task = new PriorityTask(call);
+		super.execute(task);
+		return task;
+	}
+
+	public <T> Future<T> submit(PriorityTask<T> task) {
+		super.execute(task);
+		return task;
+	}
+
+	public static void main(String[] args) throws Exception {
+		//设置一个线程的邮箱线程池,方便查看效果
+		PriorityExecutor executor = new PriorityExecutor(1, 1);
+		CountDownLatch cd = new CountDownLatch(3);
+		executor.execute(new PriorityRunnable(() -> {
+			try {
+				TimeUnit.MILLISECONDS.sleep(10);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			System.out.println(1);
+			cd.countDown();
+		}, 1));
+		executor.execute(new PriorityRunnable(() -> {
+			try {
+				TimeUnit.MILLISECONDS.sleep(2);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			System.out.println(3);
+			cd.countDown();
+		}, 3));
+		executor.execute(new PriorityRunnable(() -> {
+			try {
+				TimeUnit.MILLISECONDS.sleep(15);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			System.out.println(2);
+			cd.countDown();
+		}, 2));
+		cd.await();
+		StopUtil.stopES(executor);
+	}
+
+}

+ 37 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityRunnable.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent;
+
+/**
+ * @author 田平 create 2021/12/21 14:11
+ */
+public class PriorityRunnable implements Runnable, Comparable<PriorityRunnable> {
+
+	/**
+	 * 越小优先级越高
+	 */
+	private int priority;
+
+	private Runnable run;
+
+	public PriorityRunnable(Runnable run, int priority) {
+		this.run = run;
+		this.priority = priority;
+	}
+
+	public int getPriority() {
+		return priority;
+	}
+
+	@Override
+	public int compareTo(PriorityRunnable o) {
+		return Integer.compare(priority, o.getPriority());
+	}
+
+	@Override
+	public void run() {
+		run.run();
+	}
+}

+ 34 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityTask.java

@@ -0,0 +1,34 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent;
+
+import java.util.concurrent.FutureTask;
+
+/**
+ * @author 田平 create 2021/12/21 14:17
+ */
+public class PriorityTask<V> extends FutureTask<V> implements Comparable<PriorityTask> {
+
+	private PriorityCallable call;
+
+	public PriorityCallable getPriorityCallable(){
+		return call;
+	}
+
+	public PriorityTask(PriorityCallable<V> call) {
+		super(call);
+		this.call = call;
+	}
+
+	public PriorityTask(PriorityRunnable run, V v) {
+		super(run, v);
+		this.call = new PriorityCallable(run, v, run.getPriority());
+	}
+
+	@Override
+	public int compareTo(PriorityTask o) {
+		return this.call.compareTo(o.getPriorityCallable());
+	}
+}

+ 33 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeList.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * <pre>
+ *     固定长度的list,
+ *     达到长度后,前面的数据会删除
+ *     使用于保存最近的固定条数的记录
+ *     长度不宜过大,尽量不超过1千
+ * </pre>
+ *
+ * @author 田平 create 2022-07-26 15:30:28
+ */
+@ThreadSafe
+public interface FixSizeList<T extends Serializable & RemoveId> {
+
+	public List<T> getAll();
+
+	public boolean removeById(String id);
+
+	public void add(T element);
+
+	public void addAll(List<T> elements);
+
+	public void clear();
+}

+ 34 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeSortedSet.java

@@ -0,0 +1,34 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <pre>
+ *     固定长度的sortedSet,
+ *     达到长度后,前面的数据会删除
+ *     使用于保存最近的固定条数的记录,可能会插入重复数据
+ *     长度不宜过大,尽量不超过1千
+ * </pre>
+ *
+ * @author 田平 create 2022-07-26 15:30:28
+ */
+@ThreadSafe
+public interface FixSizeSortedSet<T extends Serializable & RemoveId & Comparable> {
+
+	public boolean removeById(String id);
+
+	public List<T> getAll();
+
+	public void add(T element, double score);
+
+	public void addAll(Map<T, Double> elements);
+
+	public void clear();
+}

+ 17 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/RemoveId.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection;
+
+/**
+ * @author 田平 create 2022-08-03 14:43:56
+ */
+public interface RemoveId {
+	/**
+	 * 通过某个id删除对象
+	 *
+	 * @return
+	 */
+	public String getRemoveId();
+}

+ 77 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeLinkedList.java

@@ -0,0 +1,77 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection.impl;
+
+import com.crunii.micro.common.concurrent.collection.FixSizeList;
+import com.crunii.micro.common.concurrent.collection.RemoveId;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <pre>
+ *     写多读少直接全部同步方法
+ * </pre>
+ *
+ * @author 田平 create 2022-07-27 14:06:56
+ */
+public class FixSizeLinkedList<T extends Serializable & RemoveId> implements FixSizeList<T> {
+
+	private LinkedList<T> list;
+	private int size;
+
+	public FixSizeLinkedList(int size) {
+		this.size = size;
+		list = new LinkedList<>();
+		Assert.isTrue(size > 0, "size must be more than zero!");
+	}
+
+	@Override
+	public synchronized List<T> getAll() {
+		return new ArrayList<>(list);
+	}
+
+	@Override
+	public synchronized void add(T element) {
+		list.add(element);
+		if (list.size() > size) {
+			list.removeFirst();
+		}
+	}
+
+	@Override
+	public synchronized void addAll(List<T> elements) {
+		list.addAll(elements);
+		int remove = list.size() - size;
+		if (remove > 0) {
+			for (int i = 0; i < remove; i++) {
+				list.removeFirst();
+			}
+		}
+	}
+
+	@Override
+	public synchronized void clear() {
+		list.clear();
+	}
+
+	@Override
+	public synchronized boolean removeById(String removeId) {
+		return list.removeIf(t -> t.getRemoveId().equals(removeId));
+	}
+
+	public static void main(String[] args) {
+		FixSizeLinkedList<SimpleRemoveId> fix = new FixSizeLinkedList(5);
+		for (int i = 0; i < 10; i++) {
+			fix.add(new SimpleRemoveId(String.valueOf(i)));
+		}
+		System.out.println(fix.removeById("5100"));
+		System.out.println(fix.removeById("7100"));
+		fix.getAll().forEach(System.out::println);
+	}
+}

+ 153 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeTreeSet.java

@@ -0,0 +1,153 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection.impl;
+
+import com.crunii.micro.common.concurrent.collection.FixSizeSortedSet;
+import com.crunii.micro.common.concurrent.collection.RemoveId;
+import com.google.common.base.Objects;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+/**
+ * @author 田平 create 2022-07-27 14:31:17
+ */
+public class FixSizeTreeSet<T extends Serializable & RemoveId & Comparable> implements FixSizeSortedSet<T> {
+
+	private TreeSet<InnerElement> treeSet;
+
+	private int size;
+
+	static class InnerElement<T extends Serializable & RemoveId & Comparable> implements Comparable<InnerElement> {
+		private T element;
+		private double score;
+
+		public InnerElement(T element, double score) {
+			this.element = element;
+			this.score = score;
+		}
+
+		public T getElement() {
+			return element;
+		}
+
+		public void setElement(T element) {
+			this.element = element;
+		}
+
+		public double getScore() {
+			return score;
+		}
+
+		public void setScore(double score) {
+			this.score = score;
+		}
+
+		@Override
+		public int compareTo(InnerElement o) {
+			if (this.getScore() == o.getScore()) {
+				return this.element.compareTo(o.getElement());
+			} else {
+				return Double.compare(this.getScore(), o.getScore());
+			}
+		}
+
+		@Override
+		public String toString() {
+			return "InnerElement{" +
+					"element=" + element +
+					", score=" + score +
+					'}';
+		}
+
+		@Override
+		public boolean equals(Object o) {
+			if (this == o) {
+				return true;
+			}
+			if (o == null || getClass() != o.getClass()) {
+				return false;
+			}
+			InnerElement<?> innerElement1 = (InnerElement<?>) o;
+			return Double.compare(innerElement1.score, score) == 0 &&
+					Objects.equal(element, innerElement1.element);
+		}
+
+		@Override
+		public int hashCode() {
+			return Objects.hashCode(element, score);
+		}
+	}
+
+	public FixSizeTreeSet(int size) {
+		this.size = size;
+		this.treeSet = new TreeSet<>();
+		Assert.isTrue(size > 0, "size must be more than zero!");
+	}
+
+	@Override
+	public synchronized List<T> getAll() {
+		return treeSet.stream().map(e -> (T) (e.getElement())).collect(Collectors.toList());
+	}
+
+	@Override
+	public synchronized void add(T element, double score) {
+		treeSet.add(new InnerElement(element, score));
+		if (treeSet.size() > size) {
+			treeSet.pollFirst();
+		}
+	}
+
+	public synchronized boolean removeById(String removeId) {
+		return treeSet.removeIf(e -> {
+			RemoveId eRemoveId = (RemoveId) e.getElement();
+			return eRemoveId.getRemoveId().equals(removeId);
+		});
+	}
+
+	@Override
+	public synchronized void addAll(Map<T, Double> elements) {
+		if (!elements.isEmpty()) {
+			List<InnerElement> es = elements.entrySet()
+					.stream()
+					.map(e -> new InnerElement(e.getKey(), e.getValue()))
+					.collect(Collectors.toList());
+			treeSet.addAll(es);
+			int removeSize = treeSet.size() - size;
+			if (removeSize > 0) {
+				for (int i = 0; i < removeSize; i++) {
+					treeSet.pollFirst();
+				}
+			}
+		}
+	}
+
+	@Override
+	public synchronized void clear() {
+		treeSet.clear();
+	}
+
+	public static void main(String[] args) {
+		FixSizeTreeSet<SimpleRemoveId> set = new FixSizeTreeSet(10);
+		Map<SimpleRemoveId, Double> map = new HashMap<>();
+		map.put(new SimpleRemoveId("4"), 4D);
+		map.put(new SimpleRemoveId("3"), 3D);
+		map.put(new SimpleRemoveId("2"), 2D);
+		map.put(new SimpleRemoveId("1"), 1D);
+		map.put(new SimpleRemoveId("1"), 1D);
+		map.put(new SimpleRemoveId("5"), 5D);
+		map.put(new SimpleRemoveId("9"), 5D);
+		map.put(new SimpleRemoveId("10"), 6D);
+		set.addAll(map);
+		System.out.println(set.removeById("4100"));
+		System.out.println(set.removeById("5100"));
+		set.getAll().forEach(System.out::println);
+	}
+}

+ 48 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/SimpleRemoveId.java

@@ -0,0 +1,48 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.collection.impl;
+
+import com.crunii.micro.common.concurrent.collection.RemoveId;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author 田平 create 2022-08-03 14:53:16
+ */
+@Data
+public class SimpleRemoveId implements RemoveId, Serializable, Comparable<SimpleRemoveId> {
+	private static final long serialVersionUID = 1L;
+
+	private String id;
+
+	private String data;
+
+	public SimpleRemoveId(String id) {
+		this.id = id;
+		this.data = id + "100";
+	}
+
+	@Override
+	public String getRemoveId() {
+		return data;
+	}
+
+	@Override
+	public int compareTo(SimpleRemoveId that) {
+		if (this.id.compareTo(that.id) < 0) {
+			return -1;
+		} else if (this.id.compareTo(that.id) > 0) {
+			return 1;
+		}
+
+		if (this.data.compareTo(that.data) < 0) {
+			return -1;
+		} else if (this.data.compareTo(that.data) > 0) {
+			return 1;
+		}
+		return 0;
+	}
+}

+ 81 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMap.java

@@ -0,0 +1,81 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.map;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * <pre>多值map</pre>
+ *
+ * @author 田平 create 2022/1/28 14:06
+ */
+public interface MultiValueMap<K, V> {
+
+	/**
+	 * 获取实现的ConcurrentMap
+	 *
+	 * @return
+	 */
+	public ConcurrentMap<K, List<V>> getCurrMap();
+
+	/**
+	 * Stores a key-value pair in the multimap.
+	 *
+	 * @param key   the key to be stored
+	 * @param value the value to be stored
+	 * @return true if the size of the multimap is increased, false if the multimap
+	 * already contains the key-value pair.
+	 */
+	public boolean put(K key, V value);
+
+	/**
+	 * @param key
+	 * @param value
+	 * @return
+	 */
+	public boolean containsKey(K key, V value);
+
+	/**
+	 * Returns the fix of values associated with the key.
+	 *
+	 * @param key the key whose associated values are returned
+	 * @return the fix of the values associated with the key
+	 */
+	public List<V> get(K key);
+
+	/**
+	 * Removes the given key value pair from the multimap.
+	 *
+	 * @param key   the key of the entry to remove
+	 * @param value the value of the entry to remove
+	 * @return true if the size of the multimap changed after the remove operation, false otherwise.
+	 */
+	public boolean remove(K key, V value);
+
+	/**
+	 * Removes all the entries associated with the given key.
+	 *
+	 * @param key the key of the entries to remove
+	 * @return the fix of removed values associated with the given key. The returned fix
+	 * might be modifiable but it has no effect on the multimap.
+	 */
+	public List<V> remove(K key);
+
+	/**
+	 * Returns the number of values matching the given key in the multimap.
+	 *
+	 * @param key the key whose number of values is to be returned
+	 * @return the number of values matching the given key in the multimap
+	 */
+	public int valueCount(K key);
+
+	/**
+	 * Returns the number of key-value pairs in the multimap.
+	 *
+	 * @return the number of key-value pairs in the multimap
+	 */
+	public int size();
+}

+ 176 - 0
micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMapImpl.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.concurrent.map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * </pre>适合读多写少的一些场景</pre>
+ *
+ * @author 田平 create 2022/1/28 14:08
+ */
+@ThreadSafe
+@Slf4j
+public class MultiValueMapImpl<K, V> implements MultiValueMap<K, V> {
+	private ConcurrentHashMap<K, List<V>> currMap;
+
+	public MultiValueMapImpl() {
+		currMap = new ConcurrentHashMap<>();
+	}
+
+	public MultiValueMapImpl(int initialCapacity) {
+		currMap = new ConcurrentHashMap<>(initialCapacity);
+	}
+
+	public MultiValueMapImpl(int initialCapacity, int factor) {
+		currMap = new ConcurrentHashMap<>(initialCapacity, factor);
+	}
+
+	public ConcurrentMap<K, List<V>> getCurrMap() {
+		return currMap;
+	}
+
+	public boolean containsKey(K key, V value) {
+		if (currMap.get(key) == null) {
+			return false;
+		} else {
+			List<V> values = currMap.getOrDefault(key, new CopyOnWriteArrayList<V>());
+			return values.contains(value);
+		}
+	}
+
+	@Override
+	public boolean put(K key, V value) {
+		currMap.compute(key, (k, v) -> {
+			if (v == null) {
+				v = new CopyOnWriteArrayList<V>();
+			}
+			v.add(value);
+			return v;
+		});
+		return true;
+	}
+
+	@Override
+	public List<V> get(K key) {
+		return currMap.get(key);
+	}
+
+	@Override
+	public boolean remove(K key, V value) {
+		currMap.computeIfPresent(key, (k, v) -> {
+			v.remove(value);
+			if (v.isEmpty()) {
+				return null;
+			} else {
+				return v;
+			}
+		});
+		return true;
+	}
+
+	@Override
+	public List<V> remove(K key) {
+		return currMap.remove(key);
+	}
+
+	@Override
+	public int valueCount(K key) {
+		List<V> values = currMap.get(key);
+		if (values == null) {
+			return 0;
+		} else {
+			return values.size();
+		}
+	}
+
+	@Override
+	public int size() {
+		return (int) currMap.values().stream().flatMap(l -> l.stream()).count();
+	}
+
+	public static void main(String[] args) {
+		MultiValueMap<Integer, String> map = new MultiValueMapImpl<>();
+		map.put(1, "a");
+		map.put(1, "b");
+		System.out.println(map.valueCount(1));
+		System.out.println(map.remove(1, "a"));
+		System.out.println(map.size());
+		System.out.println(map.valueCount(1));
+		System.out.println(map.remove(1, "b"));
+		System.out.println(map.get(1));
+		System.out.println(map.valueCount(1));
+		System.out.println(map.size());
+		System.out.println(map.remove(1));
+		System.out.println(map.size());
+
+		//Multimap<Integer, String> map = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
+//		int doSize = 100000;
+//		ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0,
+//				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(doSize));
+//		CountDownLatch startSignal = new CountDownLatch(1);
+//		CountDownLatch doneSignal = new CountDownLatch(doSize);
+//		AtomicInteger atomic = new AtomicInteger();
+//		Date date = new Date();
+//		for (int i = 0; i < doSize; i++) {
+//			executor.execute(() -> {
+//				try {
+//					startSignal.await();
+//				} catch (InterruptedException e) {
+//					log.error("startSignal.await error!", e);
+//				}
+//				try {
+//					int k = atomic.getAndIncrement() % 2000;
+//					map.put(k, NanoIdUtil.randomLowerNanoId());
+//				} finally {
+//					doneSignal.countDown();
+//				}
+//			});
+//		}
+//		startSignal.countDown();
+//		try {
+//			doneSignal.await();
+//		} catch (InterruptedException e) {
+//			e.printStackTrace();
+//		}
+//		System.out.println(map.size());
+//		System.out.println(DateUtil.getUseTime(date));
+//		//读写对比
+//		Date second = new Date();
+//		CountDownLatch startSignal1 = new CountDownLatch(1);
+//		CountDownLatch doneSignal1 = new CountDownLatch(doSize);
+//		atomic.set(0);
+//		for (int i = 0; i < doSize; i++) {
+//			executor.execute(() -> {
+//				try {
+//					startSignal1.await();
+//				} catch (InterruptedException e) {
+//					log.error("startSignal.await error!", e);
+//				}
+//				try {
+//					int k = atomic.getAndIncrement() % 2000;
+//					map.get(k);
+//				} finally {
+//					doneSignal1.countDown();
+//				}
+//			});
+//		}
+//		startSignal1.countDown();
+//		try {
+//			doneSignal1.await();
+//		} catch (InterruptedException e) {
+//			e.printStackTrace();
+//		}
+//		System.out.println(DateUtil.getUseTime(second));
+//		//map.getCurrMap().keySet().forEach(System.out::println);
+//		executor.shutdown();
+	}
+}

+ 42 - 0
micro-common/src/main/java/com/crunii/micro/common/config/CommConfiguration.java

@@ -0,0 +1,42 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.config;
+
+import com.crunii.micro.common.jackson.ObjectMapperFactoryBean;
+import com.crunii.micro.common.spring.SpringUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author 田平 create 2019年12月23日下午3:48:10
+ */
+@Configuration
+public class CommConfiguration {
+
+	@Value("${jackson.xss.serializer:false}")
+	private boolean enableXssSerializer;
+
+	@Value("${jackson.xss.deserializer:true}")
+	private boolean enableXssDeserializer;
+
+	@Bean(name = "objectMapper")
+	public ObjectMapper regObjectMapper() throws Exception {
+		ObjectMapperFactoryBean bean = new ObjectMapperFactoryBean(enableXssSerializer, enableXssDeserializer);
+		SimpleModule simpleModule = new SimpleModule();
+		simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+		simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
+		ObjectMapper objectMapper = bean.getObject().registerModule(simpleModule);
+		return objectMapper;
+	}
+
+	@Bean(name = "springUtil")
+	public SpringUtil regSpringUtil() {
+		return new SpringUtil();
+	}
+
+}

+ 41 - 0
micro-common/src/main/java/com/crunii/micro/common/config/LocalDateTimeConverter.java

@@ -0,0 +1,41 @@
+package com.crunii.micro.common.config;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author lmlksy
+ * @date 2023/11/28 14:51
+ */
+public class LocalDateTimeConverter implements Converter<LocalDateTime> {
+
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @Override
+    public Class supportJavaTypeKey() {
+        return LocalDateTime.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        String stringValue = cellData.getStringValue();
+        return LocalDateTime.parse(stringValue, formatter);
+    }
+
+    @Override
+    public CellData convertToExcelData(LocalDateTime localDateTime, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        String stringValue = formatter.format(localDateTime);
+        return new CellData(stringValue);
+    }
+}

+ 24 - 0
micro-common/src/main/java/com/crunii/micro/common/config/NacosConfiguration.java

@@ -0,0 +1,24 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.config;
+
+import com.alibaba.nacos.api.annotation.NacosProperties;
+import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author 田平 create 2019年6月26日下午3:40:55
+ */
+@Configuration
+@EnableNacosConfig(
+		globalProperties = @NacosProperties(
+				serverAddr = "${spring.cloud.nacos.config.server-addr}",
+				username = "${spring.cloud.nacos.config.username}",
+				password = "${spring.cloud.nacos.config.password}",
+				namespace = "${spring.cloud.nacos.config.namespace}"
+		)
+)
+public class NacosConfiguration {
+
+}

+ 176 - 0
micro-common/src/main/java/com/crunii/micro/common/config/RestTemplateConfiguration.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.config;
+
+import com.crunii.micro.common.constants.CommonConstants;
+import com.crunii.micro.common.spring.rest.RestBuilder;
+import com.crunii.micro.common.spring.rest.RestConfig;
+import com.crunii.micro.common.spring.rest.TimeoutRestTemplate;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.Header;
+import org.apache.http.message.BasicHeader;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.env.Environment;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ *     所有依赖注入采用名字进行@Resource(name = "xxxName")
+ *     所有的http调用内部和外部的都采用这个
+ *     一共10个RestTemplate 分为
+ *     1 内部调用不记录日志:noLogApiRestTemplate 或者 noLogApiTimeoutRestTemplate
+ *     2 内部调用记录日志:logApiRestTemplate 或者 logApiTimeoutRestTemplate
+ *     3 外部调用记录日志:logRestTemplate 或者 logTimeoutRestTemplate
+ *     4 外部调用不记录日志:noLogRestTemplate 或者 noLogTimeoutRestTemplate
+ *     5 外部公网调用记录日志:logRestTemplate 或者 logTimeoutRestTemplate
+ *
+ *     http代理访问公网
+ *     1.引入http-proxy.yml配置文件
+ *     2.通过@Resource引用:noLogInternetRestTemplate 或者 logInternetRestTemplate
+ * </pre>
+ *
+ * @author 田平 create 2018年5月29日下午3:04:37
+ */
+@Configuration
+public class RestTemplateConfiguration {
+
+	private static final String INTERNET_PROXY_KEY_NAME = "http-proxy.internet";
+
+	@Autowired
+	private Environment env;
+
+	@Resource(name = "objectMapper")
+	private ObjectMapper objectMapper;
+
+	@Bean(name = "noLogApiRestTemplate")
+	@Primary
+	@LoadBalanced
+	@DependsOn("springUtil")
+	public RestTemplate regNoLogApiRestTemplate() {
+		List<Header> headers = new ArrayList<>();
+		headers.add(new BasicHeader(CommonConstants.HEADER_IS_NEED_AUTH_URL, "false"));
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.defaultHeaders(headers)
+				.build();
+
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@Bean(name = "logApiRestTemplate")
+	@LoadBalanced
+	@DependsOn("springUtil")
+	public RestTemplate regLogApiRestTemplate() {
+		List<Header> headers = new ArrayList<>();
+		headers.add(new BasicHeader(CommonConstants.HEADER_IS_NEED_AUTH_URL, "false"));
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.defaultHeaders(headers)
+				.enabledLog(true)
+				.build();
+
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@Bean(name = "noLogApiTimeoutRestTemplate")
+	@LoadBalanced
+	@DependsOn("springUtil")
+	public TimeoutRestTemplate regNoLogApiTimeoutRestTemplate() {
+		List<Header> headers = new ArrayList<>();
+		headers.add(new BasicHeader(CommonConstants.HEADER_IS_NEED_AUTH_URL, "false"));
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.defaultHeaders(headers)
+				.build();
+
+		return RestBuilder.buildTimeoutRestTemplate(restConfig);
+	}
+
+	@Bean(name = "logApiTimeoutRestTemplate")
+	@LoadBalanced
+	@DependsOn("springUtil")
+	public TimeoutRestTemplate regLogApiTimeoutRestTemplate() {
+		List<Header> headers = new ArrayList<>();
+		headers.add(new BasicHeader(CommonConstants.HEADER_IS_NEED_AUTH_URL, "false"));
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.enabledLog(true)
+				.defaultHeaders(headers)
+				.build();
+
+		return RestBuilder.buildTimeoutRestTemplate(restConfig);
+	}
+
+	@Bean(name = "noLogRestTemplate")
+	@DependsOn("springUtil")
+	public RestTemplate regNoLogRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.build();
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@Bean(name = "logRestTemplate")
+	@DependsOn("springUtil")
+	public RestTemplate regLogRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.enabledLog(true)
+				.build();
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@ConditionalOnProperty(name = INTERNET_PROXY_KEY_NAME)
+	@Bean(name = "noLogInternetRestTemplate")
+	@DependsOn("springUtil")
+	public RestTemplate regNoLogInternetRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.proxyServerAddress(env.getProperty(INTERNET_PROXY_KEY_NAME))
+				.build();
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@ConditionalOnProperty(name = INTERNET_PROXY_KEY_NAME)
+	@Bean(name = "logInternetRestTemplate")
+	@DependsOn("springUtil")
+	public RestTemplate regLogInternetRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.enabledLog(true)
+				.proxyServerAddress(env.getProperty(INTERNET_PROXY_KEY_NAME))
+				.build();
+		return RestBuilder.buildRestTemplate(restConfig);
+	}
+
+	@Bean(name = "noLogTimeoutRestTemplate")
+	@DependsOn("springUtil")
+	public TimeoutRestTemplate regNoLogTimeoutRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.build();
+		return RestBuilder.buildTimeoutRestTemplate(restConfig);
+	}
+
+	@Bean(name = "logTimeoutRestTemplate")
+	@DependsOn("springUtil")
+	public TimeoutRestTemplate regLogTimeoutRestTemplate() {
+		RestConfig restConfig = RestConfig.builder()
+				.mapper(objectMapper)
+				.enabledLog(true)
+				.build();
+		return RestBuilder.buildTimeoutRestTemplate(restConfig);
+	}
+
+}

+ 42 - 0
micro-common/src/main/java/com/crunii/micro/common/config/StartTimeConfiguration.java

@@ -0,0 +1,42 @@
+/**
+ * Copyright 2008-2021. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+
+package com.crunii.micro.common.config;
+
+import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.cloud.nacos.NacosServiceManager;
+import com.alibaba.cloud.nacos.discovery.NacosWatch;
+import com.crunii.micro.common.utils.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.CommonsClientAutoConfiguration;
+import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Date;
+
+/**
+ * @author 田平 create 2023-02-10 03:54:47
+ */
+@Slf4j
+@Configuration
+@ConditionalOnNacosDiscoveryEnabled
+@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
+public class StartTimeConfiguration {
+
+	public static final String START_TIME = "startTime";
+
+	@Bean
+	@ConditionalOnProperty(value = { "spring.cloud.nacos.discovery.watch.enabled" }, matchIfMissing = true)
+	public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties nacosDiscoveryProperties) {
+		//添加服务注册时间
+		nacosDiscoveryProperties.getMetadata().put(START_TIME, DateUtil.format(new Date()));
+		return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties);
+	}
+
+}

+ 182 - 0
micro-common/src/main/java/com/crunii/micro/common/config/SwaggerConfiguration.java

@@ -0,0 +1,182 @@
+
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.config;
+
+import com.crunii.micro.common.constants.CommonConstants;
+import com.crunii.micro.common.exception.BusinessException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.Assert;
+import springfox.documentation.RequestHandler;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.oas.annotations.EnableOpenApi;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * <pre>
+ * 	 本地和测试环境需要api 生产环境安全原因不加载
+ * </pre>
+ *
+ * @author 田平 create 2018年10月15日下午2:39:36
+ */
+@Configuration
+@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true")
+@EnableOpenApi
+public class SwaggerConfiguration {
+
+	@Value("${spring.application.name}")
+	private String appName;
+
+	/**
+	 * <pre>
+	 *     可以单独指定一个特别的扫描路径,
+	 *     除非特殊情况,一般不建议使用
+	 * </pre>
+	 */
+	@Value("${swagger.special.controller.package:NONE}")
+	private String specialControllerPackage;
+
+	private AntPathMatcher matcher = new AntPathMatcher();
+
+	private static final String BASE_PKG_PREFIX = "com.crunii.micro.";
+	private static final String BASE_PKG_SUFFIX = ".controller";
+
+	public static final Map<String, String> PACKAGE_MAPPER = new HashMap<>();
+
+	static {
+		PACKAGE_MAPPER.put("service-", "service.");
+		PACKAGE_MAPPER.put("intf-", "intf.");
+		PACKAGE_MAPPER.put("module-", "module.");
+	}
+
+	@Bean
+	public Docket createRestApi() {
+		// 设置可以扫描多个包逗号分隔
+		String packageStr = getControllerPackage(appName);
+
+		if (!specialControllerPackage.equals("NONE")) {
+			packageStr = packageStr + "," + specialControllerPackage;
+		}
+
+		return new Docket(DocumentationType.OAS_30)
+				.apiInfo(apiInfo())
+				.select()
+				.apis(basePackage(packageStr))
+				.paths(PathSelectors.any())
+				.build()
+				.securitySchemes(securitySchemes())
+				.securityContexts(securityContexts());
+	}
+
+	private static String replaceLast(String text, String regex, String replacement) {
+		return text.replaceFirst("(?s)" + regex + "(?!.*?" + regex + ")", replacement);
+	}
+
+	public static void main(String[] args) {
+		System.out.println(getControllerPackage("service-sysmgr"));
+		System.out.println(getControllerPackage("intf-hlra-ws"));
+		System.out.println(getControllerPackage("module-log-http"));
+	}
+
+	private static String getControllerPackage(String appName) {
+		Assert.notNull(appName, "appName not allowed null!");
+		String lower = appName.toLowerCase();
+		String[] suffixes = lower.split("-");
+		int length = suffixes.length;
+		if (length == 1) {
+			return BASE_PKG_PREFIX + lower + BASE_PKG_SUFFIX;
+		} else {
+			StringBuilder pkgs = new StringBuilder(BASE_PKG_PREFIX);
+			pkgs.append(getPackage(lower));
+			List<String> subs = new ArrayList<>();
+			for (int i = 1; i < length; i++) {
+				subs.add(suffixes[i]);
+				pkgs.append(suffixes[i]);
+			}
+			pkgs.append(BASE_PKG_SUFFIX);
+			String subsStr = subs.stream().collect(Collectors.joining("."));
+			while (subsStr.indexOf(".") > -1) {
+				StringBuilder sb = new StringBuilder(",");
+				sb.append(BASE_PKG_PREFIX);
+				sb.append(getPackage(lower));
+				sb.append(subsStr);
+				sb.append(BASE_PKG_SUFFIX);
+				pkgs.append(sb);
+				subsStr = replaceLast(subsStr.toString(), "\\.", "");
+			}
+			return pkgs.toString();
+		}
+	}
+
+	public static String getPackage(String lower) {
+		for (Map.Entry<String, String> entry : PACKAGE_MAPPER.entrySet()) {
+			if (lower.startsWith(entry.getKey())) {
+				return entry.getValue();
+			}
+		}
+		throw new BusinessException("unsupported the project name prefix:{},please config in SwaggerConfig.PACKAGE_MAPPER", lower);
+	}
+
+	public static Predicate<RequestHandler> basePackage(final String basePackage) {
+		return new Predicate<RequestHandler>() {
+			@Override
+			public boolean test(RequestHandler t) {
+				return declaringClass(t).map(handlerPackage(basePackage)).orElse(true);
+			}
+		};
+	}
+
+	private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
+		return input -> {
+			for (String strPackage : basePackage.split(",")) {
+				boolean isMatch = input.getPackage().getName().startsWith(strPackage);
+				if (isMatch) {
+					return true;
+				}
+			}
+			return false;
+		};
+	}
+
+	@SuppressWarnings("deprecation")
+	private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
+		return Optional.ofNullable(input.declaringClass());
+	}
+
+	private ApiInfo apiInfo() {
+		return new ApiInfoBuilder().title(appName + " Rest Api").version("1.0").build();
+	}
+
+	private List<SecurityScheme> securitySchemes() {
+		List<SecurityScheme> schemes = new ArrayList<>();
+		schemes.add(new ApiKey("Authorization", "token", "header"));
+		return schemes;
+	}
+
+	private List<SecurityContext> securityContexts() {
+		return Arrays.asList(SecurityContext.builder()
+				.securityReferences(defaultAuth())
+				.operationSelector(ctx -> matcher.match(CommonConstants.AUTH_PATTERN, ctx.requestMappingPattern()))
+				.build());
+	}
+
+	private List<SecurityReference> defaultAuth() {
+		AuthorizationScope[] authorizationScopes = { new AuthorizationScope("global", "accessEverything") };
+		return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
+	}
+
+}

+ 61 - 0
micro-common/src/main/java/com/crunii/micro/common/config/SwaggerConflictFixConfiguration.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
+import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
+import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author 田平 create 2023-02-13 10:41:44
+ */
+@Configuration
+public class SwaggerConflictFixConfiguration {
+
+	/**
+	 * 解决SpringBoot2.6.x与Swagger3的兼容问题,以下配置也是必须的
+	 *
+	 * @return
+	 */
+	@Bean
+	public BeanPostProcessor regSpringfoxHandlerProviderBeanPostProcessor() {
+		return new BeanPostProcessor() {
+			@Override
+			public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+				if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
+					customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
+				}
+				return bean;
+			}
+
+			private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
+				List<T> copy = mappings.stream()
+						.filter(mapping -> mapping.getPatternParser() == null)
+						.collect(Collectors.toList());
+				mappings.clear();
+				mappings.addAll(copy);
+			}
+
+			private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
+				try {
+					Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
+					field.setAccessible(true);
+					return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
+				} catch (Exception e) {
+					throw new IllegalStateException(e);
+				}
+			}
+		};
+	}
+}

+ 43 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/BNConstants.java

@@ -0,0 +1,43 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+/**
+ * <pre>t_busi_log.busi_name专用配置类,不要重复了</pre>
+ * @author 田平 create 2020年1月13日下午4:46:11
+ *
+ */
+public class BNConstants {
+
+	public static final String BN_CORE_TEST = "CORE_TEST";
+
+	public static final String BN_CORE_TEST_DEMO = "CORE_TEST_DEMO";
+
+	public static final String BN_CFX_CLIENT_DEMO_TEST = "CFX_CLIENT_DEMO_TEST";
+
+	public static final String BN_CORE_TEST_HTTP_UTIL = "CORE_TEST_HTTP_UTIL";
+
+	public static final String BN_PRODUCER_FILE = "PRODUCER_FILE";
+
+	public static final String BN_CDMA_STOP_OPEN = "CDMA_STOP_OPEN";
+
+	public static final String BN_BURIED_TEST = "BURIED_TEST";
+
+	public static final String BN_SERVER_CORE_TEST = "SERVER_CORE_TEST";
+
+	public static final String BN_SERVER_AXIS_TEST = "SERVER_AXIS_TEST";
+
+	public static final String BN_SERVER_WEBFLUX_TEST = "SERVER_WEBFLUX_TEST";
+
+	public static final String BN_SERVER_WEBFLUX_TEST_USER = "SERVER_WEBFLUX_TEST_USER";
+
+	/**
+	 * 编排调用采控
+	 */
+	public static final String BN_PROCESS_API_EXCHANGE = "BN_PROCESS_API_EXCHANGE";
+
+	public static void main(String[] args) {
+		System.out.println(1231);
+	}
+}

+ 199 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/CommonConstants.java

@@ -0,0 +1,199 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author 田平 create 2018年10月10日下午6:17:08
+ */
+public class CommonConstants {
+	/**
+	 * //有效
+	 */
+	public static final int STATE_VALID = 1;
+	/**
+	 * //无效
+	 */
+	public static final int STATE_INVALID = 0;
+
+	/**
+	 * 状态:1是  0否
+	 */
+	public static final int STATE_YES = 1;
+	public static final int STATE_NO = 0;
+	/**
+	 * //普通完全匹配
+	 */
+	public static final int URL_TYPE_COMMON = 1;
+	/**
+	 * //Ant匹配
+	 */
+	public static final int URL_TYPE_ANT = 2;
+	/**
+	 * //需要操作日志
+	 */
+	public static final int LOG_FLAG_YES = 1;
+	/**
+	 * //不需要操作日志
+	 */
+	public static final int LOG_FLAG_NO = 0;
+
+	public static final String API_VERSION_GRAY = "gray";
+
+	public static final String API_VERSION_DEFAULT = "default";
+
+	public static final String API_VERSION = "apiVersion";
+
+	public static final String WF_ENGINE_CLUSTER_SERIAL_NO = "WF_ENGINE_CLUSTER_SERIAL_NO";
+
+	public static final String TYPE_CLIENT = "CLIENT";
+
+	public static final String TYPE_SERVER = "SERVER";
+
+	public static final String RESULT_SUCCESS = "SUCCESS";
+
+	public static final String RESULT_FAILURE = "FAILURE";
+
+	public static final String AUTH_PATTERN = "/**/auth/**";
+
+	/**
+	 * 是否是需要授权的url
+	 */
+	public static final String HEADER_IS_NEED_AUTH_URL = "is_need_auth_url";
+
+	public static final String HEADER_TOKEN = "token";
+	/**
+	 * 当前用户的信息的json字符串,不包含权限相关信息 roles urls treeNodes
+	 */
+	public static final String HEADER_CURRENT_USER = "CURRENT_USER_JSON_STRING";
+
+	public static final String HEADER_INNER_AUTH_TOKEN = "inner_auth_token";
+
+	public static final String INNER_AUTH_TOKEN = "xoxntfvsakqtrlfwdtegvzab";
+
+	public static final String SCOPE_WEB = "WEB";
+
+	public static final String SCOPE_PHONE = "PHONE";
+
+	public static final String SCOPE_SSO = "SSO";
+
+	public static final String LOGIN_TYPE_SMS = "1";
+
+	public static final String LOGIN_TYPE_PASSWORD = "0";
+
+	public static final Map<String,String> LOGIN_TYPE_MAP = new HashMap();
+	static {
+		LOGIN_TYPE_MAP.put(LOGIN_TYPE_SMS,"短信验证码登录");
+		LOGIN_TYPE_MAP.put(LOGIN_TYPE_PASSWORD,"密码登录");
+	}
+
+	// 登录成功
+	public static final int LOGIN_SUCCESS = 1;
+	// 未注册用户
+	public static final int LOGIN_FAIL_NOT_REG = 0;
+	// 登录失败、错误的密码、或被锁定
+	public static final int LOGIN_FAIL_ERR_PWD = -1;
+	// 登录失败、错误的短信验证码
+	public static final int LOGIN_FAIL_ERR_SMS_CODE = -2;
+	// 风险请求-登录失败、错误的客户端类型
+	public static final int LOGIN_FAIL_ERR_SCOPE = -3;
+	// 用户已失效
+	public static final int LOGIN_FAIL_INVALID_USER = -4;
+	// 风险请求-解密失败
+	public static final int LOGIN_FAIL_ERR_DECRYPTION = -5;
+
+
+	public static final List<Integer> SUPER_LOGIN_IDS = new ArrayList<Integer>();
+
+
+	/**
+	 * 公共组权限
+	 */
+	public static List<Integer> COMMON_GROUP_IDS = new ArrayList<>();
+	static {
+		/**
+		 * // 超级用户id可以配置多个
+		 */
+		SUPER_LOGIN_IDS.add(9999);
+
+		COMMON_GROUP_IDS.add(9999);
+		COMMON_GROUP_IDS.add(9998);
+	}
+
+	public static final String SUPER_LOGIN_IDS_SQL = SUPER_LOGIN_IDS.stream().map(id -> id.toString()).collect(Collectors.joining(",", "(", ") "));
+	/**
+	 * // 最大的手机终端数
+	 */
+	public static final int MAX_PHONE_CLIENT = 20;
+
+	/**
+	 * // 根菜单编码
+	 */
+	public static final String ROOT_NODE_ID = "1";
+
+	/**9998
+	 * 公共权限角色ID,新建员工的公共权限(如修改密码等,没有对应的左侧的菜单)
+	 */
+	public static final Integer COMMON_ROLE_ID = 9999;
+
+
+	/**
+	 *
+	 */
+	public static final int MENU_TYPE_DIR = 0;
+	/**
+	 * //菜单
+	 */
+	public static final int MENU_TYPE_MENU = 1;
+	/**
+	 * //按钮
+	 */
+	public static final int MENU_TYPE_BUTTON = 2;
+
+	/**
+	 * 在用户手动输入id值的情况下,关闭实体id生成策略
+	 */
+	public static final String TURN_OFF_AUTO_ID = "0";
+
+	/**
+	 * obs_file表的对应业务CODE编码————纪实系统业务编码
+	 */
+	public static final String OBS_FILE_RECORD_CODE = "RECORD_ACTUAL_EVENTS";
+
+	/**
+	 * obs_file表的对应业务CODE编码————图绘青春业务编码
+	 */
+	public static final String OBS_FILE_PIC_YOUTH_CODE = "RECORD_PIC_YOUTH";
+
+	/**
+	 * obs_file表的对应业务CODE编码————赛事业务编码
+	 */
+	public static final String OBS_FILE_MATCH_CODE = "RECORD_MATCH";
+
+	/**
+	 * obs_file表的对应业务CODE编码————用户头像
+	 */
+	public static final String OBS_FILE_HEAD_PIC = "OBS_FILE_HEAD_PIC";
+
+	/**
+	 * obs_file表的对应业务CODE编码————培训基地编码
+	 */
+
+	public static final String OBS_FILE_PXJD= "PXJD";
+
+	/**
+	 * 赛事签到二维码标识
+	 */
+	public static final String MATCH_SIGN_IDENTIFY= "QCCQ_SIGN";
+
+	/**
+	 * 活动签到二维码标识
+	 */
+	public static final String ACTIVITY_SIGN_IDENTIFY= "ACTIVITY_SIGN";
+}

+ 88 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/DSConstants.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 田平 create 2021年3月31日下午5:17:50
+ */
+@Slf4j
+public class DSConstants {
+
+	public static final String DATABASE_MYSQL = "mysql";
+	public static final String DATABASE_POSTGRESQL = "postgresql";
+	public static final String DATABASE_ORACLE = "oracle";
+	public static final String DATABASE_INFORMIX = "informix";
+	public static final String DATABASE_UNKNOWN = "unknown";
+	public static final String DATABASE_DM = "dm";
+
+	/**
+	 * 不需要记录sql日志的表,比如各种日志表,防止sql日志表过大
+	 */
+	public static final List<String> NO_SQL_TABS = new ArrayList<>();
+
+	static {
+		NO_SQL_TABS.add("tma_sql_log");
+		NO_SQL_TABS.add("tma_oper_log");
+		NO_SQL_TABS.add("tma_http_log");
+		NO_SQL_TABS.add("tma_call_log");
+		NO_SQL_TABS.add("t_sql_log");
+		NO_SQL_TABS.add("t_oper_log");
+		NO_SQL_TABS.add("t_http_log");
+		NO_SQL_TABS.add("t_call_log");
+	}
+
+	public static boolean containLogTable(String sql) {
+		String checkSql = sql.toLowerCase();
+		for (String tab : NO_SQL_TABS) {
+			if (checkSql.indexOf(tab) > -1) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	public static final String POOL_NAME_SUFFIX = "-db-pool";
+
+	public static final String AOP_POINTCUT_EXPRESSION = "execution(* com.crunii.micro..*Service.*(..))";
+
+	//start数据源名称
+	public static final String DB_MAIN = "main";
+
+	//public static final String DB_LOG = "log";
+
+	//end数据源名称
+
+	/**
+	 * 非jta事务配置文件前缀
+	 */
+	public static final String DATA_ID_DRUID_PREFIX = "db-druid-";
+	/**
+	 * jta事务配置前缀
+	 */
+	public static final String DATA_ID_JTA_PREFIX = "db-jta-";
+
+	public static String getDruidDsDataId(String dbName, String dbType) {
+		return DATA_ID_DRUID_PREFIX + dbName + "-" + dbType + ".yml";
+	}
+
+	public static String getJtaDsDataId(String dbName, String dbType) {
+		return DATA_ID_JTA_PREFIX + dbName + "-" + dbType + ".yml";
+	}
+
+	public static void main(String[] args) {
+		System.out.println(getDruidDsDataId("mysql", "main"));
+		System.out.println(getJtaDsDataId("mysql", "main"));
+		System.out.println(getDruidDsDataId("postgresql", "main"));
+		System.out.println(getJtaDsDataId("postgresql", "main"));
+	}
+
+	public static final String DOMAIN_BASE_PKG = "com.crunii.micro.domain";
+
+}

+ 11 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/FontConstants.java

@@ -0,0 +1,11 @@
+package com.crunii.micro.common.constants;
+
+/**
+ * <pre>字体常量类</pre>
+ * @author gongsongtao
+ * @date 2023/12/6 16:19
+ */
+public class FontConstants {
+
+    public static String PingFang_SC="PingFang SC";
+}

+ 17 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/LockConstants.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+/**
+ * @author 田平 create 2021/9/3 12:15
+ */
+public class LockConstants {
+
+	//加锁的key,全局唯一
+	public static final String LOCK_TOKEN = "LOCK:TOKEN";
+	public static final String LOCK_MENU = "LOCK:MENU";
+
+
+}

+ 39 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/LogConstants.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+/**
+ * @author 田平 create 2021/9/17 11:03
+ */
+public class LogConstants {
+	/**
+	 * 业务类型
+	 */
+	public static final String BUSI_TYPE = "busiType";
+	/**
+	 * 业务编号
+	 */
+	public static final String BUSI_NO = "busiNo";
+
+	/**
+	 * 交易编号
+	 */
+	public static final String TRADE_NO = "tradeNo";
+
+	/**
+	 * 交易编号
+	 */
+	public static final String LOGIN_ID = "loginId";
+
+	/**
+	 * 交易编号
+	 */
+	public static final String LOGIN_CODE = "loginCode";
+
+	/**
+	 * 交易编号
+	 */
+	public static final String WORKER_NAME = "workerName";
+}

+ 141 - 0
micro-common/src/main/java/com/crunii/micro/common/constants/RedisKeys.java

@@ -0,0 +1,141 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.constants;
+
+/**
+ * <pre>
+ * redis的对象key,集中管理避免冲突
+ * </pre>
+ *
+ * @author 田平 create 2018年11月6日下午2:16:25
+ */
+public class RedisKeys {
+
+	public static final String TOKEN_WEB_KEY = CommonConstants.SCOPE_WEB + "_MICRO_TOKEN";
+
+	public static final String TOKEN_PHONE_KEY = CommonConstants.SCOPE_PHONE + "_MICRO_TOKEN";
+
+	public static final String MICRO_CAPTCHA_KEY_PREFIX = "MICRO_CAPTCHA_KEY_PREFIX_";
+
+	public static final String LOGIN_PWD_ERROR_KEY_PREFIX = "MICRO_LOGIN_PWD_ERROR_KEY_";
+
+	public static final String SMS_LOGIN_CODE = "SMS_LOGIN_CODE";
+
+	public static final String SMS_TRY_LOGIN_CODE = "SMS_TRY_LOGIN_CODE";
+
+	/**
+	 * // 3600 秒
+	 */
+	public static final int TOKEN_WEB_INACTIVE_TIMEOUT = 3600;
+
+	/**
+	 * // 1天
+	 */
+	public static final int TOKEN_PHONE_INACTIVE_TIMEOUT = 3600 * 24;
+	/**
+	 * // 秒
+	 */
+	public static final int CAPTCHA_INACTIVE_TIMEOUT = 120;
+
+	/**
+	 * // 最大密码登录错误次数,超过后单位时间内不能登录
+	 */
+	public static final int LOGIN_PWD_MAX_ERROR_TIMES = 5;
+
+	/**
+	 * // 登录密码错误后多少时间不能从新登录单位小时
+	 */
+	public static final int LOGIN_PWD_MAX_ERROR_PERIOD = 1;
+
+	/**
+	 * 那些url记录日志的缓解key
+	 */
+	public static final String LOG_URLS_CACHE_KEY = "MICRO_LOG_URLS_CACHE_KEY";
+
+	public static final String TEMPLATE_CACHE_KEY = "MICRO_TEMPLATE_CACHE_KEY";
+
+	public static final String PHONE_TOKEN_USER_MAP_KEY = "MICRO_PHONE_TOKEN_USER_MAP_KEY";
+	public static final String CAPTCHA_MAP_KEY = "CAPTCHA_MAP_KEY";
+	public static final String LOGIN_PWD_ERROR_KEY = "LOGIN_PWD_ERROR_KEY";
+
+	/**
+	 * 分布式ID 服务器元数据管理的key
+	 */
+	public static final String DISTRIBUTED_SNOWFLAKE_KEY = "DISTRIBUTED_SNOWFLAKE_ID_KEY";
+
+
+	/**
+	 * 数据的缓存key
+	 */
+	public static final String TSM_URL_CACHE_KEY = "TSM_URL_CACHE_KEY";
+	public static final String TSM_MENU_CACHE_KEY = "TSM_MENU_CACHE_KEY";
+	public static final String TSM_APP_CONF_CACHE_KEY = "TSM_APP_CONF_CACHE_KEY";
+	public static final String TSM_PROPERTY_VALUE_CACHE_KEY = "TSM_PROPERTY_VALUE_CACHE_KEY";
+
+	public static final String RETRIEVE_PWD_KEY = "RETRIEVE_PWD_KEY";
+
+	/**
+	 * SSO相关的key
+	 */
+	public static final String SSO_AUTH_CODE_CACHE_KEY = "SSO_AUTH_CODE_CACHE_KEY";
+	public static final String SSO_TOKEN_CACHE_KEY = "SSO_TOKEN_CACHE_KEY";
+
+	public static final String LABEL_SYNCHRONOUS_TREE = "LABEL_SYNCHRONOUS_TREE";
+	public static final String LABEL_USE_COUNT = "LABEL_USE_COUNT";
+
+	public static final String HOT_LABEL_FORTY_EIGHT = "HOT_LABEL_FORTY_EIGHT";
+	public static final String HOT_LABEL_LONG_TIME = "HOT_LABEL_LONG_TIME";
+
+	public static final String MATCH_ACTIVITY = "MATCH_ACTIVITY";
+
+	/**
+	 * 图绘青春,热门问题key
+	 */
+	public static final String HOT_FILE_SEARCH = "HOT_FILE_SEARCH";
+
+	/**
+	 * 手机登录登录
+	 */
+	public static final String PHONE_SHORT_CODE = "PHONE_SHORT_CODE";
+
+	/**
+	 * 手机号注册
+	 */
+	public static final String PHONE_REGISTER = "PHONE_REGISTER";
+
+	/**
+	 * 手机号注册session
+	 */
+	public static final String PHONE_REGISTER_SESSION = "PHONE_REGISTER_SESSION";
+
+	/**
+	 * 首页地图数据key
+	 */
+	public static final String DASHBOARD_MAP_KEY = "DASHBOARD_MAP_KEY";
+
+	/**
+	 * 首页网站数据key
+	 */
+	public static final String WEBSITE_GATHER_KEY = "WEBSITE_GATHER_KEY";
+
+	/**
+	 * 系统主题key
+	 */
+	public static final String SYS_THEME_KEY = "SYS_THEME_KEY";
+
+	/**
+	 * APP签名key
+	 */
+	public static final String SIGN_KEY = "SIGN_KEY";
+
+	/**
+	 * 权限节点
+	 */
+	public static final String AUTH_TREE_NODE = "AUTH_TREE_NODE";
+
+	/**
+	 * 短信验证码key
+	 */
+	public static final String VALIDATE_CODE_KEY = "YCQ_YOUTH_Call_CREATE_APPEAL_VALIDATE_CODE:";
+}

+ 426 - 0
micro-common/src/main/java/com/crunii/micro/common/converter/ConvertUtilBean.java

@@ -0,0 +1,426 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.converter;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.ConversionException;
+import org.apache.commons.beanutils.Converter;
+import org.apache.commons.beanutils.converters.DateConverter;
+import org.apache.commons.beanutils.converters.*;
+
+import java.io.File;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author 田平 create 2020年3月31日下午3:44:04
+ */
+@Slf4j
+public class ConvertUtilBean {
+	private static final Integer ZERO = new Integer(0);
+	private static final Character SPACE = new Character(' ');
+
+	private final ConcurrentHashMap<Class<?>, Converter> converters = new ConcurrentHashMap<Class<?>, Converter>(32);
+
+	public ConvertUtilBean() {
+		register();
+	}
+
+	/**
+	 * Convert the specified value into a String. If the specified value is an array, the first element (converted to a String) will be returned. The registered {@link Converter} for the <code>java.lang.String</code> class will be used, which allows applications to customize Object->String conversions (the default implementation simply uses toString()).
+	 *
+	 * @param value Value to be converted (may be null)
+	 * @return The converted String value or null if value is null
+	 */
+	public String convert(Object value) {
+
+		if (value == null) {
+			return null;
+		} else if (value.getClass().isArray()) {
+			if (Array.getLength(value) < 1) {
+				return (null);
+			}
+			value = Array.get(value, 0);
+			if (value == null) {
+				return null;
+			} else {
+				final Converter converter = lookup(String.class);
+				return (converter.convert(String.class, value));
+			}
+		} else {
+			final Converter converter = lookup(String.class);
+			return (converter.convert(String.class, value));
+		}
+
+	}
+
+	/**
+	 * Convert the specified value to an object of the specified class (if possible). Otherwise, return a String representation of the value.
+	 *
+	 * @param value Value to be converted (may be null)
+	 * @param clazz Java class to be converted to (must not be null)
+	 * @return The converted value
+	 * @throws ConversionException if thrown by an underlying Converter
+	 */
+	public Object convert(final String value, final Class<?> clazz) {
+
+		if (log.isDebugEnabled()) {
+			log.debug("Convert string '" + value + "' to class '" +
+					clazz.getName() + "'");
+		}
+		Converter converter = lookup(clazz);
+		if (converter == null) {
+			converter = lookup(String.class);
+		}
+		if (log.isTraceEnabled()) {
+			log.trace("  Using converter " + converter);
+		}
+		return (converter.convert(clazz, value));
+
+	}
+
+	/**
+	 * Convert an array of specified values to an array of objects of the specified class (if possible). If the specified Java class is itself an array class, this class will be the type of the returned value. Otherwise, an array will be constructed whose component type is the specified class.
+	 *
+	 * @param values Array of values to be converted
+	 * @param clazz  Java array or element class to be converted to (must not be null)
+	 * @return The converted value
+	 * @throws ConversionException if thrown by an underlying Converter
+	 */
+	public Object convert(final String[] values, final Class<?> clazz) {
+
+		Class<?> type = clazz;
+		if (clazz.isArray()) {
+			type = clazz.getComponentType();
+		}
+		if (log.isDebugEnabled()) {
+			log.debug("Convert String[" + values.length + "] to class '" +
+					type.getName() + "[]'");
+		}
+		Converter converter = lookup(type);
+		if (converter == null) {
+			converter = lookup(String.class);
+		}
+		if (log.isTraceEnabled()) {
+			log.trace("  Using converter " + converter);
+		}
+		final Object array = Array.newInstance(type, values.length);
+		for (int i = 0; i < values.length; i++) {
+			Array.set(array, i, converter.convert(type, values[i]));
+		}
+		return (array);
+
+	}
+
+	/**
+	 * Convert the value to an object of the specified class (if possible). If no converter for the desired target type is registered, the passed in object is returned unchanged.
+	 *
+	 * @param value      Value to be converted (may be null)
+	 * @param targetType Class of the value to be converted to (must not be null)
+	 * @return The converted value
+	 * @throws ConversionException if thrown by an underlying Converter
+	 */
+	public Object convert(final Object value, final Class<?> targetType) {
+
+		final Class<?> sourceType = value == null ? null : value.getClass();
+
+		if (log.isDebugEnabled()) {
+			if (value == null) {
+				log.debug("Convert null value to type '" +
+						targetType.getName() + "'");
+			} else {
+				log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
+						"' to type '" + targetType.getName() + "'");
+			}
+		}
+
+		Object converted = value;
+		Converter converter = lookup(sourceType, targetType);
+		if (converter != null) {
+			if (log.isTraceEnabled()) {
+				log.trace("  Using converter " + converter);
+			}
+			converted = converter.convert(targetType, value);
+		}
+		if (String.class.equals(targetType) && converted != null &&
+				!(converted instanceof String)) {
+
+			// NOTE: For backwards compatibility, if the Converter
+			//       doesn't handle  conversion-->String then
+			//       use the registered String Converter
+			converter = lookup(String.class);
+			if (converter != null) {
+				if (log.isTraceEnabled()) {
+					log.trace("  Using converter " + converter);
+				}
+				converted = converter.convert(String.class, converted);
+			}
+
+			// If the object still isn't a String, use toString() method
+			if (converted != null && !(converted instanceof String)) {
+				converted = converted.toString();
+			}
+
+		}
+		return converted;
+
+	}
+
+	/**
+	 * Remove all registered {@link Converter}s, and re-establish the standard Converters.
+	 */
+	private void register() {
+		registerPrimitives(true);
+		registerStandard(true, false);
+		registerOther(true);
+		registerArrays(true, 0);
+		register(BigDecimal.class, new BigDecimalConverter(false));
+		register(BigInteger.class, new BigIntegerConverter(false));
+	}
+
+	/**
+	 * Register the converters for primitive types.
+	 * </p>
+	 * This method registers the following converters:
+	 * <ul>
+	 * <li><code>Boolean.TYPE</code> - {@link BooleanConverter}</li>
+	 * <li><code>Byte.TYPE</code> - {@link ByteConverter}</li>
+	 * <li><code>Character.TYPE</code> - {@link CharacterConverter}</li>
+	 * <li><code>Double.TYPE</code> - {@link DoubleConverter}</li>
+	 * <li><code>Float.TYPE</code> - {@link FloatConverter}</li>
+	 * <li><code>Integer.TYPE</code> - {@link IntegerConverter}</li>
+	 * <li><code>Long.TYPE</code> - {@link LongConverter}</li>
+	 * <li><code>Short.TYPE</code> - {@link ShortConverter}</li>
+	 * </ul>
+	 *
+	 * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
+	 *                       <code>false</code> if a default value should be used.
+	 */
+	private void registerPrimitives(final boolean throwException) {
+		register(Boolean.TYPE, throwException ? new BooleanConverter() : new BooleanConverter(Boolean.FALSE));
+		register(Byte.TYPE, throwException ? new ByteConverter() : new ByteConverter(ZERO));
+		register(Character.TYPE, throwException ? new CharacterConverter() : new CharacterConverter(SPACE));
+		register(Double.TYPE, throwException ? new DoubleConverter() : new DoubleConverter(ZERO));
+		register(Float.TYPE, throwException ? new FloatConverter() : new FloatConverter(ZERO));
+		register(Integer.TYPE, throwException ? new IntegerConverter() : new IntegerConverter(ZERO));
+		register(Long.TYPE, throwException ? new LongConverter() : new LongConverter(ZERO));
+		register(Short.TYPE, throwException ? new ShortConverter() : new ShortConverter(ZERO));
+	}
+
+	/**
+	 * Register the converters for standard types.
+	 * </p>
+	 * This method registers the following converters:
+	 * <ul>
+	 * <li><code>BigDecimal.class</code> - {@link BigDecimalConverter}</li>
+	 * <li><code>BigInteger.class</code> - {@link BigIntegerConverter}</li>
+	 * <li><code>Boolean.class</code> - {@link BooleanConverter}</li>
+	 * <li><code>Byte.class</code> - {@link ByteConverter}</li>
+	 * <li><code>Character.class</code> - {@link CharacterConverter}</li>
+	 * <li><code>Double.class</code> - {@link DoubleConverter}</li>
+	 * <li><code>Float.class</code> - {@link FloatConverter}</li>
+	 * <li><code>Integer.class</code> - {@link IntegerConverter}</li>
+	 * <li><code>Long.class</code> - {@link LongConverter}</li>
+	 * <li><code>Short.class</code> - {@link ShortConverter}</li>
+	 * <li><code>String.class</code> - {@link StringConverter}</li>
+	 * </ul>
+	 *
+	 * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
+	 *                       <code>false</code> if a default value should be used.
+	 * @param defaultNull    <code>true</code>if the <i>standard</i> converters
+	 */
+	private void registerStandard(final boolean throwException, final boolean defaultNull) {
+
+		final Number defaultNumber = defaultNull ? null : ZERO;
+		final BigDecimal bigDecDeflt = defaultNull ? null : new BigDecimal("0.0");
+		final BigInteger bigIntDeflt = defaultNull ? null : new BigInteger("0");
+		final Boolean booleanDefault = defaultNull ? null : Boolean.FALSE;
+		final Character charDefault = defaultNull ? null : SPACE;
+		final String stringDefault = defaultNull ? null : "";
+
+		register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
+		register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
+		register(Boolean.class, throwException ? new BooleanConverter() : new BooleanConverter(booleanDefault));
+		register(Byte.class, throwException ? new ByteConverter() : new ByteConverter(defaultNumber));
+		register(Character.class, throwException ? new CharacterConverter() : new CharacterConverter(charDefault));
+		register(Double.class, throwException ? new DoubleConverter() : new DoubleConverter(defaultNumber));
+		register(Float.class, throwException ? new FloatConverter() : new FloatConverter(defaultNumber));
+		register(Integer.class, throwException ? new IntegerConverter() : new IntegerConverter(defaultNumber));
+		register(Long.class, throwException ? new LongConverter() : new LongConverter(defaultNumber));
+		register(Short.class, throwException ? new ShortConverter() : new ShortConverter(defaultNumber));
+		register(String.class, throwException ? new StringConverter() : new StringConverter(stringDefault));
+
+	}
+
+	/**
+	 * Register the converters for other types.
+	 * </p>
+	 * This method registers the following converters:
+	 * <ul>
+	 * <li><code>Class.class</code> - {@link ClassConverter}</li>
+	 * <li><code>java.util.Date.class</code> - {@link DateConverter}</li>
+	 * <li><code>java.util.Calendar.class</code> - {@link CalendarConverter}</li>
+	 * <li><code>File.class</code> - {@link FileConverter}</li>
+	 * <li><code>java.sql.Date.class</code> - {@link SqlDateConverter}</li>
+	 * <li><code>java.sql.Time.class</code> - {@link SqlTimeConverter}</li>
+	 * <li><code>java.sql.Timestamp.class</code> - {@link SqlTimestampConverter}</li>
+	 * <li><code>URL.class</code> - {@link URLConverter}</li>
+	 * </ul>
+	 *
+	 * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
+	 *                       <code>false</code> if a default value should be used.
+	 */
+	private void registerOther(final boolean throwException) {
+		register(Class.class, throwException ? new ClassConverter() : new ClassConverter(null));
+		//register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
+		register(java.util.Date.class, com.crunii.micro.common.converter.DateConverter.getDateConverter());
+		register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
+		register(File.class, throwException ? new FileConverter() : new FileConverter(null));
+		register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
+		register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));
+		register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
+		register(URL.class, throwException ? new URLConverter() : new URLConverter(null));
+	}
+
+	/**
+	 * Register array converters.
+	 *
+	 * @param throwException   <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
+	 *                         <code>false</code> if a default value should be used.
+	 * @param defaultArraySize The size of the default array value for array converters (N.B. This values is ignored if <code>throwException</code> is <code>true</code>). Specifying a value less than zero causes a <code>null<code> value to be used for the default.
+	 */
+	private void registerArrays(final boolean throwException, final int defaultArraySize) {
+
+		// Primitives
+		registerArrayConverter(Boolean.TYPE, new BooleanConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Byte.TYPE, new ByteConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Double.TYPE, new DoubleConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Float.TYPE, new FloatConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Integer.TYPE, new IntegerConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Long.TYPE, new LongConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Short.TYPE, new ShortConverter(), throwException, defaultArraySize);
+
+		// Standard
+		registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
+		registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Boolean.class, new BooleanConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Byte.class, new ByteConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Character.class, new CharacterConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Double.class, new DoubleConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Float.class, new FloatConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Integer.class, new IntegerConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Long.class, new LongConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Short.class, new ShortConverter(), throwException, defaultArraySize);
+		registerArrayConverter(String.class, new StringConverter(), throwException, defaultArraySize);
+
+		// Other
+		registerArrayConverter(Class.class, new ClassConverter(), throwException, defaultArraySize);
+		//registerArrayConverter(java.util.Date.class, new DateConverter(), throwException, defaultArraySize);
+		registerArrayConverter(java.util.Date.class, com.crunii.micro.common.converter.DateConverter.getDateConverter(), throwException,
+				defaultArraySize);
+		registerArrayConverter(Calendar.class, new DateConverter(), throwException, defaultArraySize);
+		registerArrayConverter(File.class, new FileConverter(), throwException, defaultArraySize);
+		registerArrayConverter(java.sql.Date.class, new SqlDateConverter(), throwException, defaultArraySize);
+		registerArrayConverter(java.sql.Time.class, new SqlTimeConverter(), throwException, defaultArraySize);
+		registerArrayConverter(Timestamp.class, new SqlTimestampConverter(), throwException, defaultArraySize);
+		registerArrayConverter(URL.class, new URLConverter(), throwException, defaultArraySize);
+
+	}
+
+	/**
+	 * Register a new ArrayConverter with the specified element delegate converter that returns a default array of the specified size in the event of conversion errors.
+	 *
+	 * @param componentType      The component type of the array
+	 * @param componentConverter The converter to delegate to for the array elements
+	 * @param throwException     Whether a conversion exception should be thrown or a default value used in the event of a conversion error
+	 * @param defaultArraySize   The size of the default array
+	 */
+	private void registerArrayConverter(final Class<?> componentType, final Converter componentConverter, final boolean throwException, final int defaultArraySize) {
+		final Class<?> arrayType = Array.newInstance(componentType, 0).getClass();
+		Converter arrayConverter = null;
+		if (throwException) {
+			arrayConverter = new ArrayConverter(arrayType, componentConverter);
+		} else {
+			arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
+		}
+		register(arrayType, arrayConverter);
+	}
+
+	/**
+	 * strictly for convenience since it has same parameter order as Map.put
+	 */
+	private void register(final Class<?> clazz, final Converter converter) {
+		register(new ConverterFacade(converter), clazz);
+	}
+
+	/**
+	 * Look up and return any registered {@link Converter} for the specified destination class; if there is no registered Converter, return <code>null</code>.
+	 *
+	 * @param clazz Class for which to return a registered Converter
+	 * @return The registered {@link Converter} or <code>null</code> if not found
+	 */
+	private Converter lookup(final Class<?> clazz) {
+		return (converters.get(clazz));
+	}
+
+	/**
+	 * Look up and return any registered {@link Converter} for the specified source and destination class; if there is no registered Converter, return <code>null</code>.
+	 *
+	 * @param sourceType Class of the value being converted
+	 * @param targetType Class of the value to be converted to
+	 * @return The registered {@link Converter} or <code>null</code> if not found
+	 */
+	private Converter lookup(final Class<?> sourceType, final Class<?> targetType) {
+		if (targetType == null) {
+			throw new IllegalArgumentException("Target type is missing");
+		}
+		if (sourceType == null) {
+			return lookup(targetType);
+		}
+		Converter converter = null;
+		// Convert --> String
+		if (targetType == String.class) {
+			converter = lookup(sourceType);
+			if (converter == null && (sourceType.isArray() ||
+					Collection.class.isAssignableFrom(sourceType))) {
+				converter = lookup(String[].class);
+			}
+			if (converter == null) {
+				converter = lookup(String.class);
+			}
+			return converter;
+		}
+
+		// Convert --> String array
+		if (targetType == String[].class) {
+			if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
+				converter = lookup(sourceType);
+			}
+			if (converter == null) {
+				converter = lookup(String[].class);
+			}
+			return converter;
+		}
+
+		return lookup(targetType);
+
+	}
+
+	/**
+	 * Register a custom {@link Converter} for the specified destination <code>Class</code>, replacing any previously registered Converter.
+	 *
+	 * @param converter Converter to be registered
+	 * @param clazz     Destination class for conversions performed by this Converter
+	 */
+	private void register(final Converter converter, final Class<?> clazz) {
+		converters.put(clazz, converter);
+	}
+}

+ 41 - 0
micro-common/src/main/java/com/crunii/micro/common/converter/DateConverter.java

@@ -0,0 +1,41 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.converter;
+
+import java.util.Date;
+
+import org.apache.commons.beanutils.Converter;
+
+import com.crunii.micro.common.utils.DateUtil;
+
+/**
+ * @author 田平 create 2018年10月19日下午1:02:31
+ */
+public class DateConverter implements Converter {
+
+	private static DateConverter converter = new DateConverter();
+
+	private DateConverter() {
+
+	}
+
+	public static DateConverter getDateConverter() {
+		return converter;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T> T convert(Class<T> type, Object value) {
+		if (value != null) {
+			if (value instanceof Date) {
+				return (T) value;
+			} else {
+				return (T) DateUtil.parse(value.toString());
+			}
+		} else {
+			return null;
+		}
+	}
+
+}

+ 33 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/CBTree.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author 田平 create 2019年2月27日上午11:50:46
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel(description = "CheckBox树")
+public class CBTree<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	@ApiModelProperty(value = "树形节点列表")
+	private List<TreeNode<T>> nodes;
+	@ApiModelProperty(value = "全选择节点ID列表")
+	private List<String> checkedIds;
+
+//	@ApiModelProperty(value = "半选中节点ID列表,注意修改关联的时候需要这个属性,展现不需要")
+//	private List<String> halfCheckedIds;
+
+}

+ 31 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/CaptchaInfo.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @author 田平 create 2018年10月31日下午2:49:59
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel(description = "验证码信息")
+public class CaptchaInfo implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty(value = "验证码对应的key,登录需要回传")
+	private String captchaKey;
+
+	@ApiModelProperty(value = "验证码图片的base64编码")
+	private String captchaBase64;
+
+}

+ 39 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/FileResponse.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @author 田平 create 2019年6月18日上午11:28:02
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "文件下载响应信息")
+public class FileResponse implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "文件名称")
+    private String name;
+
+    @ApiModelProperty(value = "原始文件名称")
+    private String originalFilename;
+
+    @ApiModelProperty(value = "文件状态")
+    private String status = "done";
+
+    @ApiModelProperty(value = "文件id")
+    private String uid;
+
+    @ApiModelProperty(value = "文件url")
+    private String url;
+}

+ 19 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/GeoCoordinate.java

@@ -0,0 +1,19 @@
+package com.crunii.micro.common.dto;
+
+import lombok.*;
+
+/**
+ * @author luochj
+ * @date 2021/5/11
+ */
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ToString
+public class GeoCoordinate {
+
+    private  double latitude;
+    private  double longitude;
+}

+ 20 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/NoModelWriteData.java

@@ -0,0 +1,20 @@
+package com.crunii.micro.common.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author denggl
+ * date 2024/1/18
+ */
+@Data
+public class NoModelWriteData implements Serializable {
+    private String fileName;//文件名
+    private String[] headMap;//表头数组
+    private String[] dataStrMap;//对应数据字段数组 顺序必须与headMap一致
+    private List<Map<String, Object>> dataList;//数据集合
+
+}

+ 40 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/OrderProp.java

@@ -0,0 +1,40 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2018年10月19日上午11:07:21
+ *
+ * <pre>
+ * 排序属性
+ * </pre>
+ */
+@Getter
+@Setter
+@ToString
+public class OrderProp implements Serializable {
+	private static final long serialVersionUID = 1L;
+	private String prop;// 属性名
+	private boolean ascending = true;// 是否升序,默认是升序排序
+
+	public OrderProp(){
+
+	}
+
+	public OrderProp(String prop) {
+		this.prop = prop;
+	}
+
+	public OrderProp(String prop, boolean ascending) {
+		this.prop = prop;
+		this.ascending = ascending;
+	}
+
+}

+ 68 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/PageReq.java

@@ -0,0 +1,68 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+import com.crunii.micro.common.utils.JsonUtil;
+import com.fasterxml.jackson.databind.JavaType;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import javax.validation.Valid;
+
+/**
+ * @author 田平 create 2017年4月24日下午3:02:44
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "分页请求对象")
+public class PageReq<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty(value = "页码,默认1")
+	private int pageNo = 1;
+	@ApiModelProperty(value = "页大小,默认10")
+	private int pageSize = 10;
+	@Valid
+	@ApiModelProperty(value = "参数实体")
+	private T query;
+	@ApiModelProperty(value = "排序字段,一般前端不传,由后端指定")
+	private List<OrderProp> orderProps = Collections.emptyList();
+
+	public PageReq() {
+
+	}
+
+	public PageReq(T t) {
+		this.query = t;
+	}
+
+	/**
+	 * 获取mysql的limit的开始位置
+	 *
+	 * @return
+	 */
+	public int getMyStart() {
+		return (pageNo - 1) * pageSize;
+	}
+
+	public static <K> PageReq<K> getPageReq(String json, Class<K> objClass) {
+		JavaType javaType = JsonUtil.getMapper().getTypeFactory().constructParametricType(PageReq.class, objClass);
+		try {
+			return JsonUtil.getMapper().readValue(json, javaType);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}

+ 55 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/PageRsp.java

@@ -0,0 +1,55 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author 田平 create 2017年4月24日下午3:07:34
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "分页响应对象")
+public class PageRsp<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	@ApiModelProperty(value = "页码")
+	private int pageNo;
+	@ApiModelProperty(value = "页大小")
+	private int pageSize;
+	@ApiModelProperty(value = "list结果集")
+	private List<T> result;
+	@ApiModelProperty(value = "总记录数")
+	private long totalNum;
+	/**
+	 * 手机端专用属性
+	 */
+	@ApiModelProperty(value = "是否由下一页")
+	private boolean hasNext;
+
+	public void next() {
+		this.hasNext = pageNo * pageSize < this.totalNum ? true : false;
+	}
+
+	public PageRsp() {
+		result = Collections.emptyList();
+	}
+
+	public PageRsp(int pageNo, int pageSize, int total, List<T> list) {
+		this.pageNo = pageNo;
+		this.pageSize = pageSize;
+		this.totalNum = total;
+		this.result = list;
+		next();
+	}
+}

+ 237 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/Result.java

@@ -0,0 +1,237 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import com.crunii.micro.common.exception.ErrorCode;
+import com.crunii.micro.common.utils.JsonUtil;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.http.HttpStatus;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <pre>
+ * 	因为使用泛型的原因才能生成api参数说明
+ * </pre>
+ *
+ * @author 田平 create 2013-4-28上午10:01:02
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "基础请求响应结果")
+public class Result<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 默认错误编码
+	 */
+	public static final String DEFAULT_ERROR_CODE = "-1";
+	/**
+	 * 默认编码
+	 */
+	public static final String DEFAULT_CODE = "0";
+
+	@ApiModelProperty(value = "业务调用是否成功")
+	private boolean success;
+
+	@ApiModelProperty(value = "错误编码")
+	private String code = DEFAULT_CODE;
+
+	@ApiModelProperty(value = "错误信息")
+	private String msg;
+
+	/**
+	 * 当返回的数据为单对象是实用此属性
+	 */
+	@ApiModelProperty(value = "业务数据")
+	private T data;
+
+	public static <T> Result<T> getResultFromJson(String json, Class<T> dataClass) {
+
+		JavaType javaType = JsonUtil.getMapper().getTypeFactory().constructParametricType(Result.class, dataClass);
+		try {
+			return JsonUtil.getMapper().readValue(json, javaType);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static <T> Result<T> getResultFromJson(String json, TypeReference<Result<T>> valueTypeRef) {
+		try {
+			return JsonUtil.getMapper().readValue(json, valueTypeRef);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static <R> Result<R> ofSuccess(R data) {
+		Result<R> t = new Result<>();
+		t.setMsg("success");
+		t.setData(data);
+		return t;
+	}
+
+	public static Result ofSuccess() {
+		Result t = new Result();
+		t.setMsg("success");
+		return t;
+	}
+
+	public static <R> Result<R> ofSuccessWithMsg(R data, String msg) {
+		Result<R> t = new Result<>();
+		t.setSuccess(true);
+		t.setMsg(msg);
+		t.setData(data);
+		return t;
+	}
+
+	public static <R> Result<R> ofFail(ErrorCode errorCode) {
+		return ofFail(Integer.toString(errorCode.getCode()), errorCode.getMsg());
+	}
+
+	public static <R> Result<R> ofFail(String msg) {
+		Result<R> result = new Result<>();
+		result.setSuccess(false);
+		result.setCode(DEFAULT_ERROR_CODE);
+		result.setMsg(msg);
+		return result;
+	}
+
+	public static <R> Result<R> ofFail(String code,String msg) {
+		Result<R> result = new Result<>();
+		result.setSuccess(false);
+		result.setCode(code);
+		result.setMsg(msg);
+		return result;
+	}
+
+	public static <R> Result<R> ofFail(String code,String msg,R data) {
+		Result<R> result = new Result<>();
+		result.setSuccess(false);
+		result.setCode(code);
+		result.setMsg(msg);
+		result.setData(data);
+		return result;
+	}
+	public static <R> Result<R> ofFail(String msg,R data) {
+		Result<R> result = new Result<>();
+		result.setSuccess(false);
+		result.setMsg(msg);
+		result.setData(data);
+		return result;
+	}
+
+
+	public static <R> Result<R> ofSuccessMsg(String msg) {
+		Result<R> t = new Result<>();
+		t.setMsg(msg);
+		return t;
+	}
+
+	public static void main(String[] args) {
+		Result<List<String>> r = new Result<List<String>>(Arrays.asList("abc", "abc1"));
+		System.out.println(getResultFromJson(JsonUtil.getJsonString(r), new TypeReference<Result<List<String>>>() {}));
+	}
+
+	public Result() {
+		this.success = true;
+	}
+
+	public Result(HttpStatus status) {
+		this.success = true;
+		this.code = String.valueOf(status.value());
+		this.msg = status.getReasonPhrase();
+	}
+
+	public Result(boolean success) {
+		this.success = success;
+	}
+
+	public Result(T data) {
+		this.success = true;
+		this.data = data;
+	}
+
+	public Result(boolean success, T data) {
+		this.success = success;
+		this.data = data;
+	}
+
+	public Result(boolean success, String code, T data) {
+		this.success = success;
+		this.code = code;
+		this.data = data;
+	}
+
+	public Result(String msg, boolean success) {
+		this.msg = msg;
+		this.success = success;
+	}
+
+	public Result(boolean success, String code, String msg) {
+		this.success = success;
+		this.code = code;
+		this.msg = msg;
+	}
+
+	public Result(boolean success, String code, String msg, T data) {
+		this.success = success;
+		this.code = code;
+		this.msg = msg;
+		this.data = data;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	/**
+	 * @return the success
+	 */
+	public boolean isSuccess() {
+		return success;
+	}
+
+	/**
+	 * @param success the success to set
+	 */
+	public void setSuccess(boolean success) {
+		this.success = success;
+	}
+
+	/**
+	 * @return the data
+	 */
+	public T getData() {
+		return data;
+	}
+
+	/**
+	 * @param data the data to set
+	 */
+	public void setData(T data) {
+		this.data = data;
+	}
+}

+ 27 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/SSHHost.java

@@ -0,0 +1,27 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2017年4月24日下午3:07:34
+ */
+@Setter
+@Getter
+@ToString
+public class SSHHost implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	private String host;
+	private int port = 22;
+	private String username;
+	private String password;
+	private int soTimeout = 120000;// 传输超时时间
+	private int connectTimeout = 30000;// 连接超时时间
+}

+ 64 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/ServiceInst.java

@@ -0,0 +1,64 @@
+/**
+ * addr2 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import java.io.Serializable;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2020年4月10日下午1:10:54
+ *
+ */
+@ApiModel(description = "服务实例")
+@Getter
+@Setter
+@ToString
+@EqualsAndHashCode
+@AllArgsConstructor
+@NoArgsConstructor
+public class ServiceInst implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty(value = "满足前端需求的ID,无实际意义")
+	private int instId;
+
+	@ApiModelProperty(value = "实例组,例如工作流,接口,应用 default,workflow,intf,service")
+	private String group;
+
+	@ApiModelProperty(value = "spring.application.name属性,相同的组appName不能相同")
+	private String appName;
+
+	@ApiModelProperty(value = "主机地址")
+	private String host;
+
+	@ApiModelProperty(value = "主机端口")
+	private int port;
+
+	@ApiModelProperty(value = "灰度版本号")
+	private String apiVersion;
+
+	public static String getPrefix(String group, String appName) {
+		StringBuilder sb = new StringBuilder();
+		return sb.append("/").append(group)
+				.append("/").append(appName)
+				.append("/").toString();
+	}
+
+	public String getProxyPrefix() {
+		return getPrefix(this.group, this.appName);
+	}
+
+	public String getAddr() {
+		return "[" + this.host + ":" + this.port + "]";
+	}
+
+}

+ 70 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/TreeNode.java

@@ -0,0 +1,70 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author 田平 create 2019年2月20日上午9:11:29
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel(description = "树节点")
+public class TreeNode<T> implements Serializable {
+	private static final long serialVersionUID = 1L;
+	// 1菜单,2按扭,3url组,4URL
+	public static final int NODE_TYPE_MENU = 1;
+	public static final int NODE_TYPE_BUTTON = 2;
+	public static final int NODE_TYPE_GROUP = 3;
+	public static final int NODE_TYPE_URL = 4;
+
+	@ApiModelProperty(value = "节点ID")
+	protected String nodeId;
+
+	@ApiModelProperty(value = "父节点ID")
+	protected String parentNodeId;
+
+	@ApiModelProperty(value = "真实数据ID")
+	protected String rawId;
+
+	@ApiModelProperty(value = "组Id")
+	private Integer groupId;
+
+	@ApiModelProperty(value = "节点类型,用于前端渲染图标样式,1菜单,2按扭,3url组,4URL")
+	protected Integer nodeType;
+
+	@ApiModelProperty(value = "排序")
+	private Integer sortId;
+
+	@ApiModelProperty(value = "节点名字")
+	protected String nodeName;
+
+	@ApiModelProperty(value = "图标")
+	private String icon;
+
+	@ApiModelProperty(value = "是否叶子节点")
+	protected boolean leaf;
+
+	@ApiModelProperty(value = "节点数据")
+	protected T nodeData;
+
+	@ApiModelProperty(value = "父路径s")
+	protected Set<String> parents;
+
+	@ApiModelProperty(value = "子节点列表")
+	protected List<TreeNode<T>> children = new ArrayList<TreeNode<T>>();
+
+	@ApiModelProperty(value = "URL列表")
+	protected List<TreeNode<T>> urlList = new ArrayList<TreeNode<T>>();
+}

+ 23 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/UserTest.java

@@ -0,0 +1,23 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * <pre>测试专用类</pre>
+ * @author 田平 create 2020年1月15日下午5:11:44
+ *
+ */
+@Setter
+@Getter
+@ToString
+public class UserTest {
+	
+	private String username;
+	private String password;
+	
+}

+ 61 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/WorkerC.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+import com.crunii.micro.common.utils.JsonUtil;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author 田平 create 2018年11月1日下午3:00:26
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "创建员工")
+public class WorkerC implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty(value = "组织id")
+	private String orgId;
+
+	@ApiModelProperty(value = "生日")
+	private Date birthday;
+	@ApiModelProperty(value = "邮件地址")
+	private String email;
+	@ApiModelProperty(value = "登录工号")
+	private String loginCode;
+	@ApiModelProperty(value = "手机号")
+	private String mobile;
+	@ApiModelProperty(value = "电话")
+	private String phone;
+	@ApiModelProperty(value = "性别|0女1男")
+	private Integer sex;
+	@ApiModelProperty(value = "职务")
+	private String position;
+	@ApiModelProperty(value = "姓名")
+	private String workerName;
+	@ApiModelProperty(value = "角色ids")
+	private List<Integer> roleIds;
+	@ApiModelProperty(value = "状态|1启用0禁用")
+	private int state;
+
+	@ApiModelProperty(value = "用户头像图片key")
+	private String headPortraitPicKey;
+
+	@ApiModelProperty(value = "fileId")
+	private Long fileId;
+
+
+	public static void main(String[] args) {
+		System.out.println(JsonUtil.getJsonString(new WorkerC()));
+	}
+}

+ 12 - 0
micro-common/src/main/java/com/crunii/micro/common/dto/WorkerId.java

@@ -0,0 +1,12 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.dto;
+
+/**
+ * @author 田平 create 2019年9月9日下午5:18:48
+ */
+public abstract class WorkerId {
+
+	public abstract String workerId();
+}

+ 47 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/AuditStatusEnums.java

@@ -0,0 +1,47 @@
+package com.crunii.micro.common.enums;
+
+import com.crunii.micro.common.exception.BusinessException;
+
+/**
+ * @author lmlksy
+ * @date 2023/11/22 15:26
+ */
+public enum AuditStatusEnums {
+    REJECT(0, "不通过"),
+    PASS(1, "通过"),
+    WAIT_AUDIT(2, "待审核"),
+    PUBLISHED(3, "已公示");
+
+    private final Integer statusCode;
+    private final String statusDescription;
+
+    AuditStatusEnums(Integer statusCode, String statusDescription) {
+        this.statusCode = statusCode;
+        this.statusDescription = statusDescription;
+    }
+
+    public Integer getStatusCode() {
+        return statusCode;
+    }
+
+    public String getStatusDescription() {
+        return statusDescription;
+    }
+
+    public static String getByCode(Integer code) {
+        for (AuditStatusEnums status : values()) {
+            if (status.getStatusCode() != null && status.getStatusCode() == code) {
+                return status.getStatusDescription();
+            }
+        }
+        return null;
+    }
+    public static Integer getByDescription(String description) {
+        for (AuditStatusEnums status : values()) {
+            if (status.getStatusDescription().equals(description)) {
+                return status.getStatusCode();
+            }
+        }
+        return null;
+    }
+}

+ 47 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/GovInstitutionLevel.java

@@ -0,0 +1,47 @@
+package com.crunii.micro.common.enums;
+
+
+/**
+ * @Description:愉快政组织单位级别govInstitutionLevelCode
+ * @Author: lml
+ * @Date: 2023/10/12 15:29
+ **/
+public enum GovInstitutionLevel {
+	ZHENG_GUO_JIA_JI("DAN_WEI_JI_BIE_ZHENG_GUO_JIA_JI", "正国家级"),
+	FU_GUO_JIA_JI("DAN_WEI_JI_BIE_FU_GUO_JIA_JI", "副国家级"),
+	ZHENG_SHENG_BU_JI("DAN_WEI_JI_BIE_ZHENG_SHENG_BU_JI", "正省部级"),
+	FU_SHENG_BU_JI("DAN_WEI_JI_BIE_FU_SHENG_BU_JI", "副省部级"),
+	ZHENG_SI_TING_JU_JI("DAN_WEI_JI_BIE_ZHENG_SI_TING_JU_JI", "正司厅局级"),
+	FU_SI_TING_JU_JI("DAN_WEI_JI_BIE_FU_SI_TING_JU_JI", "副司厅局级"),
+	ZHENG_XIAN_CHU_JI("DAN_WEI_JI_BIE_ZHENG_XIAN_CHU_JI", "正县处级"),
+	FU_XIAN_CHU_JI("DAN_WEI_JI_BIE_FU_XIAN_CHU_JI", "副县处级"),
+	ZHENG_XIANG_ZHEN_KE_JI("DAN_WEI_JI_BIE_ZHENG_XIANG_ZHEN_KE_JI", "正乡镇科级"),
+	FU_XIANG_ZHEN_KE_JI("DAN_WEI_JI_BIE_FU_XIANG_ZHEN_KE_JI", "副乡镇科级"),
+	QUN_ZHONG_ZI_ZHI_TUAN_TI("DAN_WEI_JI_BIE_QUN_ZHONG_ZI_ZHI_TUAN_TI", "群众自治团体"),
+	QI_TA("DAN_WEI_JI_BIE_QI_TA", "其他");
+
+	private String code;
+	private String chineseMeaning;
+
+	GovInstitutionLevel(String code, String chineseMeaning) {
+		this.code = code;
+		this.chineseMeaning = chineseMeaning;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public String getChineseMeaning() {
+		return chineseMeaning;
+	}
+
+	public static GovInstitutionLevel fromCode(String code) {
+		for (GovInstitutionLevel level : values()) {
+			if (level.code.equals(code)) {
+				return level;
+			}
+		}
+		return null;
+	}
+}

+ 145 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/LimitUploadFileType.java

@@ -0,0 +1,145 @@
+package com.crunii.micro.common.enums;
+
+/**
+ * @description 限制上传的文件类型
+ */
+public enum LimitUploadFileType {
+    /**
+     * JPEG  (jpg)
+     */
+    JPEG("JPEG", "FFD8FF"),
+
+    JPG("JPG", "FFD8FF"),
+
+    /**
+     * PNG
+     */
+    PNG("PNG", "89504E47"),
+
+    /**
+     * GIF
+     */
+    GIF("GIF", "47494638"),
+
+    /**
+     * TIFF (tif)
+     */
+    TIFF("TIF", "49492A00"),
+
+    /**
+     * Windows bitmap (bmp)
+     */
+    BMP("BMP", "424D"),
+
+    /**
+     * 16色位图(bmp)
+     */
+    BMP_16("BMP", "424D228C010000000000"),
+
+    /**
+     * 24位位图(bmp)
+     */
+    BMP_24("BMP", "424D8240090000000000"),
+
+    /**
+     * 256色位图(bmp)
+     */
+    BMP_256("BMP", "424D8E1B030000000000"),
+
+    /**
+     * XML
+     */
+    XML("XML", "3C3F786D6C"),
+
+    /**
+     * HTML (html)
+     */
+    HTML("HTML", "68746D6C3E"),
+
+    /**
+     * Microsoft Word/Excel 注意:word 和 excel的文件头一样
+     */
+    XLS("XLS", "D0CF11E0"),
+
+    /**
+     * Microsoft Word/Excel 注意:word 和 excel的文件头一样
+     */
+    DOC("DOC", "D0CF11E0"),
+
+    /**
+     * Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样
+     */
+    DOCX("DOCX", "504B0304"),
+
+    /**
+     * Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样 504B030414000600080000002100
+     */
+    XLSX("XLSX", "504B0304"),
+
+    /**
+     * Microsoft Word/Excel/PPT  2007以上版本文件 注意:word 和 excel的文件头一样 504B030414000600080000002100
+     */
+    PPT("PPT", "504B0304"),
+
+    /**
+     * Microsoft Word/Excel/PPT 2007以上版本文件 注意:word 和 excel的文件头一样 504B030414000600080000002100
+     */
+    PPTX("PPTX", "504B0304"),
+
+    /**
+     * Adobe Acrobat (pdf) 255044462D312E
+     */
+    PDF("PDF", "25504446"),
+    /**
+     * TXT
+     */
+    TXT("TXT", "25504446"),
+    /**
+     * RAR
+     */
+    RAR("RAR", "52617221"),
+    /**
+     * ZIP
+     */
+    ZIP("ZIP", "504B0304"),
+    /**
+     * DWG
+     */
+    DWG("DWG", "41433130"),
+    /**
+     * CAD
+     */
+    CAD("CAD", "41433130");
+
+    /**
+     * 后缀 大写字母
+     */
+    private final String suffix;
+
+    /**
+     * 魔数
+     */
+    private final String magicNumber;
+
+    LimitUploadFileType(String suffix, String magicNumber) {
+        this.suffix = suffix;
+        this.magicNumber = magicNumber;
+    }
+
+    public static LimitUploadFileType getBySuffix(String suffix) {
+        for (LimitUploadFileType fileType : values()) {
+            if (fileType.getSuffix().equals(suffix.toUpperCase())) {
+                return fileType;
+            }
+        }
+        throw new IllegalArgumentException("不支持的文件后缀 : " + suffix);
+    }
+
+    public String getSuffix() {
+        return this.suffix;
+    }
+
+    public String getMagicNumber() {
+        return this.magicNumber;
+    }
+}

+ 45 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/PolicyLevelEnums.java

@@ -0,0 +1,45 @@
+package com.crunii.micro.common.enums;
+
+/**
+ * @author lmlksy
+ * @date 2023/11/22 15:26
+ */
+public enum PolicyLevelEnums {
+
+    NATIONAL(0, "国家级"),
+    MUNICIPAL(1, "市级");
+
+    private final Integer code;
+    private final String description;
+
+    PolicyLevelEnums(Integer code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public static Integer getCodeByDescription(String description) {
+        for (PolicyLevelEnums level : values()) {
+            if (level.getDescription().equals(description)) {
+                return level.getCode();
+            }
+        }
+        return null;
+    }
+
+    public static String getDescriptionByCode(Integer code) {
+        for (PolicyLevelEnums level : values()) {
+            if (level.getCode().equals(code)) {
+                return level.getDescription();
+            }
+        }
+        return null;
+    }
+}

+ 56 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/PolicyTypeEnums.java

@@ -0,0 +1,56 @@
+package com.crunii.micro.common.enums;
+
+/**
+ * @author lmlksy
+ * @date 2023/11/22 15:26
+ */
+public enum PolicyTypeEnums {
+
+    INDUSTRIAL_SUPPORT(1, "产业扶持政策"),
+    SITE_INCUBATION(2, "场地孵化政策"),
+    ACHIEVEMENT_TRANSFORMATION(3, "成果转化政策"),
+    INNOVATION_ENTREPRENEURSHIP(4, "创新创业政策"),
+    GRADUATE_ATTRACTION(5, "高校毕业生招引政策"),
+    SKILL_TRAINING(6, "技能培训政策"),
+    FINANCIAL_LOAN(7, "金融贷款政策"),
+    RESEARCH_FUND(8, "科研经费政策"),
+    TALENT_STATION(9, "人才驿站政策"),
+    TALENT_INTRODUCTION(10, "人才引进政策"),
+    TAX_PREFERENCE(11, "税收优惠政策"),
+    INTELLECTUAL_PROPERTY(12, "知识产权政策"),
+    HOUSING_GUARANTEE(13, "住房保障政策");
+
+    private final Integer code;
+    private final String description;
+
+    PolicyTypeEnums(Integer code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public static Integer getCodeByDescription(String description) {
+        for (PolicyTypeEnums type : values()) {
+            if (type.getDescription().equals(description)) {
+                return type.getCode();
+            }
+        }
+        return null;
+    }
+
+    public static String getDescriptionByCode(Integer code) {
+        for (PolicyTypeEnums type : values()) {
+            if (type.getCode().equals(code)) {
+                return type.getDescription();
+            }
+        }
+        return null;
+    }
+}

+ 46 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/ResultEnums.java

@@ -0,0 +1,46 @@
+package com.crunii.micro.common.enums;
+
+import com.crunii.micro.common.exception.BusinessException;
+
+/**
+ * @author lmlksy
+ * @date 2023/11/22 15:26
+ */
+public enum ResultEnums {
+
+    SUCCESS(1, "成功"),
+    FAILURE(0, "失败");
+
+    private final Integer code;
+    private final String description;
+
+    ResultEnums(Integer code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public static String getByCode(Integer code) {
+        for (ResultEnums result : values()) {
+            if (result.getCode() != null && result.getCode().equals(code)) {
+                return result.getDescription();
+            }
+        }
+       return null;
+    }
+    public static Integer getByDescription(String description) {
+        for (ResultEnums status : values()) {
+            if (status.getDescription().equals(description)) {
+                return status.getCode();
+            }
+        }
+        return null;
+    }
+}

+ 24 - 0
micro-common/src/main/java/com/crunii/micro/common/enums/TaskTypeEnums.java

@@ -0,0 +1,24 @@
+package com.crunii.micro.common.enums;
+
+/**
+ * @author gongsongtao
+ * @date 2024/1/17 16:22
+ */
+public enum TaskTypeEnums {
+
+    MATCH("match", "赛事"),
+    HOT("hot", "热点"),
+    ACTIVITY("activity", "活动");
+
+    private final String code;
+    private final String desc;
+
+    TaskTypeEnums(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode() {
+        return code;
+    }
+}

+ 97 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/ExcelCellStyle.java

@@ -0,0 +1,97 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.excel;
+
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.util.StyleUtil;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.AbstractCellStyleStrategy;
+
+/**
+ * 单元自定义样式
+ * 
+ * @author 张波 create 2020年10月12日上午10:38:17
+ */
+public class ExcelCellStyle extends AbstractCellStyleStrategy {
+
+	/**
+	 * 操作行
+	 */
+	private List<Integer> columnIndexes;
+	/**
+	 * 操作列
+	 */
+	private List<Integer> rowIndexes;
+	/**
+	 * 颜色
+	 */
+	private Short colorIndex;
+
+	public ExcelCellStyle(List<Integer> rowIndexes, List<Integer> columnIndexes, Short colorIndex) {
+		this.rowIndexes = rowIndexes;
+		this.columnIndexes = columnIndexes;
+		this.colorIndex = colorIndex;
+	}
+
+	@Override
+	protected void initCellStyle(Workbook workbook) {
+
+	}
+
+	/**
+	 * 自定义头部样式
+	 *
+	 * @param cell
+	 * @param head
+	 * @param integer
+	 */
+	@Override
+	protected void setHeadCellStyle(Cell cell, Head head, Integer integer) {
+		// 获取workbook
+		Workbook workbook = cell.getSheet().getWorkbook();
+		// 获取样式实例
+		WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+		// 获取字体实例
+		WriteFont headWriteFont = new WriteFont();
+		// 设置字体样式
+		headWriteFont.setFontName("宋体");
+		// 设置字体大小
+		headWriteFont.setFontHeightInPoints((short) 12);
+		// 边框
+		headWriteFont.setBold(true);
+		// 设置背景颜色为灰色
+		headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+		if (CollectionUtils.isNotEmpty(columnIndexes) && columnIndexes.contains(cell.getColumnIndex()) && CollectionUtils.isNotEmpty(rowIndexes) && rowIndexes.contains(cell.getRowIndex()) && colorIndex != null) {
+			// 设置指定单元格字体自定义颜色
+			headWriteFont.setColor(colorIndex);
+		}
+		headWriteCellStyle.setWriteFont(headWriteFont);
+		// 获取样式实例
+		CellStyle cellStyle = StyleUtil.buildHeadCellStyle(workbook, headWriteCellStyle);
+		// 单元格设置样式
+		cell.setCellStyle(cellStyle);
+	}
+
+	/**
+	 * 自定义内容样式
+	 *
+	 * @param cell
+	 * @param head
+	 * @param integer
+	 */
+	@Override
+	protected void setContentCellStyle(Cell cell, Head head, Integer integer) {
+
+	}
+
+}

+ 60 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/ExcelListenerUtil.java

@@ -0,0 +1,60 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.excel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+
+/**
+ * @author 张波 create 2020年10月9日上午11:22:25
+ */
+
+public class ExcelListenerUtil extends AnalysisEventListener {
+
+	/**
+	 * 自定义用于暂时存储data。 可以通过实例获取该值
+	 */
+	private List<Object> datas = new ArrayList<>();
+
+	/**
+	 * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
+	 */
+	@Override
+	public void invoke(Object data, AnalysisContext context) {
+		// 数据存储到list,供批量处理,或后续自己业务逻辑处理。
+		datas.add(data);
+		// 根据业务自行 do something
+		doSomething();
+		/*
+		 * 如数据过大,可以进行定量分批处理 if(datas.size()<=100){ datas.add(object); }else { doSomething(); datas = new ArrayList<Object>(); }
+		 */
+
+	}
+
+	@Override
+	public void doAfterAllAnalysed(AnalysisContext context) {
+		/*
+		 * datas.clear(); 解析结束销毁不用的资源
+		 */
+
+	}
+
+	/**
+	 * 根据业务自行实现该方法
+	 */
+	private void doSomething() {
+	}
+
+	public List<Object> getDatas() {
+		return datas;
+	}
+
+	public void setDatas(List<Object> datas) {
+		this.datas = datas;
+	}
+
+}

+ 144 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/ExcelOperFieldConfig.java

@@ -0,0 +1,144 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * Excel导入OD接口字段对应配置表: 配置了接口数据包中的属性和PF系统业务过程表数据记录表中的对应关系,是业务受理的模板表,业务受 DTO
+ *
+ * @author 张波 create 2020年10月9日下午14:22:25
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel(description = "OD接口字段对应配置表  Excel导入导出: 配置了接口数据包中的属性和PF系统业务过程表数据记录表中的对应关系")
+public class ExcelOperFieldConfig {
+
+	/**
+	 * 接口包的路径 varchar(128)
+	 */
+	@ApiModelProperty(value = "接口包的路径")
+	@ExcelProperty(value = "接口包的路径", index = 0)
+	private String packet;
+
+	/**
+	 * 接口包的字段名称 varchar(64)
+	 */
+	@ApiModelProperty(value = "接口包节点名")
+	@ExcelProperty(value = "接口包节点名", index = 1)
+	private String field;
+
+	/**
+	 * 产品类型 1主产品,2附属产品
+	 */
+	@ApiModelProperty(value = "产品类型  1主产品,2附属产品")
+	@ExcelProperty(value = { "产品信息", "产品类型" }, index = 2)
+	private String prodType;
+
+	/**
+	 * 产品ID,公用的为0,(如果prodType为1写入主产品ID,如果prodType为2写入附属产品ID),实际long类型
+	 */
+	@ApiModelProperty(value = "产品ID,公用的为0")
+	@ExcelProperty(value = { "产品信息", "产品ID" }, index = 3)
+	private String prodId;
+
+	/**
+	 * 产品编码
+	 */
+	@ApiModelProperty(value = "产品编码")
+	@ExcelProperty(value = { "产品信息", "产品编码" }, index = 4)
+	private String prodCode;
+
+	/**
+	 * 产品名称
+	 */
+	@ApiModelProperty(value = "产品名称")
+	@ExcelProperty(value = { "产品信息", "产品名称" }, index = 5)
+	private String prodName;
+
+	// /**
+	// * 附属产品ID int(11)
+	// */
+	// @ApiModelProperty(value = "附属产品ID")
+	// private long appProdId;
+
+	/**
+	 * 数据域类型[2:纵表,1:横表]【字典ID:7000020】 tinyInt(4)
+	 */
+	@ApiModelProperty(value = "数据域类型[2:纵表,1:横表]【字典ID:7000020】")
+	@ExcelProperty(value = "数据域类型", index = 6)
+	private String domainType;
+
+	/**
+	 * 数据域表名 varchar(32)
+	 */
+	@ApiModelProperty(value = "数据域表名")
+	@ExcelProperty(value = { "横表填写", "数据域表名" }, index = 7)
+	private String domain;
+
+	/**
+	 * 横表的字段名 varchar(64)
+	 */
+	@ApiModelProperty(value = "横表的字段名")
+	@ExcelProperty(value = { "横表填写", "表字段名" }, index = 8)
+	private String property;
+
+	/**
+	 * 数据域表名 varchar(32)
+	 */
+	@ApiModelProperty(value = "数据域表名")
+	@ExcelProperty(value = { "纵表填写", "数据域表名" }, index = 9)
+	private String domain2;
+
+	/**
+	 * 对应纵表的K表字段 varchar(64)
+	 */
+	@ApiModelProperty(value = "对应纵表的K表字段")
+	@ExcelProperty(value = { "纵表填写", "KEY字段名" }, index = 10)
+	private String keyProperty;
+
+	/**
+	 * 对应纵表的KEY表字段值 varchar(64)
+	 */
+	@ApiModelProperty(value = "对应纵表的KEY表字段值")
+	@ExcelProperty(value = { "纵表填写", "KEY字段值" }, index = 11)
+	private String keyValue;
+
+	/**
+	 * 纵表的字段名 varchar(64)
+	 */
+	@ApiModelProperty(value = "VALUE字段名")
+	@ExcelProperty(value = { "纵表填写", "VALUE字段名" }, index = 12)
+	private String property2;
+
+	/**
+	 * 纵表的老值对应表字段 varchar(64)
+	 */
+	@ApiModelProperty(value = "OLD VALUE字段名")
+	@ExcelProperty(value = { "纵表填写", "OLD VALUE字段名" }, index = 13)
+	private String oldProperty;
+
+	@ApiModelProperty(value = "0 失效 1、2、3.。。。  都为有效")
+	@ExcelProperty(value = "状态", index = 14)
+	private String stateName;
+
+	/**
+	 * 状态优先级,实际int类型
+	 */
+	@ApiModelProperty(value = "0 失效1 优先级从1开始计,如1、2、3.。。。数字越大,优先级越高")
+	@ExcelProperty(value = "优先级", index = 15)
+	private String state;
+
+	@ApiModelProperty(value = "导入结果")
+	@ExcelProperty(value = "导入结果 (导入前请清空)", index = 16)
+	private String importResult;
+
+}

+ 49 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/ExcelSheetWriteHandler.java

@@ -0,0 +1,49 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.excel;
+
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.util.CellRangeAddressList;
+
+import com.alibaba.excel.write.handler.SheetWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+
+/**
+ * OD解析配置导出模板,设置下拉框选项
+ * @author 张波 create 2020年10月12日上午9:46:27
+ */
+public class ExcelSheetWriteHandler implements SheetWriteHandler {
+	@Override
+	public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+
+	}
+
+	@Override
+	public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+		selectList(writeWorkbookHolder,writeSheetHolder,2,2,new String[] { "主产品", "附属产品" });
+		selectList(writeWorkbookHolder,writeSheetHolder,6,6,new String[] { "横表", "纵表" });
+		selectList(writeWorkbookHolder,writeSheetHolder,14,14,new String[] { "有效", "无效" });
+	}
+	
+	/**
+	 * 设置Excel下拉框选项
+	 * @param writeWorkbookHolder
+	 * @param writeSheetHolder
+	 * @param firstCol
+	 * @param lastCol
+	 * @param strings
+	 */
+	public static void selectList(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder,int firstCol,int lastCol,String[] strings ){
+		// 设置下拉
+		CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 1000, firstCol, lastCol);
+		DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
+		DataValidationConstraint constraint = helper.createExplicitListConstraint(strings);
+		DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
+		writeSheetHolder.getSheet().addValidationData(dataValidation);	
+	}
+	
+}

+ 288 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/ExcelUtil.java

@@ -0,0 +1,288 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.excel;
+
+import cn.hutool.core.lang.func.Func;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import com.alibaba.nacos.common.utils.StringUtils;
+import com.crunii.micro.common.dto.NoModelWriteData;
+import com.crunii.micro.common.exception.BusinessException;
+import com.crunii.micro.common.utils.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.URLEncoder;
+import java.util.*;
+
+/**
+ * @author 张波 create 2020年10月9日上午10:20:25
+ */
+@Slf4j
+public class ExcelUtil {
+
+	public static boolean isExcel2003(String filePath)
+    {
+        return filePath.matches("^.+\\.(?i)(xls)$");
+    }
+
+    public static boolean isExcel2007(String filePath)
+    {
+        return filePath.matches("^.+\\.(?i)(xlsx)$");
+    }
+
+	/**
+	 * 读取单个sheet的excel文件
+	 *
+	 * @param excel 文件
+	 * @param t 实体类型
+	 * @param headRowNumber 头行数
+	 * @return
+	 * @throws Exception
+	 */
+	public static <T> List<T> readSingleExcel(MultipartFile excel, T t, int headRowNumber) throws Exception {
+		return EasyExcel.read(excel.getInputStream(), t.getClass(), new ExcelListenerUtil()).sheet().headRowNumber(headRowNumber).doReadSync();
+	}
+
+	/**
+	 * 导出文件 导出模板时,tList传一个空list即可
+	 * 专门用于OD解析配置导出普通模板
+	 * @param tList 数据集
+	 * @param tClass 数据类型
+	 * @param <T>
+	 * @throws IOException
+	 */
+	public static <T> void writeSingleExcel(String fileName, String sheetName, List<T> tList, Class<T> tClass) throws IOException {
+		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+		try (ServletOutputStream outputStream = response.getOutputStream()) {
+
+			setResponse(fileName, response);
+			EasyExcel.write(outputStream, tClass).autoCloseStream(Boolean.FALSE).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).registerWriteHandler(new ExcelSheetWriteHandler()).sheet(sheetName).doWrite(tList);
+		} catch (Exception e) {
+			errorWrite(response, e);
+		}
+	}
+
+	
+
+	/**
+	 * 导出文件 导出模板时,tList传一个空list即可
+	 * 通用
+	 * @param tList 数据集
+	 * @param tClass 数据类型
+	 * @param <T>
+	 * @throws IOException
+	 */
+	public static <T> void writeSingleCommonExcel(String fileName, String sheetName, List<T> tList, Class<T> tClass) throws IOException {
+		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+		try (ServletOutputStream outputStream = response.getOutputStream()) {
+			setResponse(fileName, response);
+			EasyExcel.write(outputStream, tClass).autoCloseStream(Boolean.FALSE).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(tList);
+		} catch (Exception e) {
+			errorWrite(response, e);
+		}
+	}
+
+	/**
+	 * @description: 导出list<map<String,Object>>类型的数据
+	 * @param
+	 * @return:
+	 * @author liuchuan
+	 * @date: 2022/11/17 21:43
+	 */
+	public static void noModleWrite(NoModelWriteData data) {
+		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+		try {//            response.setContentType("application/vnd.ms-excel");
+			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+			response.setCharacterEncoding("utf-8");
+			// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
+			String fileName = URLEncoder.encode(data.getFileName(), "UTF-8");
+			response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+			// 这里需要设置不关闭流
+			EasyExcel.write(response.getOutputStream()).head(head(data.getHeadMap())).sheet(data.getFileName()).
+					doWrite(dataList(data.getDataList(), data.getDataStrMap()));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new RuntimeException("导出Excel异常");
+
+		}
+	}
+
+	//设置导出的数据内容
+	private static List<List<Object>> dataList(List<Map<String, Object>> dataList, String[] dataStrMap) {
+		List<List<Object>> list = new ArrayList<List<Object>>();
+		for (Map<String, Object> map : dataList) {
+			List<Object> data = new ArrayList<Object>();
+			for (int i = 0; i < dataStrMap.length; i++) {
+				data.add(map.get(dataStrMap[i]));
+			}
+			list.add(data);
+		}
+		return list;
+	}
+
+	//设置表头
+	private static List<List<String>> head(String[] headMap) {
+		List<List<String>> list = new ArrayList<List<String>>();
+
+		for (String head : headMap) {
+			List<String> headList = new ArrayList<String>();
+			headList.add(head);
+			list.add(headList);
+		}
+		return list;
+	}
+
+	/**
+	 * 忽略部分导出字段方法
+	 * @param fileName 文件名称
+	 * @param sheetName sheet名称
+	 * @param dataList 需要导出的数据
+	 * @param clazz 类
+	 * @param ignoreParam 忽略的字段
+	 * @param <T>
+	 * @throws IOException
+	 */
+	public static <T> void export(String fileName, String sheetName, List<T> dataList, Class<T> clazz, Set<String> ignoreParam) throws IOException {
+		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+		try (ServletOutputStream outputStream = response.getOutputStream()) {
+			setResponse(fileName, response);
+			if (ignoreParam.size() > 0) {
+				EasyExcel.write(outputStream, clazz).excludeColumnFiledNames(ignoreParam).sheet(sheetName).doWrite(dataList);
+			} else {
+				EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(dataList);
+			}
+		}catch (Exception e) {
+			errorWrite(response, e);
+		}
+	}
+
+	/**
+	 * 导出错误
+	 *
+	 * @param response
+	 * @param e
+	 * @throws IOException
+	 */
+	private static void errorWrite(HttpServletResponse response, Exception e) throws IOException {
+		// 重置response
+		response.reset();
+		log.error(e.getMessage(), e);
+		response.setContentType("application/json");
+		response.setCharacterEncoding("utf-8");
+		response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
+		// response.getWriter().println(JsonUtil.getJsonString(BaseResponse.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "导出失败")));
+	}
+
+	/**
+	 * 设置导出信息
+	 *
+	 * @param fileName
+	 * @param response
+	 * @throws UnsupportedEncodingException
+	 */
+	private static void setResponse(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
+		// 重置response
+		response.reset();
+		// response.setContentType("application/octet-stream;charset=utf-8");
+		response.setContentType("application/vnd.ms-excel");
+		response.setCharacterEncoding("utf-8");
+		// 这里URLEncoder.encode可以防止中文乱码
+		fileName = URLEncoder.encode(fileName + DateUtil.format(new Date(), "yyyy-MM-dd_HH_mm_ss") + ExcelTypeEnum.XLSX.getValue(), "UTF-8");
+		response.setHeader("Content-disposition", "attachment;filename=" + fileName);
+	}
+
+	/**
+     * 校验模板是否正确
+     * tClass是传入对应的excel表头实体
+     * rowNo 头行号,如果复杂头,取最下层头行号
+     * @param file
+     */
+    public static <T> void checkModel(MultipartFile file,Class<T> tClass,Integer rowNo) {
+        // 校验模板是否正确
+        List<String> columnNames = getColumnNames(file,0,rowNo);
+        Field[] declaredFields = tClass.getDeclaredFields();
+        // 判断列数是否等于对象变量数量
+        if(columnNames.size() != declaredFields.length){
+            throw new BusinessException("上传模板错误!");
+        }
+        for (int i = 0,fLength = declaredFields.length ; i <fLength; i++) {
+            Field declaredField = declaredFields[i];
+            declaredField.setAccessible(true);
+            ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
+            String[] values = annotation.value();
+            String columnValue = values[values.length-1];
+            if(!columnValue.equals(columnNames.get(i))){
+            	throw new BusinessException("上传模板错误!");
+            }
+        }
+    }
+
+    /**
+     * 获取文件头列名称
+     *
+     * @param file 文件
+     * @param sheetNo sheet编号
+     * @param rowNo 头行号,如果复杂头,取最下层头行号
+     * @return
+     */
+    public static List<String> getColumnNames(MultipartFile file,Integer sheetNo,Integer rowNo) {
+        List<String> columnNames = new ArrayList<>(28);
+        Workbook workBook = getWorkBook(file);
+        Iterator<Cell> iterator = workBook.getSheetAt(sheetNo).getRow(rowNo).iterator();
+        while (true){
+            if(iterator.hasNext()){
+            	Cell cell = iterator.next();
+            	//设置单元格类型
+            	cell.setCellType(CellType.STRING);
+            	//获取单元格数据
+                String columnName = cell.getStringCellValue();
+                columnNames.add(columnName);
+            } else {
+                break;
+            }
+        }
+        return columnNames;
+    }
+
+    /**
+     * 得到Workbook对象
+     *
+     * @param file
+     * @return
+     */
+    public static Workbook getWorkBook(MultipartFile file) {
+        Workbook workbook = null;
+        try (InputStream inputStream = file.getInputStream()) {
+            String filename = file.getOriginalFilename();
+            if(isExcel2003(filename)){
+                workbook = new HSSFWorkbook(inputStream);
+            }else if(isExcel2007(filename)){
+                workbook = new XSSFWorkbook(inputStream);
+            }else {
+                throw new BusinessException("文件类型错误,请上传Excel文件!");
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+        }
+        return workbook;
+    }
+
+}

+ 358 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/excel/ExcelExportUtil.java

@@ -0,0 +1,358 @@
+package com.crunii.micro.common.excel.excel;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.crunii.micro.common.excel.excel.annotation.ExcelField;
+import com.crunii.micro.common.excel.excel.annotation.ExcelSheet;
+import com.crunii.micro.common.excel.excel.util.FieldReflectionUtil;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Excel导出工具
+ *
+ * @author: dumingchun
+ * @date: 2018年12月26日 下午2:58:14
+ * @version:
+ */
+public class ExcelExportUtil {
+	private static Logger logger = LoggerFactory.getLogger(ExcelExportUtil.class);
+
+	/**
+	 * 获取MINE文件
+	 *
+	 * @return
+	 * @throws Exception
+	 */
+	public static String getMimeMappings(String fileName) {
+		Map<String, String> mimes = new HashMap<String, String>();
+
+		mimes.put(".txt", "text/plain");
+		mimes.put(".jpg", "image/JPEG");
+		mimes.put(".png", "image/PNG");
+		mimes.put(".gif", "image/GIF");
+		mimes.put(".doc", "application/msword");
+		mimes.put(".xls", "application/vnd.ms-excel");
+		mimes.put(".xlsx", "application/vnd.ms-excel");
+		mimes.put(".pdf", "application/pdf");
+		mimes.put(".zip", "application/zip");
+		mimes.put(".rar", "application/octet-stream");
+		mimes.put(".apk", "application/vnd.android.package-archive");
+		mimes.put("*", "application/octet-stream");
+
+		int pos = fileName.lastIndexOf(".");
+		String suffix = pos >= 0 ? fileName.substring(pos) : "";
+		suffix = suffix.toLowerCase();
+
+		String contentType = mimes.get(suffix);
+		if (contentType == null) {
+			contentType = mimes.get("*");
+		}
+		return contentType;
+	}
+
+	/**
+	 * 导出Excel对象
+	 *
+	 * @param sheetDataListArr Excel数据
+	 * @return Workbook
+	 */
+	public static Workbook exportWorkbook(List<?>... sheetDataListArr) {
+
+		// data array valid
+		if (sheetDataListArr == null || sheetDataListArr.length == 0) {
+			throw new RuntimeException(">>>>>>>>>>> excel error, data array can not be empty.");
+		}
+
+		// book (HSSFWorkbook=2003/xls、XSSFWorkbook=2007/xlsx)
+		Workbook workbook = new HSSFWorkbook();
+
+		// sheet
+		for (List<?> dataList : sheetDataListArr) {
+			makeSheet(workbook, dataList);
+		}
+
+		return workbook;
+	}
+
+	private static void makeSheet(Workbook workbook, List<?> sheetDataList) {
+		// data
+		if (sheetDataList == null || sheetDataList.size() == 0) {
+			throw new RuntimeException(">>>>>>>>>>> excel error, data can not be empty.");
+		}
+
+		// sheet
+		Class<?> sheetClass = sheetDataList.get(0).getClass();
+		ExcelSheet excelSheet = sheetClass.getAnnotation(ExcelSheet.class);
+
+		String sheetName = sheetDataList.get(0).getClass().getSimpleName();
+		int headColorIndex = -1;
+		int headFontColorIndex = -1;
+		if (excelSheet != null) {
+			if (excelSheet.name() != null && excelSheet.name().trim().length() > 0) {
+				sheetName = excelSheet.name().trim();
+			}
+			headColorIndex = excelSheet.headFontColor();
+			headFontColorIndex = excelSheet.headFontColor();
+		}
+
+		Sheet existSheet = workbook.getSheet(sheetName);
+		if (existSheet != null) {
+			for (int i = 2; i <= 1000; i++) {
+				String newSheetName = sheetName.concat(String.valueOf(i)); // avoid sheetName repetition
+				existSheet = workbook.getSheet(newSheetName);
+				if (existSheet == null) {
+					sheetName = newSheetName;
+					break;
+				} else {
+					continue;
+				}
+			}
+		}
+
+		Sheet sheet = workbook.createSheet(sheetName);
+
+		// sheet field
+		List<Field> fields = new ArrayList<Field>();
+		if (sheetClass.getDeclaredFields() != null && sheetClass.getDeclaredFields().length > 0) {
+			for (Field field : sheetClass.getDeclaredFields()) {
+				if (Modifier.isStatic(field.getModifiers())) {
+					continue;
+				}
+				fields.add(field);
+			}
+		}
+
+		if (fields == null || fields.size() == 0) {
+			throw new RuntimeException(">>>>>>>>>>> excel error, data field can not be empty.");
+		}
+
+		// sheet header row
+		CellStyle[] fieldDataStyleArr = new CellStyle[fields.size()];
+		int[] fieldWidthArr = new int[fields.size()];
+		Row headRow = sheet.createRow(0);
+		for (int i = 0; i < fields.size(); i++) {
+
+			// field
+			Field field = fields.get(i);
+			ExcelField excelField = field.getAnnotation(ExcelField.class);
+
+			String fieldName = field.getName();
+			int fieldWidth = 0;
+			HorizontalAlignment align = null;
+			if (excelField != null) {
+				if (excelField.name() != null && excelField.name().trim().length() > 0) {
+					fieldName = excelField.name().trim();
+				}
+				fieldWidth = excelField.width();
+				align = excelField.align();
+			}
+
+			// field width
+			fieldWidthArr[i] = fieldWidth;
+
+			// head-style、field-data-style
+			CellStyle fieldDataStyle = workbook.createCellStyle();
+			if (align != null) {
+				fieldDataStyle.setAlignment(align);
+			}
+			fieldDataStyleArr[i] = fieldDataStyle;
+
+			CellStyle headStyle = workbook.createCellStyle();
+			headStyle.cloneStyleFrom(fieldDataStyle);
+			if (headColorIndex > -1) {
+				headStyle.setFillForegroundColor((short) headColorIndex);
+				headStyle.setFillBackgroundColor((short) headColorIndex);
+				headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+			}
+
+			if (headFontColorIndex > -1) {
+				Font font = workbook.createFont();
+				font.setColor((short) headFontColorIndex);
+				headStyle.setFont(font);
+			}
+
+			// head-field data
+			Cell cellX = headRow.createCell(i, CellType.STRING);
+			cellX.setCellStyle(headStyle);
+			cellX.setCellValue(String.valueOf(fieldName));
+		}
+
+		// sheet data rows
+		for (int dataIndex = 0; dataIndex < sheetDataList.size(); dataIndex++) {
+			int rowIndex = dataIndex + 1;
+			Object rowData = sheetDataList.get(dataIndex);
+
+			Row rowX = sheet.createRow(rowIndex);
+
+			for (int i = 0; i < fields.size(); i++) {
+				Field field = fields.get(i);
+				try {
+					field.setAccessible(true);
+					Object fieldValue = field.get(rowData);
+
+					String fieldValueString = FieldReflectionUtil.formatValue(field, fieldValue);
+
+					Cell cellX = rowX.createCell(i, CellType.STRING);
+					cellX.setCellValue(fieldValueString);
+					cellX.setCellStyle(fieldDataStyleArr[i]);
+				} catch (IllegalAccessException e) {
+					logger.error(e.getMessage(), e);
+					throw new RuntimeException(e);
+				}
+			}
+		}
+
+		// sheet finally
+		for (int i = 0; i < fields.size(); i++) {
+			int fieldWidth = fieldWidthArr[i];
+			if (fieldWidth > 0) {
+				sheet.setColumnWidth(i, fieldWidth);
+			} else {
+				sheet.autoSizeColumn((short) i);
+			}
+		}
+	}
+
+	/**
+	 * 导出Excel文件到磁盘
+	 *
+	 * @param filePath
+	 * @param sheetDataListArr 数据,可变参数,如多个参数则代表导出多张Sheet
+	 */
+	public static void exportToFile(String filePath, List<?>... sheetDataListArr) {
+		// workbook
+		Workbook workbook = exportWorkbook(sheetDataListArr);
+
+		FileOutputStream fileOutputStream = null;
+		try {
+			File exFile = new File(filePath);
+			if (!exFile.getParentFile().exists()) {
+				exFile.getParentFile().mkdirs();
+			}
+
+			// workbook 2 FileOutputStream
+			fileOutputStream = new FileOutputStream(filePath);
+			workbook.write(fileOutputStream);
+
+			// flush
+			fileOutputStream.flush();
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			throw new RuntimeException(e);
+		} finally {
+			try {
+				if (fileOutputStream != null) {
+					fileOutputStream.close();
+				}
+			} catch (Exception e) {
+				logger.error(e.getMessage(), e);
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	/**
+	 * 导出Excel文件到输出流
+	 *
+	 * @param filePath
+	 * @param sheetDataListArr 数据,可变参数,如多个参数则代表导出多张Sheet
+	 */
+	public static void exportToOutputStream(OutputStream fileOutputStream, List<?>... sheetDataListArr) {
+		// workbook
+		Workbook workbook = exportWorkbook(sheetDataListArr);
+
+		try {
+			workbook.write(fileOutputStream);
+
+			// flush
+			fileOutputStream.flush();
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			throw new RuntimeException(e);
+		} finally {
+			try {
+				if (fileOutputStream != null) {
+					fileOutputStream.close();
+				}
+			} catch (Exception e) {
+				logger.error(e.getMessage(), e);
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	/**
+	 * 导出Excel文件到输出流
+	 *
+	 * @param filePath
+	 * @param sheetDataListArr 数据,可变参数,如多个参数则代表导出多张Sheet
+	 */
+	public static void exportToOutputStream(HttpServletResponse response,String fileName, List<?>... sheetDataListArr) throws IOException {
+		// workbook
+		Workbook workbook = exportWorkbook(sheetDataListArr);
+		// 设置响应头
+		response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+		response.setHeader("Content-Disposition", "attachment; filename=" + fileName+ ".xls");
+		// 写入响应流
+		workbook.write(response.getOutputStream());
+		workbook.close();
+	}
+
+	/**
+	 * 导出Excel字节数据
+	 *
+	 * @param sheetDataListArr
+	 * @return byte[]
+	 */
+	public static byte[] exportToBytes(List<?>... sheetDataListArr) {
+		// workbook
+		Workbook workbook = exportWorkbook(sheetDataListArr);
+
+		ByteArrayOutputStream byteArrayOutputStream = null;
+		byte[] result = null;
+		try {
+			// workbook 2 ByteArrayOutputStream
+			byteArrayOutputStream = new ByteArrayOutputStream();
+			workbook.write(byteArrayOutputStream);
+
+			// flush
+			byteArrayOutputStream.flush();
+
+			result = byteArrayOutputStream.toByteArray();
+			return result;
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			throw new RuntimeException(e);
+		} finally {
+			try {
+				if (byteArrayOutputStream != null) {
+					byteArrayOutputStream.close();
+				}
+			} catch (Exception e) {
+				logger.error(e.getMessage(), e);
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+}

+ 174 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/excel/ExcelImportUtil.java

@@ -0,0 +1,174 @@
+package com.crunii.micro.common.excel.excel;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.crunii.micro.common.excel.excel.annotation.ExcelSheet;
+import com.crunii.micro.common.excel.excel.util.FieldReflectionUtil;
+
+/**
+ * 
+ * Excel导入工具
+ * @author: dumingchun
+ * @date:   2018年12月26日 下午2:58:52
+ * @version: 
+ *
+ */
+public class ExcelImportUtil {
+    private static Logger logger = LoggerFactory.getLogger(ExcelImportUtil.class);
+
+    /**
+     * 从Workbook导入Excel文件,并封装成对象
+     *
+     * @param workbook
+     * @param sheetClass
+     * @return List<Object>
+     */
+    public static List<Object> importExcel(Workbook workbook, Class<?> sheetClass) {
+        List<Object> sheetDataList = importSheet(workbook, sheetClass);
+        return sheetDataList;
+    }
+
+    public static List<Object> importSheet(Workbook workbook, Class<?> sheetClass) {
+        try {
+            // sheet
+            ExcelSheet excelSheet = sheetClass.getAnnotation(ExcelSheet.class);
+            String sheetName = (excelSheet!=null && excelSheet.name()!=null && excelSheet.name().trim().length()>0)?excelSheet.name().trim():sheetClass.getSimpleName();
+
+            // sheet field
+            List<Field> fields = new ArrayList<Field>();
+            if (sheetClass.getDeclaredFields()!=null && sheetClass.getDeclaredFields().length>0) {
+                for (Field field: sheetClass.getDeclaredFields()) {
+                    if (Modifier.isStatic(field.getModifiers())) {
+                        continue;
+                    }
+                    fields.add(field);
+                }
+            }
+
+            if (fields==null || fields.size()==0) {
+                throw new RuntimeException(">>>>>>>>>>> excel error, data field can not be empty.");
+            }
+
+            // sheet data
+            Sheet sheet = workbook.getSheet(sheetName);
+            if (sheet == null) {
+                return null;
+            }
+
+            Iterator<Row> sheetIterator = sheet.rowIterator();
+            int rowIndex = 0;
+            List<Object> dataList = new ArrayList<Object>();
+            while (sheetIterator.hasNext()) {
+                Row rowX = sheetIterator.next();
+                if (rowIndex > 0) {
+                    Object rowObj = sheetClass.newInstance();
+                    for (int i = 0; i < fields.size(); i++) {
+
+                        // cell
+                        Cell cell = rowX.getCell(i);
+                        if (cell == null) {
+                            continue;
+                        }
+
+                        // call val str
+                        cell.setCellType(CellType.STRING);
+                        String fieldValueStr = cell.getStringCellValue();       // cell.getCellTypeEnum()
+
+                        // java val
+                        Field field = fields.get(i);
+                        Object fieldValue = FieldReflectionUtil.parseValue(field, fieldValueStr);
+                        if (fieldValue == null) {
+                            continue;
+                        }
+
+                        // fill val
+                        field.setAccessible(true);
+                        field.set(rowObj, fieldValue);
+                    }
+                    dataList.add(rowObj);
+                }
+                rowIndex++;
+            }
+            return dataList;
+        } catch (IllegalAccessException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        } catch (InstantiationException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 导入Excel文件,并封装成对象
+     *
+     * @param excelFile
+     * @param sheetClass
+     * @return List<Object>
+     */
+    public static List<Object> importExcel(File excelFile, Class<?> sheetClass) {
+        try {
+            Workbook workbook = WorkbookFactory.create(excelFile);
+            List<Object> dataList = importExcel(workbook, sheetClass);
+            return dataList;
+        } catch (IOException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        } catch (InvalidFormatException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 从文件路径导入Excel文件,并封装成对象
+     *
+     * @param filePath
+     * @param sheetClass
+     * @return List<Object>
+     */
+    public static List<Object> importExcel(String filePath, Class<?> sheetClass) {
+        File excelFile = new File(filePath);
+        List<Object> dataList = importExcel(excelFile, sheetClass);
+        return dataList;
+    }
+
+    /**
+     * 导入Excel数据流,并封装成对象
+     *
+     * @param inputStream
+     * @param sheetClass
+     * @return List<Object>
+     */
+    public static List<Object> importExcel(InputStream inputStream, Class<?> sheetClass) {
+        try {
+            Workbook workbook = WorkbookFactory.create(inputStream);
+            List<Object> dataList = importExcel(workbook, sheetClass);
+            return dataList;
+        } catch (IOException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        } catch (InvalidFormatException e) {
+            logger.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 50 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/excel/annotation/ExcelField.java

@@ -0,0 +1,50 @@
+package com.crunii.micro.common.excel.excel.annotation;
+
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+
+import java.lang.annotation.*;
+
+/**
+ * 列属性信息
+ *
+ *      支持Java对象数据类型:Boolean、String、Short、Integer、Long、Float、Double、Date
+ *      支持Excel的Cell类型为:String
+ * @author: dumingchun
+ * @date:   2018年12月26日 下午2:57:34
+ * @version: 
+ *
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelField {
+
+    /**
+     * 列名称
+     *
+     * @return String
+     */
+    String name() default "";
+
+    /**
+     * 列宽 (大于0时生效; 如果不指定列宽,将会自适应调整宽度;)
+     *
+     * @return int
+     */
+    int width() default 0;
+
+    /**
+     * 水平对齐方式
+     *
+     * @return HorizontalAlignment
+     */
+    HorizontalAlignment align() default HorizontalAlignment.LEFT;
+
+    /**
+     * 时间格式化,日期类型时生效
+     *
+     * @return String
+     */
+    String dateformat() default "yyyy-MM-dd HH:mm:ss";
+
+}

+ 31 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/excel/annotation/ExcelSheet.java

@@ -0,0 +1,31 @@
+package com.crunii.micro.common.excel.excel.annotation;
+
+import org.apache.poi.hssf.util.HSSFColor;
+
+import java.lang.annotation.*;
+
+/**
+ * 表信息
+ *
+ * @author: dumingchun
+ * @date: 2018年12月26日 下午2:57:53
+ * @version:
+ */
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelSheet {
+
+	/**
+	 * 表名称
+	 *
+	 * @return String
+	 */
+	String name() default "";
+
+	/**
+	 * @return 白色
+	 */
+	short headFontColor() default 9;
+
+}

+ 179 - 0
micro-common/src/main/java/com/crunii/micro/common/excel/excel/util/FieldReflectionUtil.java

@@ -0,0 +1,179 @@
+package com.crunii.micro.common.excel.excel.util;
+
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.crunii.micro.common.excel.excel.annotation.ExcelField;
+
+
+public final class FieldReflectionUtil {
+
+	private FieldReflectionUtil(){}
+
+	public static Byte parseByte(String value) {
+		try {
+			value = value.replaceAll(" ", "");
+			return Byte.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseByte but input illegal input=" + value, e);
+		}
+	}
+
+	public static Boolean parseBoolean(String value) {
+		value = value.replaceAll(" ", "");
+		if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
+			return Boolean.TRUE;
+		} else if (Boolean.FALSE.toString().equalsIgnoreCase(value)) {
+			return Boolean.FALSE;
+		} else {
+			throw new RuntimeException("parseBoolean but input illegal input=" + value);
+		}
+	}
+
+	public static Integer parseInt(String value) {
+		try {	
+			value = value.replaceAll(" ", "");
+			return Integer.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseInt but input illegal input=" + value, e);
+		}
+	}
+
+	public static Short parseShort(String value) {
+		try {
+			value = value.replaceAll(" ", "");
+			return Short.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseShort but input illegal input=" + value, e);
+		}
+	}
+
+	public static Long parseLong(String value) {
+		try {
+			value = value.replaceAll(" ", "");
+			return Long.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseLong but input illegal input=" + value, e);
+		}
+	}
+
+	public static Float parseFloat(String value) {
+		try {
+			value = value.replaceAll(" ", "");
+			return Float.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseFloat but input illegal input=" + value, e);
+		}
+	}
+
+	public static Double parseDouble(String value) {
+		try {
+			value = value.replaceAll(" ", "");
+			return Double.valueOf(value);
+		} catch(NumberFormatException e) {
+			throw new RuntimeException("parseDouble but input illegal input=" + value, e);
+		}
+	}
+
+	public static Date parseDate(String value, ExcelField excelField) {
+		try {
+			String datePattern = "yyyy-MM-dd HH:mm:ss";
+			if (excelField != null) {
+				datePattern = excelField.dateformat();
+			}
+			SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
+			return dateFormat.parse(value);
+		} catch(ParseException e) {
+			throw new RuntimeException("parseDate but input illegal input=" + value, e);
+		}
+	}
+
+	/**
+	 * 参数解析 (支持:Byte、Boolean、String、Short、Integer、Long、Float、Double、Date)
+	 *
+	 * @param field
+	 * @param value
+	 * @return Object
+	 */
+	public static Object parseValue(Field field, String value) {
+		Class<?> fieldType = field.getType();
+
+		ExcelField excelField = field.getAnnotation(ExcelField.class);
+		if(value==null || value.trim().length()==0)
+			return null;
+		value = value.trim();
+
+		/*if (Byte.class.equals(fieldType) || Byte.TYPE.equals(fieldType)) {
+			return parseByte(value);
+		} else */if (Boolean.class.equals(fieldType) || Boolean.TYPE.equals(fieldType)) {
+			return parseBoolean(value);
+		}/* else if (Character.class.equals(fieldType) || Character.TYPE.equals(fieldType)) {
+			 return value.toCharArray()[0];
+		}*/ else if (String.class.equals(fieldType)) {
+			return value;
+		} else if (Short.class.equals(fieldType) || Short.TYPE.equals(fieldType)) {
+			 return parseShort(value);
+		} else if (Integer.class.equals(fieldType) || Integer.TYPE.equals(fieldType)) {
+			return parseInt(value);
+		} else if (Long.class.equals(fieldType) || Long.TYPE.equals(fieldType)) {
+			return parseLong(value);
+		} else if (Float.class.equals(fieldType) || Float.TYPE.equals(fieldType)) {
+			return parseFloat(value);
+		} else if (Double.class.equals(fieldType) || Double.TYPE.equals(fieldType)) {
+			return parseDouble(value);
+		} else if (Date.class.equals(fieldType)) {
+			 return parseDate(value, excelField);
+
+		} else {
+			throw new RuntimeException("request illeagal type, type must be Integer not int Long not long etc, type=" + fieldType);
+		}
+	}
+
+	/**
+	 * 参数格式化为String
+	 *
+	 * @param field
+	 * @param value
+	 * @return String
+	 */
+	public static String formatValue(Field field, Object value) {
+		Class<?> fieldType = field.getType();
+
+		ExcelField excelField = field.getAnnotation(ExcelField.class);
+		if(value==null) {
+			return null;
+		}
+
+		if (Boolean.class.equals(fieldType) || Boolean.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (String.class.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (Short.class.equals(fieldType) || Short.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (Integer.class.equals(fieldType) || Integer.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (Long.class.equals(fieldType) || Long.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (Float.class.equals(fieldType) || Float.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (Double.class.equals(fieldType) || Double.TYPE.equals(fieldType)) {
+			return String.valueOf(value);
+		} else if (BigDecimal.class.equals(fieldType)) {
+			return String.valueOf(value);
+		}  else if (Date.class.equals(fieldType)) {
+			String datePattern = "yyyy-MM-dd HH:mm:ss";
+			if (excelField != null && excelField.dateformat()!=null) {
+				datePattern = excelField.dateformat();
+			}
+			SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
+			return dateFormat.format(value);
+		} else {
+			throw new RuntimeException("request illeagal type, type must be Integer not int Long not long etc, type=" + fieldType);
+		}
+	}
+
+}

+ 42 - 0
micro-common/src/main/java/com/crunii/micro/common/exception/BusinessException.java

@@ -0,0 +1,42 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.exception;
+
+import com.crunii.micro.common.utils.Slf4jUtil;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2019年6月3日下午4:28:49
+ */
+@Data
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+public class BusinessException extends RuntimeException {
+
+	private static final long serialVersionUID = 1L;
+
+	public BusinessException() {
+
+	}
+	
+	public BusinessException(String message,Throwable e) {
+		super(message,e);
+	}
+
+	public BusinessException(String message) {
+		super(message);
+	}
+
+	public BusinessException(String pattern, Object... args) {
+		super(Slf4jUtil.formatException(pattern, args));
+	}
+
+	public static void main(String[] args) {
+		throw new BusinessException("id:{},name:{}", 10000, "Nick", new RuntimeException("abcd12312"));
+	}
+
+}

+ 29 - 0
micro-common/src/main/java/com/crunii/micro/common/exception/ErrorCode.java

@@ -0,0 +1,29 @@
+package com.crunii.micro.common.exception;
+
+import com.crunii.micro.common.exception.enums.GlobalErrorCodeConstants;
+import lombok.Data;
+
+/**
+ * 错误码对象
+ *
+ * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
+ * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
+ */
+@Data
+public class ErrorCode {
+
+    /**
+     * 错误码
+     */
+    private final Integer code;
+    /**
+     * 错误提示
+     */
+    private final String msg;
+
+    public ErrorCode(Integer code, String message) {
+        this.code = code;
+        this.msg = message;
+    }
+
+}

+ 112 - 0
micro-common/src/main/java/com/crunii/micro/common/exception/MicroException.java

@@ -0,0 +1,112 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.exception;
+
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.common.utils.Slf4jUtil;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2014-12-8下午4:41:03
+ */
+@ToString
+@Slf4j
+public class MicroException extends RuntimeException {
+
+	private static final long serialVersionUID = 1L;
+
+	public static final Map<String, String> messages = new HashMap<String, String>();
+
+	public static final String LOCK_FAILURE = "1000";
+
+	static {
+		messages.put(LOCK_FAILURE, "lockId={} lock failure by reason:{}!");
+	}
+
+	private String code = Result.DEFAULT_ERROR_CODE;
+	private String message;
+
+	public MicroException() {
+	}
+
+	/**
+	 * <pre>
+	 * 注意不要使用code和message来调用这个方法
+	 * 需要直接设置这两个属性使用
+	 * MicroException me = new MicroException();
+	 * me.setCode(result.getCode());
+	 * me.setMessage(result.getMsg());
+	 * throw me;
+	 * </pre>
+	 *
+	 * @param code
+	 * @param args
+	 */
+	public MicroException(String code, Object... args) {
+		this(code, null, args);
+	}
+
+	public MicroException(String code, Throwable cause, Object... args) {
+		super(cause);
+		this.code = code;
+		this.message = getMessageDetail(code, cause, args);
+	}
+
+	private static String getMessageDetail(String code, Throwable cause, Object... args) {
+
+		String pattern = messages.get(code);
+
+		if (!StringUtils.hasLength(pattern)) {
+			throw new RuntimeException("错误编码code:" + code + " 未定义!");
+		}
+
+		if (args != null && args.length > 0) {
+			Object[] params = new Object[args.length + 1];
+			System.arraycopy(args, 0, params, 0, args.length);
+			params[args.length] = cause;
+			return Slf4jUtil.formatException(pattern, params);
+		} else {
+			return Slf4jUtil.formatException(pattern, cause);
+		}
+	}
+
+	public MicroException(ErrorCode errorCode) {
+		this.code = Integer.toString(errorCode.getCode());
+		this.message = errorCode.getMsg();
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public void setMessage(String message) {
+		this.message = message;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public static String getErrorMsg(Throwable e) {
+		return Slf4jUtil.getErrorMsg(e);
+	}
+
+	public static String getMaxDesc(Object desc) {
+		return Slf4jUtil.getMaxDesc(desc);
+	}
+
+	public static void main(String[] args) {
+		MicroException m = new MicroException("1015", null, "OSHIT", "1231");
+		log.error("测试错误!", m);
+	}
+}

+ 51 - 0
micro-common/src/main/java/com/crunii/micro/common/exception/enums/GlobalErrorCodeConstants.java

@@ -0,0 +1,51 @@
+package com.crunii.micro.common.exception.enums;
+
+import com.crunii.micro.common.exception.ErrorCode;
+
+/**
+ * 全局错误码枚举
+ * 0-999 系统异常编码保留
+ * <p>
+ * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
+ * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
+ * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
+ *
+ * @author 芋道源码
+ */
+public interface GlobalErrorCodeConstants {
+
+    ErrorCode SUCCESS = new ErrorCode(0, "成功");
+
+    // ========== 客户端错误段 ==========
+
+    ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
+    ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
+    ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
+    ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
+    ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
+    ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
+    ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
+
+    // ========== 服务端错误段 ==========
+
+    ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
+
+    // ========== 自定义错误段 ==========
+    ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
+    ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
+
+    ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
+    ErrorCode DEPT_NOT_FOND = new ErrorCode(601, "角色数据被删除或用户未指定角色");
+    ErrorCode ROLE_NOT_FOND = new ErrorCode(701, "部门数据被删除或用户未指定部门");
+
+    /**
+     * 是否为服务端错误,参考 HTTP 5XX 错误码段
+     *
+     * @param code 错误码
+     * @return 是否
+     */
+    static boolean isServerErrorCode(Integer code) {
+        return code != null
+                && code >= INTERNAL_SERVER_ERROR.getCode() && code <= INTERNAL_SERVER_ERROR.getCode() + 99;
+    }
+}

+ 12 - 0
micro-common/src/main/java/com/crunii/micro/common/file/LineHandler.java

@@ -0,0 +1,12 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.file;
+
+/**
+ * @author 田平 create 2017年9月5日上午10:41:06
+ */
+public interface LineHandler {
+
+	public void lineString(String line);
+}

+ 19 - 0
micro-common/src/main/java/com/crunii/micro/common/file/LineHandlerImpl.java

@@ -0,0 +1,19 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.file;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author 田平 create 2017年9月5日上午10:57:09
+ */
+public class LineHandlerImpl implements LineHandler {
+	protected Logger log = LoggerFactory.getLogger(getClass());
+
+	@Override
+	public void lineString(String line) {
+		log.info(line);
+	}
+}

+ 195 - 0
micro-common/src/main/java/com/crunii/micro/common/file/MultReadFile.java

@@ -0,0 +1,195 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author 田平 create 2017年9月5日上午11:05:47
+ */
+public class MultReadFile {
+	protected Logger log = LoggerFactory.getLogger(getClass());
+	private File file;
+	private LineHandler handler = new LineHandlerImpl();
+	private int threadSize = 2;// 默认2个线程,实际线程因为文件平均分片的问题不是绝对平均所有可能会少一个线程。
+	private int lineSize = 0;// 文件行数,空行不算行数
+	private int bufferSize = 1024 * 1024;
+	private String charset = "UTF-8";// 源文件字符集
+
+	public static void main(String[] args) {
+		LineHandlerImpl handler = new LineHandlerImpl();
+		String url = "C:\\Users\\Administrator\\Desktop\\start2.txt";
+		File file = new File(url);
+		MultReadFile readFile = new MultReadFile(file, handler, "GBK", 1024 * 1024 * 10, 3);
+		readFile.read();
+	}
+
+	public void read() {
+		RandomAccessFile raf = null;
+		ExecutorService threadPool = null;
+		try {
+			threadPool = Executors.newFixedThreadPool(20);
+			raf = new RandomAccessFile(file, "r");
+			List<Range> ranges = new ArrayList<Range>();
+			long section = raf.length() / threadSize;
+			if (section == 0) {
+				section = 1;
+			}
+			Range.sharding(ranges, raf, 0, section);
+			CountDownLatch doneSignal = new CountDownLatch(ranges.size());
+			List<RangeReader> readers = new ArrayList<RangeReader>();
+			for (Range range : ranges) {
+				MappedByteBuffer mb = raf.getChannel().map(MapMode.READ_ONLY, range.getStart(), range.getLength());
+				RangeReader reader = new RangeReader(handler, mb, bufferSize, charset, doneSignal);
+				threadPool.execute(reader);
+				readers.add(reader);
+			}
+			doneSignal.await();
+			for (RangeReader reader : readers) {
+				lineSize = lineSize + reader.getLineSize();
+			}
+			log.info(file.getName() + " has " + lineSize + " rows.");
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			if (threadPool != null) {
+				threadPool.shutdown();
+			}
+			if (raf != null) {
+				try {
+					raf.close();
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		}
+
+	}
+
+	public MultReadFile(File file) {
+		this.file = file;
+	}
+
+	public MultReadFile(File file, LineHandler handler) {
+		this.file = file;
+		this.handler = handler;
+	}
+
+	public MultReadFile(File file, LineHandler handler, String charset) {
+		this.file = file;
+		this.handler = handler;
+	}
+
+	public MultReadFile(File file, LineHandler handler, String charset, int bufferSize) {
+		this.file = file;
+		this.handler = handler;
+		this.charset = charset;
+		this.bufferSize = bufferSize;
+	}
+
+	public MultReadFile(File file, LineHandler handler, String charset, int bufferSize, int threadSize) {
+		this.file = file;
+		this.handler = handler;
+		this.charset = charset;
+		this.bufferSize = bufferSize;
+		this.threadSize = threadSize;
+	}
+
+	/**
+	 * @return the file
+	 */
+	public File getFile() {
+		return file;
+	}
+
+	/**
+	 * @param file the file to set
+	 */
+	public void setFile(File file) {
+		this.file = file;
+	}
+
+	/**
+	 * @return the handler
+	 */
+	public LineHandler getHandler() {
+		return handler;
+	}
+
+	/**
+	 * @param handler the handler to set
+	 */
+	public void setHandler(LineHandler handler) {
+		this.handler = handler;
+	}
+
+	/**
+	 * @return the threadSize
+	 */
+	public int getThreadSize() {
+		return threadSize;
+	}
+
+	/**
+	 * @param threadSize the threadSize to set
+	 */
+	public void setThreadSize(int threadSize) {
+		this.threadSize = threadSize;
+	}
+
+	/**
+	 * @return the lineSize
+	 */
+	public int getLineSize() {
+		return lineSize;
+	}
+
+	/**
+	 * @param lineSize the lineSize to set
+	 */
+	public void setLineSize(int lineSize) {
+		this.lineSize = lineSize;
+	}
+
+	/**
+	 * @return the bufferSize
+	 */
+	public int getBufferSize() {
+		return bufferSize;
+	}
+
+	/**
+	 * @param bufferSize the bufferSize to set
+	 */
+	public void setBufferSize(int bufferSize) {
+		this.bufferSize = bufferSize;
+	}
+
+	/**
+	 * @return the charset
+	 */
+	public String getCharset() {
+		return charset;
+	}
+
+	/**
+	 * @param charset the charset to set
+	 */
+	public void setCharset(String charset) {
+		this.charset = charset;
+	}
+
+}

+ 90 - 0
micro-common/src/main/java/com/crunii/micro/common/file/Range.java

@@ -0,0 +1,90 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.file;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2017年9月3日上午8:41:19
+ */
+@Getter
+@Setter
+@ToString
+public class Range implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	private long start;
+	private long length;
+
+	public Range() {
+	}
+
+	public Range(int start, int length) {
+		this.start = start;
+		this.length = length;
+	}
+
+	public static void main(String[] args) throws Exception {
+		String url = "C:\\Users\\Administrator\\Desktop\\start2.txt";
+		File file = new File(url);
+		RandomAccessFile raf = new RandomAccessFile(file, "r");
+		List<Range> ranges = new ArrayList<Range>();
+		long section = raf.length() / 4;
+		sharding(ranges, raf, 0, section);
+		for (Range range : ranges) {
+			System.out.println(range);
+		}
+	}
+
+	public static void sharding(List<Range> ranges, RandomAccessFile raf, long position, long section) throws Exception {
+		if (position > raf.length() - 1) {
+			return;
+		}
+		Range range = new Range();
+		range.start = position;
+
+		long endPosition = position + section;
+		if (endPosition >= raf.length() - 1) {
+			range.length = raf.length() - position;
+			ranges.add(range);
+			return;
+		}
+
+		raf.seek(endPosition);
+		int tmp = raf.read();
+		while (!checkLineChar(tmp)) {
+			endPosition++;
+			if (endPosition >= raf.length() - 1) {
+				endPosition = raf.length() - 1;
+				break;
+			}
+			raf.seek(endPosition);
+			tmp = raf.read();
+		}
+		int tmp2 = raf.read();
+		if (tmp2 != -1 && checkLineChar(tmp2)) {
+			endPosition++;
+		}
+		range.length = endPosition - position;
+		ranges.add(range);
+		sharding(ranges, raf, endPosition + 1, section);
+	}
+
+	public static boolean checkLineChar(int data) {
+		return data == 10 || data == 13 ? true : false;
+	}
+
+	public static boolean checkLineChar(char data) {
+		return data == '\n' || data == '\r' ? true : false;
+	}
+
+}

+ 153 - 0
micro-common/src/main/java/com/crunii/micro/common/file/RangeReader.java

@@ -0,0 +1,153 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.file;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.MappedByteBuffer;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @author 田平 create 2017年9月5日上午10:39:08
+ */
+public class RangeReader implements Runnable {
+
+	private int lineSize = 0;// 文件行数,空行不算行数
+	private int bufferSize = 1024;
+	private String charset = "UTF-8";// 源文件字符集
+	private MappedByteBuffer mb;
+	private LineHandler handler;
+	private CountDownLatch doneSignal;
+
+	public RangeReader(LineHandler handler, MappedByteBuffer mb, CountDownLatch doneSignal) {
+		this.handler = handler;
+		this.mb = mb;
+		this.doneSignal = doneSignal;
+	}
+
+	public RangeReader(LineHandler handler, MappedByteBuffer mb, String charset, CountDownLatch doneSignal) {
+		this.handler = handler;
+		this.mb = mb;
+		this.charset = charset;
+		this.doneSignal = doneSignal;
+	}
+
+	public RangeReader(LineHandler handler, MappedByteBuffer mb, int bufferSize, String charset, CountDownLatch doneSignal) {
+		this.handler = handler;
+		this.mb = mb;
+		this.bufferSize = bufferSize;
+		this.charset = charset;
+		this.doneSignal = doneSignal;
+	}
+
+	public void run() {
+		ByteArrayOutputStream bos = null;
+		String line = null;
+		try {
+			bos = new ByteArrayOutputStream(bufferSize);
+			byte[] readBuff = new byte[bufferSize];
+			int start = 0;
+			int readLength = bufferSize;
+			while (mb.hasRemaining()) {
+				if (start + readLength < mb.limit()) {
+					readLength = bufferSize;
+				} else {
+					readLength = mb.limit() - start;
+				}
+				mb.get(readBuff, 0, readLength);
+				for (int i = 0; i < readLength; i++) {
+					byte tp = readBuff[i];
+					if (Range.checkLineChar(tp)) {
+						if (bos.size() > 0) {
+							line = new String(bos.toByteArray(), charset);
+							lineSize++;
+							handler.lineString(line);
+							bos.reset();
+						}
+					} else {
+						bos.write(tp);
+					}
+				}
+				start = start + readLength;
+			}
+			if (bos.size() > 0) {
+				line = new String(bos.toByteArray(), charset);
+				lineSize++;
+				handler.lineString(line);
+			}
+
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			doneSignal.countDown();
+			if (mb != null) {
+				mb.clear();
+			}
+			if (bos != null) {
+				try {
+					bos.close();
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		}
+	}
+
+	/**
+	 * @return the lineSize
+	 */
+	public int getLineSize() {
+		return lineSize;
+	}
+
+	/**
+	 * @param lineSize the lineSize to set
+	 */
+	public void setLineSize(int lineSize) {
+		this.lineSize = lineSize;
+	}
+
+	/**
+	 * @return the bufferSize
+	 */
+	public int getBufferSize() {
+		return bufferSize;
+	}
+
+	/**
+	 * @param bufferSize the bufferSize to set
+	 */
+	public void setBufferSize(int bufferSize) {
+		this.bufferSize = bufferSize;
+	}
+
+	/**
+	 * @return the charset
+	 */
+	public String getCharset() {
+		return charset;
+	}
+
+	/**
+	 * @param charset the charset to set
+	 */
+	public void setCharset(String charset) {
+		this.charset = charset;
+	}
+
+	/**
+	 * @return the handler
+	 */
+	public LineHandler getHandler() {
+		return handler;
+	}
+
+	/**
+	 * @param handler the handler to set
+	 */
+	public void setHandler(LineHandler handler) {
+		this.handler = handler;
+	}
+
+}

+ 193 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/ChannelSftpFactory.java

@@ -0,0 +1,193 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.Session;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+/**
+ * @author 田平 create 2016-6-14上午11:48:47
+ */
+@Slf4j
+@Configuration
+public class ChannelSftpFactory extends BasePooledObjectFactory<ChannelSftp> implements DisposableBean, InitializingBean {
+
+	private String host;
+	private int port = 22;
+	private String username;
+	private String password;
+	private int connectTimeout = 60000;// 连接超时时间
+	private Properties config = new Properties();
+	private static JSch jsch = new JSch();
+	private Session session;
+
+	/**
+	 * @see BasePooledObjectFactory#create()
+	 */
+	@Override
+	public ChannelSftp create() throws Exception {
+		ChannelSftp channelSftp = null;
+		synchronized (session) {
+			if (!session.isConnected()) {
+				session = createSession();
+			}
+			// session.openChannel is not thread safe
+			channelSftp = (ChannelSftp) session.openChannel("sftp");
+			channelSftp.connect(connectTimeout);
+		}
+		return channelSftp;
+	}
+
+	@Override
+	public void destroyObject(final PooledObject<ChannelSftp> p) throws Exception {
+		if (p.getObject() != null) {
+			p.getObject().disconnect();
+		}
+	}
+
+	@Override
+	public boolean validateObject(final PooledObject<ChannelSftp> p) {
+		if (p.getObject() != null) {
+			return p.getObject().isConnected();
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * @see BasePooledObjectFactory#wrap(Object)
+	 */
+	@Override
+	public PooledObject<ChannelSftp> wrap(ChannelSftp obj) {
+		return new DefaultPooledObject<ChannelSftp>(obj);
+	}
+
+	/**
+	 * @return the host
+	 */
+	public String getHost() {
+		return host;
+	}
+
+	/**
+	 * @param host the host to set
+	 */
+	public void setHost(String host) {
+		this.host = host;
+	}
+
+	/**
+	 * @return the port
+	 */
+	public int getPort() {
+		return port;
+	}
+
+	/**
+	 * @param port the port to set
+	 */
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	/**
+	 * @return the username
+	 */
+	public String getUsername() {
+		return username;
+	}
+
+	/**
+	 * @param username the username to set
+	 */
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	/**
+	 * @return the password
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * @param password the password to set
+	 */
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	/**
+	 * @return the connectTimeout
+	 */
+	public int getConnectTimeout() {
+		return connectTimeout;
+	}
+
+	/**
+	 * @param connectTimeout the connectTimeout to set
+	 */
+	public void setConnectTimeout(int connectTimeout) {
+		this.connectTimeout = connectTimeout;
+	}
+
+	/**
+	 * @return the config
+	 */
+	public Properties getConfig() {
+		return config;
+	}
+
+	/**
+	 * @param config the config to set
+	 */
+	public void setConfig(Properties config) {
+		this.config = config;
+	}
+
+	/**
+	 * @see InitializingBean#afterPropertiesSet()
+	 */
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		session = createSession();
+	}
+
+	/**
+	 * @see DisposableBean#destroy()
+	 */
+	@Override
+	public void destroy() throws Exception {
+		if (session != null) {
+			session.disconnect();
+		}
+	}
+
+	private synchronized Session createSession() {
+		Session session = null;
+		try {
+			session = jsch.getSession(username, host, port);// 根据用户名,主机ip和端口获取一个Session对象
+			session.setPassword(password); // 设置密码
+			config.put("StrictHostKeyChecking", "no");
+			session.setConfig(config);// 为Session对象设置properties
+			session.connect(connectTimeout);// 通过Session建立连接
+			log.info("SSH connect to {}:{} with user {}!", host, port, username);
+			return session;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}

+ 122 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/ChannelSftpPoolFactory.java

@@ -0,0 +1,122 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import com.crunii.micro.common.pool.PoolConfig;
+import com.jcraft.jsch.ChannelSftp;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * @author 田平 create 2016-6-14下午4:40:58
+ */
+public class ChannelSftpPoolFactory implements FactoryBean<GenericObjectPool<ChannelSftp>>, DisposableBean, InitializingBean {
+	private ChannelSftpFactory channelSftpFactory;
+
+	private GenericObjectPool<ChannelSftp> pool;
+
+	private GenericObjectPoolConfig<ChannelSftp> config = null;
+
+	/**
+	 * @see FactoryBean#getObject()
+	 */
+	@Override
+	public GenericObjectPool<ChannelSftp> getObject() throws Exception {
+		return pool;
+	}
+
+	/**
+	 * @see FactoryBean#getObjectType()
+	 */
+	@Override
+	public Class<?> getObjectType() {
+		return GenericObjectPool.class;
+	}
+
+	/**
+	 * @see FactoryBean#isSingleton()
+	 */
+	@Override
+	public boolean isSingleton() {
+		return false;
+	}
+
+	/**
+	 * @see DisposableBean#destroy()
+	 */
+	@Override
+	public void destroy() throws Exception {
+		if (pool != null) {
+			pool.close();
+		}
+
+	}
+
+	/**
+	 * @see InitializingBean#afterPropertiesSet()
+	 */
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		if (config == null) {
+			config = new GenericObjectPoolConfig<ChannelSftp>();
+			config.setMaxTotal(PoolConfig.DEFAULT_MAX_TOTAL);
+			config.setMaxIdle(PoolConfig.DEFAULT_MAX_IDLE);
+			config.setMinIdle(PoolConfig.DEFAULT_MIN_IDLE);
+			config.setMaxWaitMillis(PoolConfig.DEFAULT_MAX_WAIT_MILLIS);
+			config.setMinEvictableIdleTimeMillis(PoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+			config.setSoftMinEvictableIdleTimeMillis(PoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+			config.setTimeBetweenEvictionRunsMillis(PoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
+			config.setTestOnBorrow(PoolConfig.DEFAULT_TEST_ON_BORROW);
+			config.setTestOnReturn(PoolConfig.DEFAULT_TEST_ON_RETURN);
+			config.setTestOnCreate(PoolConfig.DEFAULT_TEST_ON_CREATE);
+		}
+		pool = new GenericObjectPool<ChannelSftp>(channelSftpFactory, config);
+	}
+
+	/**
+	 * @return the pool
+	 */
+	public GenericObjectPool<ChannelSftp> getPool() {
+		return pool;
+	}
+
+	/**
+	 * @param pool the pool to set
+	 */
+	public void setPool(GenericObjectPool<ChannelSftp> pool) {
+		this.pool = pool;
+	}
+
+	/**
+	 * @return the config
+	 */
+	public GenericObjectPoolConfig<ChannelSftp> getConfig() {
+		return config;
+	}
+
+	/**
+	 * @param config the config to set
+	 */
+	public void setConfig(GenericObjectPoolConfig<ChannelSftp> config) {
+		this.config = config;
+	}
+
+	/**
+	 * @return the channelSftpFactory
+	 */
+	public ChannelSftpFactory getChannelSftpFactory() {
+		return channelSftpFactory;
+	}
+
+	/**
+	 * @param channelSftpFactory the channelSftpFactory to set
+	 */
+	public void setChannelSftpFactory(ChannelSftpFactory channelSftpFactory) {
+		this.channelSftpFactory = channelSftpFactory;
+	}
+
+}

+ 254 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/FTPClientFactory.java

@@ -0,0 +1,254 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+
+/**
+ * @author 田平 create 2016-6-14上午11:48:47
+ */
+@Slf4j
+public class FTPClientFactory extends BasePooledObjectFactory<FTPClient> {
+	private FTPClientConfig clientConfig;
+	private String host;
+	private int port;
+	private String username;
+	private String password;
+	private int timeout = 30000;// 默认30秒
+	private int fileType = FTP.BINARY_FILE_TYPE;// 文件类型
+	private int bufferSize = 1024 * 1024 * 20;// 20M
+	private String systemType = FTPClientConfig.SYST_UNIX;
+	private String encoding = "GBK";
+
+	public static void main(String[] args) throws Exception {
+		FTPClientFactory clientFactory = new FTPClientFactory();
+		clientFactory.setHost("136.3.243.201");
+		clientFactory.setPort(21);
+		clientFactory.setUsername("cqpf");
+		clientFactory.setPassword("cqpf");
+		GenericObjectPool<FTPClient> pool = new GenericObjectPool<FTPClient>(clientFactory, new GenericObjectPoolConfig());
+		FTPClient client = pool.borrowObject();
+		client.changeWorkingDirectory("/home/cqpf/tomcat-6.0.16-pfjob/ods/pf/");
+		System.out.println(client.deleteFile("test.txt"));
+		// client.storeFile("test.txt", new ByteArrayInputStream("中国aaaa1\n".getBytes("GBK")));
+		// client.appendFile("test.txt", new ByteArrayInputStream("中国aaaa2\n".getBytes("GBK")));
+		pool.returnObject(client);
+		pool.close();
+	}
+
+	/**
+	 * @see BasePooledObjectFactory#create()
+	 */
+	@Override
+	public FTPClient create() throws Exception {
+		FTPClient ftp = new FTPClient();
+
+		if (clientConfig != null) {
+			ftp.configure(clientConfig);
+		} else {
+			FTPClientConfig ftpClientConfig = new FTPClientConfig(systemType);
+			ftp.configure(ftpClientConfig);
+		}
+		ftp.connect(host, port);
+		int reply = ftp.getReplyCode();
+		log.info("Connect FTP server:" + host + ",port:" + port + ",replyCode:" + ftp.getReplyCode() + ",replyString:" + ftp.getReplyString());
+		if (!FTPReply.isPositiveCompletion(reply)) {
+			if (ftp.isConnected()) {
+				ftp.disconnect();
+			}
+			throw new RuntimeException("FTP server refused connection" + host + ",replyString:" + ftp.getReplyString());
+		}
+		boolean success = ftp.login(username, password);
+
+		ftp.setSoTimeout(timeout);
+		ftp.setBufferSize(bufferSize);
+		ftp.setFileType(fileType);
+		ftp.enterLocalPassiveMode();// 被动模式
+		ftp.setControlEncoding(encoding);
+		if (!success) {
+			if (ftp.isConnected()) {
+				ftp.disconnect();
+			}
+			throw new RuntimeException(
+					"FTP server login failure!server:" + host + ",username:" + username + ",password:" + password + ",replyString:" + ftp
+							.getReplyString());
+		}
+
+		return ftp;
+	}
+
+	/**
+	 * @see BasePooledObjectFactory#wrap(Object)
+	 */
+	@Override
+	public PooledObject<FTPClient> wrap(FTPClient obj) {
+		return new DefaultPooledObject<FTPClient>(obj);
+	}
+
+	public boolean validateObject(PooledObject<FTPClient> p) {
+		boolean available = p.getObject().isAvailable();
+		boolean connected = p.getObject().isConnected();
+		// log.info("available:" + available + ",connected:" + connected);
+		return available && connected;
+	}
+
+	public void destroyObject(PooledObject<FTPClient> p) throws Exception {
+		if (p != null) {
+			FTPClient ftp = p.getObject();
+			if (ftp.isConnected()) {
+				ftp.disconnect();
+			}
+		}
+	}
+
+	/**
+	 * @param clientConfig the clientConfig to set
+	 */
+	public void setClientConfig(FTPClientConfig clientConfig) {
+		this.clientConfig = clientConfig;
+	}
+
+	/**
+	 * @return the server
+	 */
+	public String getHost() {
+		return host;
+	}
+
+	/**
+	 * @param host the server to set
+	 */
+	public void setHost(String host) {
+		this.host = host;
+	}
+
+	/**
+	 * @return the port
+	 */
+	public int getPort() {
+		return port;
+	}
+
+	/**
+	 * @param port the port to set
+	 */
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	/**
+	 * @return the username
+	 */
+	public String getUsername() {
+		return username;
+	}
+
+	/**
+	 * @param username the username to set
+	 */
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	/**
+	 * @return the password
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * @param password the password to set
+	 */
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	/**
+	 * @return the clientConfig
+	 */
+	public FTPClientConfig getClientConfig() {
+		return clientConfig;
+	}
+
+	/**
+	 * @return the timeout
+	 */
+	public int getTimeout() {
+		return timeout;
+	}
+
+	/**
+	 * @param timeout the timeout to set
+	 */
+	public void setTimeout(int timeout) {
+		this.timeout = timeout;
+	}
+
+	/**
+	 * @return the fileType
+	 */
+	public int getFileType() {
+		return fileType;
+	}
+
+	/**
+	 * @param fileType the fileType to set
+	 */
+	public void setFileType(int fileType) {
+		this.fileType = fileType;
+	}
+
+	/**
+	 * @return the bufferSize
+	 */
+	public int getBufferSize() {
+		return bufferSize;
+	}
+
+	/**
+	 * @param bufferSize the bufferSize to set
+	 */
+	public void setBufferSize(int bufferSize) {
+		this.bufferSize = bufferSize;
+	}
+
+	/**
+	 * @return the systemType
+	 */
+	public String getSystemType() {
+		return systemType;
+	}
+
+	/**
+	 * @param systemType the systemType to set
+	 */
+	public void setSystemType(String systemType) {
+		this.systemType = systemType;
+	}
+
+	/**
+	 * @return the encoding
+	 */
+	public String getEncoding() {
+		return encoding;
+	}
+
+	/**
+	 * @param encoding the encoding to set
+	 */
+	public void setEncoding(String encoding) {
+		this.encoding = encoding;
+	}
+
+}

+ 124 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/FTPClientPoolFactory.java

@@ -0,0 +1,124 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import com.crunii.micro.common.pool.PoolConfig;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * @author 田平 create 2016-6-14下午4:40:58
+ */
+public class FTPClientPoolFactory implements FactoryBean<GenericObjectPool<FTPClient>>, DisposableBean, InitializingBean {
+	private FTPClientFactory clientFactory;
+
+	private GenericObjectPool<FTPClient> pool;
+
+	private GenericObjectPoolConfig<FTPClient> config = null;
+
+	/**
+	 * @see FactoryBean#getObject()
+	 */
+	@Override
+	public GenericObjectPool<FTPClient> getObject() throws Exception {
+
+		return pool;
+	}
+
+	/**
+	 * @see FactoryBean#getObjectType()
+	 */
+	@Override
+	public Class<?> getObjectType() {
+		return GenericObjectPool.class;
+	}
+
+	/**
+	 * @see FactoryBean#isSingleton()
+	 */
+	@Override
+	public boolean isSingleton() {
+		return false;
+	}
+
+	/**
+	 * @return the clientFactory
+	 */
+	public FTPClientFactory getClientFactory() {
+		return clientFactory;
+	}
+
+	/**
+	 * @param clientFactory the clientFactory to set
+	 */
+	public void setClientFactory(FTPClientFactory clientFactory) {
+		this.clientFactory = clientFactory;
+	}
+
+	/**
+	 * @see DisposableBean#destroy()
+	 */
+	@Override
+	public void destroy() throws Exception {
+		if (pool != null) {
+			pool.close();
+		}
+
+	}
+
+	/**
+	 * @see InitializingBean#afterPropertiesSet()
+	 */
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		if (config == null) {
+			config = new GenericObjectPoolConfig<FTPClient>();
+			config.setMaxTotal(PoolConfig.DEFAULT_MAX_TOTAL);
+			config.setMaxIdle(PoolConfig.DEFAULT_MAX_IDLE);
+			config.setMinIdle(PoolConfig.DEFAULT_MIN_IDLE);
+			config.setMaxWaitMillis(PoolConfig.DEFAULT_MAX_WAIT_MILLIS);
+			config.setMinEvictableIdleTimeMillis(PoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+			config.setSoftMinEvictableIdleTimeMillis(PoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+			config.setTimeBetweenEvictionRunsMillis(PoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
+			config.setTestOnBorrow(PoolConfig.DEFAULT_TEST_ON_BORROW);
+			config.setTestOnReturn(PoolConfig.DEFAULT_TEST_ON_RETURN);
+			config.setTestOnCreate(PoolConfig.DEFAULT_TEST_ON_CREATE);
+		}
+
+		pool = new GenericObjectPool<FTPClient>(clientFactory, config);
+	}
+
+	/**
+	 * @return the pool
+	 */
+	public GenericObjectPool<FTPClient> getPool() {
+		return pool;
+	}
+
+	/**
+	 * @param pool the pool to set
+	 */
+	public void setPool(GenericObjectPool<FTPClient> pool) {
+		this.pool = pool;
+	}
+
+	/**
+	 * @return the config
+	 */
+	public GenericObjectPoolConfig<FTPClient> getConfig() {
+		return config;
+	}
+
+	/**
+	 * @param config the config to set
+	 */
+	public void setConfig(GenericObjectPoolConfig<FTPClient> config) {
+		this.config = config;
+	}
+
+}

+ 245 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/FtpRemoteFile.java

@@ -0,0 +1,245 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import com.crunii.micro.common.exception.BusinessException;
+import com.crunii.micro.common.utils.CycleAtomicInteger;
+import com.crunii.micro.common.utils.FileUtil;
+import com.crunii.micro.common.utils.Slf4jUtil;
+import com.crunii.micro.common.utils.StopUtil;
+import com.google.common.base.Splitter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.util.Assert;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author 田平 create 2021/9/22 16:15
+ */
+@Slf4j
+public class FtpRemoteFile implements RemoteFile {
+
+	private GenericObjectPool<FTPClient> pool;
+
+	public static void main(String[] args) throws Exception {
+		String basePath = "";
+		FTPClientFactory clientFactory = new FTPClientFactory();
+		clientFactory.setHost("");
+		clientFactory.setPort(30002);
+		clientFactory.setUsername("");
+		clientFactory.setPassword("");
+		GenericObjectPool<FTPClient> pool = new GenericObjectPool<>(clientFactory, new GenericObjectPoolConfig());
+
+		FtpRemoteFile remoteFile = new FtpRemoteFile(pool);
+		int size = 1000;
+		CountDownLatch latch = new CountDownLatch(size);
+		AtomicInteger fileName = new AtomicInteger(1);
+		CycleAtomicInteger cycleAtomicInteger = new CycleAtomicInteger(1, 9);
+		ExecutorService es = Executors.newFixedThreadPool(10);
+		for (int i = 0; i < size; i++) {
+			es.execute(() -> {
+				String remoteName = "file_" + fileName.getAndIncrement() + ".txt";
+				String remotePath = basePath + cycleAtomicInteger.nextId() + "/";
+				remoteFile.uploadFile(remotePath, remoteName, "D:\\rr.txt");
+				latch.countDown();
+			});
+		}
+		latch.await();
+		StopUtil.stopES(es);
+		pool.close();
+	}
+
+	public FtpRemoteFile(GenericObjectPool<FTPClient> pool) {
+		this.pool = pool;
+	}
+
+	@Override
+	public void uploadFile(String remotePath, String remoteName, String localFilePath) {
+		uploadFile(remotePath, remoteName, FileUtil.getFileInputStream(localFilePath));
+	}
+
+	@Override
+	public void uploadFile(String remotePath, String remoteName, InputStream in) {
+		FTPClient ftpClient = null;
+		String realPath = FileUtil.getRealPath(remotePath);
+		try {
+			ftpClient = pool.borrowObject();
+			mkdirWithP(ftpClient, realPath);
+			ftpClient.storeFile(realPath + remoteName, in);
+		} catch (Exception e) {
+			throw new RuntimeException("uploadFile error!", e);
+		} finally {
+			IOUtils.closeQuietly(in);
+			pool.returnObject(ftpClient);
+		}
+	}
+
+	public static void mkdirWithP(FTPClient ftpClient, String remotePath) {
+		if (StringUtils.hasText(remotePath)) {
+			String realPath = FileUtil.getRealPath(remotePath);
+			Iterable<String> split = Splitter.on('/').trimResults().omitEmptyStrings().split(realPath);
+			StringBuilder sb = new StringBuilder("/");
+			try {
+				for (String curr : split) {
+					String currPath = sb.append(curr).append("/").toString();
+					ftpClient.changeWorkingDirectory(currPath);
+					int returnCode = ftpClient.getReplyCode();
+					if (returnCode == 550) {
+						//目录不存在,创建目录
+						try {
+							ftpClient.makeDirectory(currPath);
+						} catch (Exception e) {
+							log.warn("mkdir {} error!", currPath, e);
+						}
+					}
+				}
+				//切换到目标目录
+				ftpClient.changeWorkingDirectory(realPath);
+			} catch (Exception e) {
+				throw new RuntimeException(Slf4jUtil.formatMsg("mkdirWithP ftp mkdir -p {} error!", realPath), e);
+			}
+
+		}
+	}
+
+	private void isFileExists(FTPClient ftpClient, String fullPath) throws IOException {
+		FTPFile[] ftpFiles = ftpClient.listFiles(fullPath);
+		if (ftpFiles.length == 0) {
+			throw new BusinessException("remote file not exists!path:{}", fullPath);
+		}
+	}
+
+	@Override
+	public byte[] getFileBytes(String remotePath, String remoteName) {
+		FTPClient ftpClient = null;
+		String realPath = FileUtil.getRealPath(remotePath);
+		String fullPath = realPath + remoteName;
+		byte[] bytes;
+		try {
+			ftpClient = pool.borrowObject();
+			isFileExists(ftpClient, fullPath);
+			InputStream inputStream = ftpClient.retrieveFileStream(fullPath);
+			//自动关闭流
+			bytes = FileCopyUtils.copyToByteArray(inputStream);
+		} catch (Exception e) {
+			throw new RuntimeException("getFileBytes error!", e);
+		} finally {
+			try {
+				//ftpClient.retrieveFileStream 后必须调用完整指令
+				ftpClient.completePendingCommand();
+			} catch (IOException e) {
+				log.warn("completePendingCommand {} error!", fullPath, e);
+			}
+			pool.returnObject(ftpClient);
+		}
+		return bytes;
+	}
+
+	@Override
+	public int copyRange(String remotePath, String remoteName, int start, int end, OutputStream output) {
+		FTPClient ftpClient = null;
+		String realPath = FileUtil.getRealPath(remotePath);
+		String fullPath = realPath + remoteName;
+		InputStream input = null;
+		try {
+			ftpClient = pool.borrowObject();
+			isFileExists(ftpClient, fullPath);
+			input = ftpClient.retrieveFileStream(fullPath);
+			Assert.notNull(input, "No InputStream specified");
+			Assert.notNull(output, "No OutputStream specified");
+			return (int) StreamUtils.copyRange(input, output, start, end);
+		} catch (Exception e) {
+			throw new RuntimeException("copyRange error!", e);
+		} finally {
+			IOUtils.closeQuietly(input);
+			IOUtils.closeQuietly(output);
+			try {
+				//ftpClient.retrieveFileStream 后必须调用完整指令
+				ftpClient.completePendingCommand();
+			} catch (IOException e) {
+				log.warn("completePendingCommand {} error!", fullPath, e);
+			}
+			pool.returnObject(ftpClient);
+		}
+
+	}
+
+	@Override
+	public int copyToOutputStream(String remotePath, String remoteName, OutputStream output) {
+		FTPClient ftpClient = null;
+		String realPath = FileUtil.getRealPath(remotePath);
+		String fullPath = realPath + remoteName;
+		InputStream input;
+		try {
+			ftpClient = pool.borrowObject();
+			isFileExists(ftpClient, fullPath);
+			input = ftpClient.retrieveFileStream(fullPath);
+
+			Assert.notNull(input, "No InputStream specified");
+			Assert.notNull(output, "No OutputStream specified");
+			//自动关闭流
+			return FileCopyUtils.copy(input, output);
+		} catch (Exception e) {
+			throw new RuntimeException("copyToOutputStream error!", e);
+		} finally {
+			try {
+				//ftpClient.retrieveFileStream 后必须调用完整指令
+				ftpClient.completePendingCommand();
+			} catch (IOException e) {
+				log.warn("completePendingCommand {} error!", fullPath, e);
+			}
+			pool.returnObject(ftpClient);
+		}
+	}
+
+	@Override
+	public void downloadFile(String remotePath, String remoteName, String localFilePath, String localFileName) {
+		FTPClient ftpClient = null;
+		InputStream input;
+		OutputStream output;
+		String realPath = FileUtil.getRealPath(remotePath);
+		String realLocalFilePath = FileUtil.getRealPath(localFilePath);
+		String fullPath = realPath + remoteName;
+		try {
+			ftpClient = pool.borrowObject();
+			isFileExists(ftpClient, fullPath);
+			input = ftpClient.retrieveFileStream(fullPath);
+			File file = new File(realLocalFilePath + localFileName);
+			output = new FileOutputStream(file);
+			//自动关闭流
+			FileCopyUtils.copy(input, output);
+		} catch (Exception e) {
+			throw new RuntimeException("downloadFile error!", e);
+		} finally {
+			try {
+				//ftpClient.retrieveFileStream 后必须调用完整指令
+				ftpClient.completePendingCommand();
+			} catch (IOException e) {
+				log.warn("completePendingCommand {} error!", fullPath, e);
+			}
+			pool.returnObject(ftpClient);
+		}
+	}
+
+	public GenericObjectPool<FTPClient> getPool() {
+		return pool;
+	}
+
+	public void setPool(GenericObjectPool<FTPClient> pool) {
+		this.pool = pool;
+	}
+}

+ 84 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/RemoteFile.java

@@ -0,0 +1,84 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
+ * <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * <pre>支持ftp和sftp</pre>
+ *
+ * @author 田平 create 2021/9/22 16:07
+ */
+public interface RemoteFile {
+
+	/**
+	 * <pre>
+	 * 上传本地文件到远程目录,远程目录不存在自动创建
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param localFilePath
+	 */
+	public void uploadFile(String remotePath, String remoteName, String localFilePath);
+
+	/**
+	 * <pre>
+	 * 上传本地文件流到远程目录,远程目录不存在自动创建
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param in
+	 */
+	public void uploadFile(String remotePath, String remoteName, InputStream in);
+
+	/**
+	 * <pre>
+	 * 下载远程文件的字节流
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @return
+	 */
+	public byte[] getFileBytes(String remotePath, String remoteName);
+
+	/**
+	 * 拷贝字节流到输入流
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param output
+	 * @return 拷贝大小
+	 */
+	public int copyToOutputStream(String remotePath, String remoteName, OutputStream output);
+
+	/**
+	 * 范围copy
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param start
+	 * @param end
+	 * @param output
+	 * @return
+	 */
+	public int copyRange(String remotePath, String remoteName, int start, int end, OutputStream output);
+
+	/**
+	 * <pre>
+	 * 下载远程文件到本地
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param localFilePath
+	 * @param localFileName
+	 */
+	public void downloadFile(String remotePath, String remoteName, String localFilePath, String localFileName);
+
+}

+ 231 - 0
micro-common/src/main/java/com/crunii/micro/common/ftp/SftpRemoteFile.java

@@ -0,0 +1,231 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.ftp;
+
+import com.crunii.micro.common.utils.*;
+import com.jcraft.jsch.ChannelSftp;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.springframework.util.Assert;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.StreamUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * <pre>
+ *  频繁调用sftp的上传下载,如果传输大文件请使用SFTPUtil
+ *  注意连接池的最大连接数不要配置太大,有可能linux主机有限制
+ *  一般配置20个足以
+ * </pre>
+ *
+ * @author 田平 create 2019年3月28日下午3:27:57
+ */
+public class SftpRemoteFile implements RemoteFile {
+
+	private GenericObjectPool<ChannelSftp> pool;
+
+	public SftpRemoteFile(GenericObjectPool<ChannelSftp> pool) {
+		this.pool = pool;
+	}
+
+	public static void main(String[] args) throws Exception {
+
+		ChannelSftpFactory factory = new ChannelSftpFactory();
+		factory.setHost("192.168.10.142");
+		factory.setUsername("microsftp");
+		factory.setPassword("@Crunii,.123");
+		factory.setPort(22);
+		factory.afterPropertiesSet();
+		ChannelSftpPoolFactory poolFactory = new ChannelSftpPoolFactory();
+		poolFactory.setChannelSftpFactory(factory);
+		poolFactory.afterPropertiesSet();
+		poolFactory.getConfig().setMaxTotal(20);
+		String localFilePath = "C:\\Users\\Administrator\\Desktop\\json.txt";
+		String remotePath = "/home/microsftp/";
+		SftpRemoteFile sshTool = new SftpRemoteFile(poolFactory.getObject());
+
+		int threadSize = 1000;
+		ExecutorService es = Executors.newFixedThreadPool(100);
+		CountDownLatch doneSignal = new CountDownLatch(threadSize);
+		Date date = new Date();
+		for (int i = 0; i < threadSize; i++) {
+			es.execute(new Runnable() {
+				@Override
+				public void run() {
+					sshTool.uploadFile(remotePath, NanoIdUtil.randomLowerNanoId() + ".txt", FileUtil.getFileInputStream(localFilePath));
+					doneSignal.countDown();
+				}
+			});
+		}
+		doneSignal.await();
+		System.out.println("------>used " + DateUtil.getUseTime(date) + "ms");
+
+		sshTool.uploadFile(remotePath, "a.txt", FileUtil.getFileInputStream(localFilePath));
+		sshTool.downloadFile(remotePath, "a.txt", "d://", "b.txt");
+		String content = new String(sshTool.getFileBytes(remotePath, "a.txt"));
+		System.out.println(content);
+		factory.destroy();
+		poolFactory.destroy();
+		StopUtil.stopES(es);
+
+	}
+
+	/**
+	 * <pre>
+	 * 上传本地文件到远程目录,远程目录不存在自动创建
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param localFilePath
+	 */
+	@Override
+	public void uploadFile(String remotePath, String remoteName, String localFilePath) {
+		uploadFile(remotePath, remoteName, FileUtil.getFileInputStream(localFilePath));
+	}
+
+	/**
+	 * <pre>
+	 * 上传本地文件流到远程目录,远程目录不存在自动创建
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param in
+	 */
+	@Override
+	public void uploadFile(String remotePath, String remoteName, InputStream in) {
+		ChannelSftp channelSftp = null;
+		String realPath = FileUtil.getRealPath(remotePath);
+		try {
+			channelSftp = pool.borrowObject();
+			SftpUtil.mkdirWithP(channelSftp, realPath);
+			channelSftp.put(in, realPath + remoteName);
+		} catch (Exception e) {
+			throw new RuntimeException("uploadFile error!", e);
+		} finally {
+			IOUtils.closeQuietly(in);
+			pool.returnObject(channelSftp);
+		}
+	}
+
+	/**
+	 * <pre>
+	 * 下载远程文件的字节流
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @return
+	 */
+	@Override
+	public byte[] getFileBytes(String remotePath, String remoteName) {
+		String realPath = FileUtil.getRealPath(remotePath);
+		ChannelSftp channelSftp = null;
+		byte[] bytes = null;
+		InputStream input = null;
+		try {
+			channelSftp = pool.borrowObject();
+			input = channelSftp.get(realPath + remoteName);
+			bytes = FileCopyUtils.copyToByteArray(input);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			pool.returnObject(channelSftp);
+		}
+		return bytes;
+	}
+
+	@Override
+	public int copyRange(String remotePath, String remoteName, int start, int end, OutputStream output) {
+		String realPath = FileUtil.getRealPath(remotePath);
+		ChannelSftp channelSftp = null;
+		InputStream input = null;
+		try {
+			channelSftp = pool.borrowObject();
+			input = channelSftp.get(realPath + remoteName);
+			Assert.notNull(input, "No InputStream specified");
+			Assert.notNull(output, "No OutputStream specified");
+			return (int) StreamUtils.copyRange(input, output, start, end);
+		} catch (Exception e) {
+			throw new RuntimeException("copyRange error!", e);
+		} finally {
+			IOUtils.closeQuietly(input);
+			IOUtils.closeQuietly(output);
+			pool.returnObject(channelSftp);
+		}
+	}
+
+	@Override
+	public int copyToOutputStream(String remotePath, String remoteName, OutputStream output) {
+		String realPath = FileUtil.getRealPath(remotePath);
+		ChannelSftp channelSftp = null;
+		InputStream input = null;
+		try {
+			channelSftp = pool.borrowObject();
+			input = channelSftp.get(realPath + remoteName);
+			Assert.notNull(input, "No InputStream specified");
+			Assert.notNull(output, "No OutputStream specified");
+			//自动关闭流
+			return FileCopyUtils.copy(input, output);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			pool.returnObject(channelSftp);
+		}
+	}
+
+	/**
+	 * <pre>
+	 * 下载远程文件到本地
+	 * </pre>
+	 *
+	 * @param remotePath
+	 * @param remoteName
+	 * @param localFilePath
+	 * @param localFileName
+	 */
+	@Override
+	public void downloadFile(String remotePath, String remoteName, String localFilePath, String localFileName) {
+		String realPath = FileUtil.getRealPath(remotePath);
+		String realLocalPath = FileUtil.getRealPath(localFilePath);
+		ChannelSftp channelSftp = null;
+		InputStream input = null;
+		OutputStream output = null;
+		try {
+			channelSftp = pool.borrowObject();
+			input = channelSftp.get(realPath + remoteName);
+			File file = new File(realLocalPath + localFileName);
+			output = new FileOutputStream(file);
+			FileCopyUtils.copy(input, output);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			pool.returnObject(channelSftp);
+		}
+	}
+
+	/**
+	 * @return the pool
+	 */
+	public GenericObjectPool<ChannelSftp> getPool() {
+		return pool;
+	}
+
+	/**
+	 * @param pool the pool to set
+	 */
+	public void setPool(GenericObjectPool<ChannelSftp> pool) {
+		this.pool = pool;
+	}
+
+}

+ 72 - 0
micro-common/src/main/java/com/crunii/micro/common/http/HttpClientHolder.java

@@ -0,0 +1,72 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.http;
+
+import com.crunii.micro.common.log.BusiLogComHttp;
+import com.crunii.micro.common.spring.SpringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.Header;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.util.ObjectUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2020年1月10日下午3:18:23
+ */
+@Slf4j
+public class HttpClientHolder {
+
+	public static final String REQ_BODY_KEY = HttpClientHolder.class + ".request.body.key";
+	public static final String RSP_BODY_KEY = HttpClientHolder.class + ".response.body.key";
+
+	public static ThreadLocal<Map<String, Object>> busiLogInfo = new NamedThreadLocal<Map<String, Object>>("HttpClientBusiLogInfo") {
+		@Override
+		protected Map<String, Object> initialValue() {
+			return new HashMap<>(8);
+		}
+	};
+
+	public static Map<String, Object> getBusiInfoAndClean() {
+		busiLogInfo.get().clear();
+		return busiLogInfo.get();
+	}
+
+	public static List<String> getHeaderInfo(Header[] headers) {
+		if (!ObjectUtils.isEmpty(headers)) {
+			List<String> list = new ArrayList<>(headers.length);
+			for (int i = 0; i < headers.length; i++) {
+				list.add(headers[i].toString());
+			}
+			return list;
+		} else {
+			return null;
+		}
+	}
+
+	public static void doHttpLog() {
+		try {
+			ApplicationContext ctx = SpringUtil.getAppCtx();
+			if (ctx != null) {
+				BusiLogComHttp httpLog = ctx.getBean(BusiLogComHttp.class);
+				if (httpLog != null) {
+					httpLog.busiLogByMap(busiLogInfo.get());
+					busiLogInfo.get().clear();
+				} else {
+					log.info("httpLog:{}", busiLogInfo.get());
+				}
+			} else {
+				log.info("httpLog:{}", busiLogInfo.get());
+			}
+		} finally {
+			busiLogInfo.get().clear();
+		}
+
+	}
+
+}

+ 617 - 0
micro-common/src/main/java/com/crunii/micro/common/http/HttpClientUtil.java

@@ -0,0 +1,617 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.http;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.Base64;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustAllStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+import org.springframework.util.StringUtils;
+
+import com.crunii.micro.common.http.handler.HttpReqErrorHandler;
+import com.crunii.micro.common.http.interceptor.LogHttpReqInterceptor;
+import com.crunii.micro.common.http.interceptor.LogHttpRspInterceptor;
+
+/**
+ * @author 田平 create 2016年1月26日上午11:33:51 支持https的自签证书和单独的url设置超时参数
+ */
+public class HttpClientUtil {
+	private static final Log log = LogFactory.getLog(HttpClientUtil.class);
+	private static final String defaultEncode = "UTF-8";// 默认编码
+	private static final int socketTimeout = 60000;// 默认60秒 响应超时
+	private static final int requestTimeout = 30000;// 默认30秒 请求超时
+	private static final int connectTimeout = 30000;// 默认30秒 链接超时
+
+	private static final int maxTotal = 512;// 默认最大连接数 默认
+	private static final int defaultMaxPerRoute = 64;// 默认每个路由基础的连接
+	private static RequestConfig defaultReqConfig;
+	private static PoolingHttpClientConnectionManager cm;
+	private static CloseableHttpClient httpClient;
+	static {
+		RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory> create();
+		ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory();
+		registryBuilder.register("http", plainSF);
+		// 指定信任密钥存储对象和连接套接字工厂
+		try {
+			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+			SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustAllStrategy()).build();
+			LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
+			registryBuilder.register("https", sslSF);
+			Registry<ConnectionSocketFactory> registry = registryBuilder.build();
+			defaultReqConfig = RequestConfig.custom().setConnectionRequestTimeout(requestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
+			cm = new PoolingHttpClientConnectionManager(registry);
+			cm.setMaxTotal(maxTotal);
+			cm.setDefaultMaxPerRoute(defaultMaxPerRoute);
+			httpClient = HttpClients.custom()
+					.setRetryHandler(new HttpReqErrorHandler())
+					.addInterceptorFirst(new LogHttpReqInterceptor())
+					.addInterceptorFirst(new LogHttpRspInterceptor())
+					.setConnectionManager(cm).build();
+		} catch (Exception e) {
+			throw new RuntimeException("httpsClient build failure!", e);
+		}
+	}
+
+	public static void releaseResources(HttpReqRsp reqRsp) {
+		if (reqRsp != null) {
+			releaseResources(reqRsp.getReq(), reqRsp.getRsp());
+		}
+	}
+
+	public static void releaseResources(HttpRequestBase req, CloseableHttpResponse rsp) {
+		releaseReq(req);
+		releaseRsp(rsp);
+	}
+
+	public static void releaseReq(HttpRequestBase req) {
+		if (req != null) {
+			req.releaseConnection();
+		}
+	}
+
+	public static void releaseRsp(CloseableHttpResponse rsp) {
+		if (rsp != null) {
+			try {
+				rsp.close();
+			} catch (IOException e) {
+				log.warn("reqRsp.getRsp().close() error!", e);
+			}
+		}
+	}
+
+	/**
+	 * <pre>
+	 * &#64;注意自己释放请求和响应的资源@
+	 * public static void releaseReq(HttpRequestBase req)
+	 * public static void releaseRsp(CloseableHttpResponse rsp)
+	 * </pre>
+	 *
+	 * @return
+	 */
+	public static CloseableHttpClient getClient() {
+		return httpClient;
+	}
+
+	/**
+	 * 获取basic auth的header
+	 *
+	 * @param username
+	 * @param password
+	 * @return
+	 */
+	public static Header getBasicAuth(String username, String password) {
+		String auth = username + ":" + password;
+		String authHeader = "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
+		Header header = new BasicHeader("Authorization", authHeader);
+		return header;
+	}
+
+	/**
+	 * 单位为毫秒
+	 *
+	 * @param reqTimeout 请求超时
+	 * @param connTimeout 连接超时
+	 * @param sockTimeout 响应超时
+	 * @return
+	 */
+	public static RequestConfig getConf(int reqTimeout, int connTimeout, int sockTimeout) {
+		return RequestConfig.custom().setConnectionRequestTimeout(reqTimeout).setConnectTimeout(connTimeout).setSocketTimeout(sockTimeout).build();
+	}
+
+	/**
+	 * 单位为毫秒
+	 *
+	 * @param reqTimeout
+	 * @param connTimeout
+	 * @return
+	 */
+	public static RequestConfig getConf(int reqTimeout, int connTimeout) {
+		return RequestConfig.custom().setConnectionRequestTimeout(reqTimeout).setConnectTimeout(connTimeout).setSocketTimeout(socketTimeout).build();
+	}
+
+	/**
+	 * 单位是毫秒 响应超时
+	 *
+	 * @param socketTimeout
+	 * @return
+	 */
+	public static RequestConfig getSockConf(int socketTimeout) {
+		return RequestConfig.custom().setConnectionRequestTimeout(requestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
+	}
+
+	/**
+	 * 单位是毫秒 请求超时
+	 *
+	 * @param reqTimeout
+	 * @return
+	 */
+	public static RequestConfig getReqConf(int reqTimeout) {
+		return RequestConfig.custom().setConnectionRequestTimeout(reqTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
+	}
+
+	/**
+	 * 单位是毫秒 连接超时
+	 *
+	 * @param connTimeout
+	 * @return
+	 */
+	public static RequestConfig getConnConf(int connTimeout) {
+		return RequestConfig.custom().setConnectionRequestTimeout(requestTimeout).setConnectTimeout(connTimeout).setSocketTimeout(socketTimeout).build();
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @return
+	 */
+	public static String requestString(String url, List<NameValuePair> params, String methodName) {
+		return requestString(url, params, methodName, null, null, null);
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName
+	 * @param config public RequestConfig getReqConf(int reqTimeout) 通过这个可以为每个url设置独立的超时时间
+	 * @return
+	 */
+	public static String requestString(String url, List<NameValuePair> params, String methodName, RequestConfig config) {
+		return requestString(url, params, methodName, null, null, config);
+	}
+
+	public static String requestString(String url, List<NameValuePair> params, String methodName, List<Header> headers) {
+		return requestString(url, params, methodName, headers, null, null);
+	}
+
+	public static String requestString(String url, List<NameValuePair> params, String methodName, List<Header> headers, RequestConfig config) {
+		return requestString(url, params, methodName, headers, null, config);
+	}
+
+	public static String requestString(String url, List<NameValuePair> params, String methodName, List<Header> headers, String urlEncode, RequestConfig config) {
+		return requestString(url, params, methodName, headers, urlEncode, null, config);
+	}
+
+	public static String requestString(String url, List<NameValuePair> params, String methodName, List<Header> headers, String urlEncode, String entityEncode, RequestConfig config) {
+		HttpReqRsp reqRsp = request(url, params, methodName, headers, urlEncode, config);
+		try {
+			int status = reqRsp.getRsp().getStatusLine().getStatusCode();
+			if (status >= 200 && status < 300) {
+				try {
+					String encode = defaultEncode;
+					if (StringUtils.hasText(entityEncode)) {
+						encode = entityEncode;
+					}
+					return EntityUtils.toString(reqRsp.getRsp().getEntity(), encode);
+				} catch (Exception e) {
+					throw new RuntimeException(String.format("EntityUtils.toString error!%s", reqRsp.getRsp().toString()), e);
+				}
+			} else {
+				throw new RuntimeException("request Unexpected response status: " + status);
+			}
+		} finally {
+			releaseResources(reqRsp);
+		}
+
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @return
+	 */
+	public static byte[] requestBA(String url, List<NameValuePair> params, String methodName) {
+		return requestBA(url, params, methodName, null, null, null);
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName
+	 * @param config public RequestConfig getReqConf(int reqTimeout) 通过这个可以为每个url设置独立的超时时间
+	 * @return
+	 */
+	public static byte[] requestBA(String url, List<NameValuePair> params, String methodName, RequestConfig config) {
+		return requestBA(url, params, methodName, null, null, config);
+	}
+
+	public static byte[] requestBA(String url, List<NameValuePair> params, String methodName, List<Header> headers) {
+		return requestBA(url, params, methodName, headers, null, null);
+	}
+
+	public static byte[] requestBA(String url, List<NameValuePair> params, String methodName, List<Header> headers, RequestConfig config) {
+		return requestBA(url, params, methodName, headers, null, config);
+	}
+
+	public static byte[] requestBA(String url, List<NameValuePair> params, String methodName, List<Header> headers, String urlEncode, RequestConfig config) {
+		HttpReqRsp reqRsp = request(url, params, methodName, headers, urlEncode, config);
+		try {
+			int status = reqRsp.getRsp().getStatusLine().getStatusCode();
+			if (status >= 200 && status < 300) {
+				try {
+					return EntityUtils.toByteArray(reqRsp.getRsp().getEntity());
+				} catch (Exception e) {
+					throw new RuntimeException(String.format("EntityUtils.toByteArray error!%s", reqRsp.getRsp().toString()), e);
+				}
+			} else {
+				throw new RuntimeException("request Unexpected response status: " + status);
+			}
+		} finally {
+			releaseResources(reqRsp);
+		}
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @return
+	 */
+	public static HttpReqRsp request(String url, List<NameValuePair> params, String methodName) {
+		return request(url, params, methodName, null, null, null);
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @param config public RequestConfig getReqConf(int reqTimeout) 通过这个可以为每个url设置独立的超时时间
+	 * @return
+	 */
+	public static HttpReqRsp request(String url, List<NameValuePair> params, String methodName, RequestConfig config) {
+		return request(url, params, methodName, null, null, config);
+	}
+
+	/**
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @param headers
+	 * @return
+	 */
+	public static HttpReqRsp request(String url, List<NameValuePair> params, String methodName, List<Header> headers) {
+		return request(url, params, methodName, headers, null, null);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace 不区分大小写
+	 * @param headers
+	 * @param config
+	 * @return
+	 */
+	public static HttpReqRsp request(String url, List<NameValuePair> params, String methodName, List<Header> headers, RequestConfig config) {
+		return request(url, params, methodName, headers, null, config);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param params
+	 * @param methodName 支持put post get delete head patch options trace
+	 * @param headers
+	 * @param urlEncode 默认utf-8
+	 * @param config 可以设置超时时间等
+	 * @return
+	 */
+	public static HttpReqRsp request(String url, List<NameValuePair> params, String methodName, List<Header> headers, String urlEncode, RequestConfig config) {
+		CloseableHttpResponse rsp = null;
+		HttpRequestBase req = null;
+		String encode = defaultEncode;
+		if (StringUtils.hasText(urlEncode)) {
+			encode = urlEncode;
+		}
+		try {
+			String realUrl = url;
+			if (params != null && params.size() > 0) {
+				realUrl = url + "?" + EntityUtils.toString(new UrlEncodedFormEntity(params, encode));
+			}
+			if (methodName.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
+				req = new HttpGet(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
+				req = new HttpPost(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpPut.METHOD_NAME)) {
+				req = new HttpPut(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpDelete.METHOD_NAME)) {
+				req = new HttpDelete(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
+				req = new HttpHead(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpPatch.METHOD_NAME)) {
+				req = new HttpPatch(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpOptions.METHOD_NAME)) {
+				req = new HttpOptions(realUrl);
+			} else if (methodName.equalsIgnoreCase(HttpTrace.METHOD_NAME)) {
+				req = new HttpTrace(realUrl);
+			} else {
+				throw new RuntimeException("unsupport the methodName " + methodName);
+			}
+
+			if (config != null) {
+				req.setConfig(config);
+			} else {
+				req.setConfig(defaultReqConfig);
+			}
+			if (headers != null && headers.size() > 0) {
+				for (Header header : headers) {
+					req.addHeader(header);
+				}
+			}
+			rsp = httpClient.execute(req);
+			return new HttpReqRsp(req, rsp);
+		} catch (Exception e) {
+			releaseResources(req, rsp);
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param entity
+	 * @param config config 可以设置超时时间等
+	 * @return 默认post方式
+	 */
+	public static HttpReqRsp requestByEntity(String url, HttpEntity entity) {
+		return requestByEntity(url, entity, "POST", null, null);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param entity
+	 * @param config config 可以设置超时时间等
+	 * @return 默认post方式
+	 */
+	public static HttpReqRsp requestByEntity(String url, HttpEntity entity, RequestConfig config) {
+		return requestByEntity(url, entity, "POST", null, config);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param entity
+	 * @param headers
+	 * @return 默认post方式
+	 */
+	public static HttpReqRsp requestByEntity(String url, HttpEntity entity, List<Header> headers) {
+		return requestByEntity(url, entity, "POST", headers, null);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param entity
+	 * @param headers
+	 * @param config 可以设置超时时间等
+	 * @return 默认post方式
+	 */
+	public static HttpReqRsp requestByEntity(String url, HttpEntity entity, List<Header> headers, RequestConfig config) {
+		return requestByEntity(url, entity, "POST", headers, config);
+	}
+
+	/**
+	 * @注意自己释放请求和响应的资源@ public static void releaseResources(HttpReqRsp reqRsp)
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param headers
+	 * @param config 可以设置超时时间等
+	 * @return
+	 */
+	public static HttpReqRsp requestByEntity(String url, HttpEntity entity, String methodName, List<Header> headers, RequestConfig config) {
+		CloseableHttpResponse rsp = null;
+		HttpRequestBase req = null;
+		try {
+			if (methodName.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
+				HttpPost httpPost = new HttpPost(url);
+				httpPost.setEntity(entity);
+				req = httpPost;
+			} else if (methodName.equalsIgnoreCase(HttpPut.METHOD_NAME)) {
+				HttpPut httpPut = new HttpPut(url);
+				httpPut.setEntity(entity);
+				req = httpPut;
+			} else if (methodName.equalsIgnoreCase(HttpPatch.METHOD_NAME)) {
+				HttpPatch httpPatch = new HttpPatch(url);
+				httpPatch.setEntity(entity);
+				req = httpPatch;
+			} else {
+				throw new RuntimeException("unsupport the methodName " + methodName);
+			}
+			if (config != null) {
+				req.setConfig(config);
+			} else {
+				req.setConfig(defaultReqConfig);
+			}
+			if (headers != null && headers.size() > 0) {
+				for (Header header : headers) {
+					req.addHeader(header);
+				}
+			}
+			rsp = httpClient.execute(req);
+			return new HttpReqRsp(req, rsp);
+		} catch (Exception e) {
+			releaseResources(req, rsp);
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @return
+	 */
+	public static String reqStrByEntity(String url, HttpEntity entity, String methodName) {
+		return reqStrByEntity(url, entity, methodName, null, null);
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param config
+	 * @return
+	 */
+	public static String reqStrByEntity(String url, HttpEntity entity, String methodName, RequestConfig config) {
+		return reqStrByEntity(url, entity, methodName, null, null, config);
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param headers
+	 * @return
+	 */
+	public static String reqStrByEntity(String url, HttpEntity entity, String methodName, List<Header> headers) {
+		return reqStrByEntity(url, entity, methodName, headers, null, null);
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param headers
+	 * @param config
+	 * @return
+	 */
+	public static String reqStrByEntity(String url, HttpEntity entity, String methodName, List<Header> headers, RequestConfig config) {
+		return reqStrByEntity(url, entity, methodName, headers, null, config);
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param headers
+	 * @param entityEncode
+	 * @param config
+	 * @return
+	 */
+	public static String reqStrByEntity(String url, HttpEntity entity, String methodName, List<Header> headers, String entityEncode, RequestConfig config) {
+		HttpReqRsp reqRsp = requestByEntity(url, entity, methodName, headers, config);
+		try {
+			int status = reqRsp.getRsp().getStatusLine().getStatusCode();
+			if (status >= 200 && status < 300) {
+				try {
+					String encode = defaultEncode;
+					if (StringUtils.hasText(entityEncode)) {
+						encode = entityEncode;
+					}
+					return EntityUtils.toString(reqRsp.getRsp().getEntity(), encode);
+				} catch (Exception e) {
+					throw new RuntimeException(String.format("EntityUtils.toString error!%s", reqRsp.getRsp().toString()), e);
+				}
+			} else {
+				throw new RuntimeException("request Unexpected response status: " + status);
+			}
+		} finally {
+			releaseResources(reqRsp);
+		}
+
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @return
+	 */
+	public static byte[] reqBAByEntity(String url, HttpEntity entity, String methodName) {
+		return reqBAByEntity(url, entity, methodName, null, null);
+	}
+
+	public static byte[] reqBAByEntity(String url, HttpEntity entity, String methodName, List<Header> headers) {
+		return reqBAByEntity(url, entity, methodName, headers, null);
+	}
+
+	public static byte[] reqBAByEntity(String url, HttpEntity entity, String methodName, RequestConfig config) {
+		return reqBAByEntity(url, entity, methodName, null, config);
+	}
+
+	/**
+	 * @param url
+	 * @param entity
+	 * @param methodName 支持post put patch
+	 * @param headers
+	 * @param entityEncode
+	 * @param config
+	 * @return
+	 */
+	public static byte[] reqBAByEntity(String url, HttpEntity entity, String methodName, List<Header> headers, RequestConfig config) {
+		HttpReqRsp reqRsp = requestByEntity(url, entity, methodName, headers, config);
+		try {
+			int status = reqRsp.getRsp().getStatusLine().getStatusCode();
+			if (status >= 200 && status < 300) {
+				try {
+					return EntityUtils.toByteArray(reqRsp.getRsp().getEntity());
+				} catch (Exception e) {
+					throw new RuntimeException(String.format("EntityUtils.toByteArray error!%s", reqRsp.getRsp().toString()), e);
+				}
+			} else {
+				throw new RuntimeException("request Unexpected response status: " + status);
+			}
+		} finally {
+			releaseResources(reqRsp);
+		}
+	}
+}

+ 154 - 0
micro-common/src/main/java/com/crunii/micro/common/http/HttpClientUtilDemo.java

@@ -0,0 +1,154 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.http;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.Consts;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.message.BasicNameValuePair;
+import org.springframework.util.FileCopyUtils;
+
+/**
+ * @author 田平 create 2018年9月12日下午12:54:34
+ */
+public class HttpClientUtilDemo {
+	public static void main(String[] args) throws Exception {
+		 //testBA();
+		 testStr();
+		// testStr1();
+		// testTimeout();
+		// testHeader();
+		// testBodyString();
+		// testForm();
+		// testForm1();
+		// testFormFile1();
+		// testBodyParam();
+	}
+
+	/**
+	 * 测试获取二进制文件
+	 */
+	public static void testBA() throws Exception {
+		String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537337608&di=b1c391de734aa27a1c013e856c2f0f74&imgtype=jpg&er=1&src=http%3A%2F%2Fs9.sinaimg.cn%2Fmw690%2F0024zh2Bzy7cIbc0cIM68%26amp%3B690";
+		byte[] imageBA = HttpClientUtil.requestBA(url, null, "GET");
+		FileCopyUtils.copy(imageBA, new File("d:\\x.jpg"));
+	}
+
+	/**
+	 * 返回字符串
+	 */
+	public static void testStr() {
+		String url = "http://localhost:11000/TestController/getHello";
+		List<NameValuePair> params = new ArrayList<NameValuePair>();
+		params.add(new BasicNameValuePair("username", "abcd"));
+		String str = HttpClientUtil.requestString(url, params, "GET");
+		System.out.println(str);
+	}
+
+	/**
+	 * 添加url参数
+	 */
+	public static void testStr1() {
+		String url = "http://localhost:10001/TestController/getValue6";
+		List<NameValuePair> params = new ArrayList<NameValuePair>();
+		params.add(new BasicNameValuePair("username", "abcd"));
+		params.add(new BasicNameValuePair("password", "中国"));
+		String str = HttpClientUtil.requestString(url, params, "POST");
+		System.out.println(str);
+	}
+
+	/**
+	 * 设置超时时间
+	 */
+	public static void testTimeout() {
+		String url = "http://localhost:10001/TestController/getValue1";
+		String str = HttpClientUtil.requestString(url, null, "GET", HttpClientUtil.getSockConf(2000));
+		System.out.println(str);
+	}
+
+	/**
+	 * 通过body传字符串
+	 */
+	public static void testBodyString() {
+		StringEntity entity = new StringEntity("abcdefg中国", Consts.UTF_8);
+		String url = "http://localhost:10001/TestController/getValue9";
+		String str = HttpClientUtil.reqStrByEntity(url, entity, "POST");
+		System.out.println(str);
+	}
+
+	/**
+	 * 添加basic auth header
+	 */
+	public static void testHeader() {
+		List<Header> headers = new ArrayList<Header>();
+		headers.add(HttpClientUtil.getBasicAuth("micro", "micro,."));
+		String url = "http://localhost:8888/gateway/local";
+		String str = HttpClientUtil.requestString(url, null, "GET", headers);
+		System.out.println(str);
+	}
+
+	/**
+	 * form方式提交
+	 */
+	public static void testForm() {
+		MultipartEntityBuilder mb = MultipartEntityBuilder.create();
+		mb.addTextBody("username", "1234", ContentType.create("text/plain", Consts.UTF_8));
+		mb.addTextBody("password", "中国", ContentType.create("text/plain", Consts.UTF_8));
+		String url = "http://localhost:10001/TestController/getValue6";
+		String str = HttpClientUtil.reqStrByEntity(url, mb.build(), "POST");
+		System.out.println(str);
+	}
+
+	/**
+	 * form方式提交(对象接收参数)
+	 */
+	public static void testForm1() {
+		MultipartEntityBuilder mb = MultipartEntityBuilder.create();
+		mb.addTextBody("username", "abcd", ContentType.create("text/plain", Consts.UTF_8));
+		mb.addTextBody("password", "中国", ContentType.create("text/plain", Consts.UTF_8));
+		String url = "http://localhost:10001/TestController/getValue8";
+		String str = HttpClientUtil.reqStrByEntity(url, mb.build(), "POST");
+		System.out.println(str);
+	}
+
+	/**
+	 * 通过form方式提交,上传文件
+	 */
+	public static void testFormFile1() {
+		MultipartEntityBuilder mb = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE).setCharset(Consts.UTF_8);
+		mb.addTextBody("username", "abcdFF", ContentType.APPLICATION_JSON);
+		mb.addTextBody("password", "1234RRRRRRRRRRR!@#$%^&*()_中国", ContentType.APPLICATION_JSON);
+		mb.addPart("file", new FileBody(new File("E:/baidu-cloud/Desk-Files/保险.txt")));
+		mb.addPart("file", new FileBody(new File("E:/baidu-cloud/Desk-Files/铁塔.sql")));
+		String url = "http://localhost:10001/TestController/getValue7";
+		String str = HttpClientUtil.reqStrByEntity(url, mb.build(), "POST");
+		System.out.println(str);
+	}
+
+	/**
+	 * 通过body传参数
+	 */
+	public static void testBodyParam() {
+		String url = "http://localhost:10001/TestController/getValue10";
+		List<NameValuePair> params = new ArrayList<NameValuePair>();
+		params.add(new BasicNameValuePair("username", "abcd"));
+		params.add(new BasicNameValuePair("password", "中国人"));
+		HttpEntity entity = new UrlEncodedFormEntity(params, Consts.UTF_8);
+		String str = HttpClientUtil.reqStrByEntity(url, entity, "POST");
+		System.out.println(str);
+
+	}
+
+}

+ 53 - 0
micro-common/src/main/java/com/crunii/micro/common/http/HttpReqRsp.java

@@ -0,0 +1,53 @@
+/**
+ * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
+ */
+package com.crunii.micro.common.http;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * @author 田平 create 2018年9月12日下午2:40:34
+ */
+public class HttpReqRsp {
+	private HttpRequestBase req;
+	private CloseableHttpResponse rsp;
+
+	public HttpReqRsp() {
+
+	}
+
+	public HttpReqRsp(HttpRequestBase req, CloseableHttpResponse rsp) {
+		this.req = req;
+		this.rsp = rsp;
+	}
+
+	/**
+	 * @return the req
+	 */
+	public HttpRequestBase getReq() {
+		return req;
+	}
+
+	/**
+	 * @param req the req to set
+	 */
+	public void setReq(HttpRequestBase req) {
+		this.req = req;
+	}
+
+	/**
+	 * @return the rsp
+	 */
+	public CloseableHttpResponse getRsp() {
+		return rsp;
+	}
+
+	/**
+	 * @param rsp the rsp to set
+	 */
+	public void setRsp(CloseableHttpResponse rsp) {
+		this.rsp = rsp;
+	}
+
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов