liuchuan vor 10 Monaten
Commit
fc9cb02026
100 geänderte Dateien mit 6434 neuen und 0 gelöschten Zeilen
  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. 4 0
      interfaces/intf-http-demo/package.bat
  10. 72 0
      interfaces/intf-http-demo/pom.xml
  11. 57 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/Application.java
  12. 11 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/HttpDemoConstants.java
  13. 23 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/config/TemplateConfiguration.java
  14. 56 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/FeignClientTestController.java
  15. 50 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/IntfHttpDemoController.java
  16. 35 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/YkzOrganizationController.java
  17. 50 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/YkzUserInformationController.java
  18. 22 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/Weather.java
  19. 33 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/WeatherInfo.java
  20. 16 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/YkzUserLoginDto.java
  21. 26 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/jdbc/YkzObtainUserInformationJdbc.java
  22. 68 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/job/YkzJob.java
  23. 11 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/AccessTokenService.java
  24. 20 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/DemoService.java
  25. 15 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/HttpLogService.java
  26. 12 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/YkzOrganizationService.java
  27. 16 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/YkzUserInformationService.java
  28. 64 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/AccessTokenServiceImpl.java
  29. 23 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/DemoServiceImpl.java
  30. 74 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/HttpLogServiceImpl.java
  31. 229 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/YkzOrganizationServiceImpl.java
  32. 240 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/YkzUserInformationServiceImpl.java
  33. 52 0
      interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/utils/Sm2Util.java
  34. 145 0
      interfaces/intf-http-demo/src/main/resources/application.yml
  35. 12 0
      interfaces/intf-http-demo/src/main/resources/ftls/salerQuery.ftl
  36. 11 0
      interfaces/intf-http-demo/src/main/resources/ftls/tempTest.ftl
  37. BIN
      interfaces/intf-http-demo/src/main/resources/jwt.sm2.cer
  38. 145 0
      interfaces/intf-http-demo/src/main/resources/logback-spring.xml
  39. 16 0
      interfaces/intf-http-demo/src/main/resources/spring/ctx.xml
  40. 94 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/AgentSalesInfo.java
  41. 22 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/BaseTestHttpDemo.java
  42. 21 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/BsnRspTest.java
  43. 27 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/ChannelQuerySaleInfoResp.java
  44. 48 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/TempTest.java
  45. 44 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/TempTest1.java
  46. 49 0
      interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/service/TemplateTest.java
  47. 132 0
      interfaces/intf-http-demo/src/test/resources/logback-spring-test.xml
  48. 84 0
      interfaces/pom.xml
  49. 42 0
      micro-common-component/micro-common-excel/pom.xml
  50. 25 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/annotation/CellMerge.java
  51. 30 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/annotation/ExcelDictFormat.java
  52. 52 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelBigNumberConvert.java
  53. 52 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelDateConverter.java
  54. 76 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelDictConvert.java
  55. 114 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/CellMergeStrategy.java
  56. 106 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/DefaultExcelListener.java
  57. 81 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/DefautExcelResult.java
  58. 14 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/ExcelListener.java
  59. 30 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/ExcelResult.java
  60. 26 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelMultipleSheetsWriteData.java
  61. 37 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelSheetData.java
  62. 22 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelWriteData.java
  63. 450 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/utils/ExcelUtil.java
  64. 54 0
      micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/utils/FileUtils.java
  65. 1 0
      micro-common-component/micro-common-excel/src/main/resources/META-INF/spring.factories
  66. 51 0
      micro-common-component/micro-common-swagger/pom.xml
  67. 129 0
      micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/SwaggerAutoConfiguration.java
  68. 41 0
      micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/SwaggerBeanPostProcessor.java
  69. 143 0
      micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/properties/SwaggerProperties.java
  70. 3 0
      micro-common-component/micro-common-swagger/src/main/resources/META-INF/spring.factories
  71. 44 0
      micro-common-component/micro-common-web/pom.xml
  72. 24 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/I18nConfig.java
  73. 38 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/NacosConfig.java
  74. 30 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/config/UndertowConfig.java
  75. 31 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/core/I18nLocaleResolver.java
  76. 25 0
      micro-common-component/micro-common-web/src/main/java/com/crunii/micro/common/web/nacos/CustomNacosWatch.java
  77. 4 0
      micro-common-component/micro-common-web/src/main/resources/META-INF/spring.factories
  78. 19 0
      micro-common-component/pom.xml
  79. 378 0
      micro-common/pom.xml
  80. 54 0
      micro-common/src/main/java/com/crunii/micro/common/annotations/LimitUploadFileTypeCheck.java
  81. 176 0
      micro-common/src/main/java/com/crunii/micro/common/captcha/MicroCaptcha.java
  82. 28 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/BlockingRejectedExecutionHandler.java
  83. 46 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityCallable.java
  84. 123 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityExecutor.java
  85. 37 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityRunnable.java
  86. 34 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/PriorityTask.java
  87. 33 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeList.java
  88. 34 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/FixSizeSortedSet.java
  89. 17 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/RemoveId.java
  90. 77 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeLinkedList.java
  91. 153 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/FixSizeTreeSet.java
  92. 48 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/collection/impl/SimpleRemoveId.java
  93. 81 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMap.java
  94. 176 0
      micro-common/src/main/java/com/crunii/micro/common/concurrent/map/MultiValueMapImpl.java
  95. 42 0
      micro-common/src/main/java/com/crunii/micro/common/config/CommConfiguration.java
  96. 41 0
      micro-common/src/main/java/com/crunii/micro/common/config/LocalDateTimeConverter.java
  97. 24 0
      micro-common/src/main/java/com/crunii/micro/common/config/NacosConfiguration.java
  98. 176 0
      micro-common/src/main/java/com/crunii/micro/common/config/RestTemplateConfiguration.java
  99. 42 0
      micro-common/src/main/java/com/crunii/micro/common/config/StartTimeConfiguration.java
  100. 182 0
      micro-common/src/main/java/com/crunii/micro/common/config/SwaggerConfiguration.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>

+ 4 - 0
interfaces/intf-http-demo/package.bat

@@ -0,0 +1,4 @@
+@echo off
+cd ../../
+call mvn clean package -Dmaven.test.skip=true -pl interfaces\intf-http-demo -am
+@pause

+ 72 - 0
interfaces/intf-http-demo/pom.xml

@@ -0,0 +1,72 @@
+<?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>interfaces</artifactId>
+        <version>${micro.version}</version>
+    </parent>
+
+    <artifactId>intf-http-demo</artifactId>
+    <packaging>jar</packaging>
+
+    <properties>
+        <start-class>com.crunii.micro.intf.httpdemo.Application</start-class>
+    </properties>
+    <dependencies>
+
+        <!--  用户中心对接SDK      -->
+        <dependency>
+            <groupId>com.dcqc</groupId>
+            <artifactId>dcqc-uc-oauth-sdk</artifactId>
+            <version>3.0.0-RELEASE</version>
+        </dependency>
+        <!-- jwt -->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.11.0</version>
+        </dependency>
+        <!-- bc库,BouncyCastle是一款开源的密码包,其中包含了大量的密码算法,使用BouncyCastle的目的就是为了扩充算法支持
+        -->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <!-- fastjson -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.73</version>
+        </dependency>
+        <!--政务钉钉 start    -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>zwdd-sdk-java</artifactId>
+            <version>1.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.10</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+        <!--政务钉钉 end    -->
+    </dependencies>
+</project>

+ 57 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/Application.java

@@ -0,0 +1,57 @@
+package com.crunii.micro.intf.httpdemo;
+
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
+import com.crunii.micro.common.utils.JasyptEncryptorUtil;
+import com.crunii.micro.common.utils.LogPathUtil;
+import com.crunii.micro.config.ClientVersionRibbonConfiguration;
+import com.crunii.micro.feign.client.MicroCoreClient;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.EnableMBeanExport;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.jmx.support.RegistrationPolicy;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author 田平 create 2023-02-01 03:49:56
+ */
+@ImportResource({ "classpath:spring/*.xml" })
+@EnableFeignClients(basePackages = {"com.crunii.micro.feign"})
+@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
+@EnableDiscoveryClient
+@EnableCaching
+@SpringBootApplication(exclude = {
+		RedisAutoConfiguration.class,
+		RedisReactiveAutoConfiguration.class,
+		TransactionAutoConfiguration.class,
+		DataSourceAutoConfiguration.class,
+		DruidDataSourceAutoConfigure.class })
+@LoadBalancerClients(defaultConfiguration = ClientVersionRibbonConfiguration.class)
+public class Application {
+
+	public static void main(String[] args) {
+		JasyptEncryptorUtil.setConfigClientProps();
+		JasyptEncryptorUtil.setAdminClientProps();
+		// 接netty重复设置的问题
+		System.setProperty("es.set.netty.runtime.available.processors", "false");
+
+		String env = System.getProperty("spring.profiles.active");
+		if (!StringUtils.hasText(env)) {
+			// local or dev or prod
+			env = "dev";
+			System.setProperty("spring.profiles.active", env);
+			// 设置nacos的日志目录
+			System.setProperty("nacos.logging.path", LogPathUtil.getNacosLogPath());
+		}
+		SpringApplication.run(Application.class, args);
+	}
+
+}

+ 11 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/HttpDemoConstants.java

@@ -0,0 +1,11 @@
+/**
+ * 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.intf.httpdemo;
+
+/**
+ * @author 田平 create 2019年6月26日下午3:33:09
+ */
+public class HttpDemoConstants {
+
+}

+ 23 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/config/TemplateConfiguration.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.intf.httpdemo.config;
+
+import com.crunii.micro.common.template.FreemarkerTempConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author 田平 create 2022-12-27 12:24:54
+ */
+@Configuration
+public class TemplateConfiguration {
+
+	@Bean("freemarkerTempConfiguration")
+	public FreemarkerTempConfiguration regFreemarkerTempConfiguration() {
+		FreemarkerTempConfiguration conf = new FreemarkerTempConfiguration();
+		conf.setResourcePath("classpath:ftls/*");
+		return conf;
+	}
+}

+ 56 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/FeignClientTestController.java

@@ -0,0 +1,56 @@
+/**
+ * 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.intf.httpdemo.controller;
+
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.feign.client.MicroCoreClient;
+import feign.Request;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author 田平 create 2019年12月24日下午4:38:52
+ */
+@RestController
+@RequestMapping("/intf/FeignClientTestController")
+@Slf4j
+public class FeignClientTestController {
+
+	@Autowired
+	private MicroCoreClient microCoreClient;
+
+	@GetMapping("/testFeignClient")
+	@ResponseBody
+	public Result<String> testFeignClient() {
+		return microCoreClient.testFeignClient();
+	}
+
+	@GetMapping("/testFeignClientCustomTimeout")
+	@ResponseBody
+	public Result<String> testFeignClientCustomTimeout() {
+		//单独设置连接超时时间和读取超时时间(响应)
+		Request.Options options = new Request.Options(10, TimeUnit.SECONDS,
+				1, TimeUnit.SECONDS, true);
+		return microCoreClient.testFeignClientCustomTimeout(options);
+	}
+
+	@GetMapping("/testTimeout")
+	@ResponseBody
+	public Result<String> testTimeout() {
+		return microCoreClient.testTimeout();
+	}
+
+	@GetMapping("/testBreaker")
+	@ResponseBody
+	public Result<String> testBreaker() {
+		return microCoreClient.testBreaker();
+	}
+
+}

+ 50 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/IntfHttpDemoController.java

@@ -0,0 +1,50 @@
+/**
+ * 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.intf.httpdemo.controller;
+
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.common.dto.UserTest;
+import com.crunii.micro.intf.httpdemo.service.DemoService;
+import com.crunii.micro.intf.httpdemo.service.HttpLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author 田平 create 2019年12月24日下午4:38:52
+ */
+@RestController
+@RequestMapping("/intf/IntfHttpDemoController")
+@Slf4j
+public class IntfHttpDemoController {
+
+	@Autowired
+	private HttpLogService httpLogService;
+
+	@Autowired
+	private DemoService demoService;
+
+	@PostMapping("/getUserTest")
+	@ResponseBody
+	public UserTest getUserTest(@RequestBody UserTest user) {
+		log.info("{}", user);
+		return demoService.getUserTest(user);
+	}
+
+	@GetMapping("/getUser")
+	@ResponseBody
+	public UserTest getUser(String username, String password) {
+		//int a=1/0;
+		UserTest user = new UserTest();
+		user.setUsername(username);
+		user.setPassword(password);
+		return user;
+	}
+
+	@GetMapping("/httpLogDemo")
+	@ResponseBody
+	public Result<String> httpLogDemo() {
+		return httpLogService.httpLogDemo();
+	}
+}

+ 35 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/YkzOrganizationController.java

@@ -0,0 +1,35 @@
+/**
+ * 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.intf.httpdemo.controller;
+
+import com.crunii.micro.domain.entity.main.YkzOrganization;
+import com.crunii.micro.intf.httpdemo.service.YkzOrganizationService;
+import com.dcqc.uc.oauth.sdk.model.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+
+/**
+ * @author 田平 create 2019年12月24日下午4:38:52
+ */
+@RestController
+@RequestMapping("/intf/YkzOrganizationController")
+@Slf4j
+public class YkzOrganizationController {
+
+	@Autowired
+	private YkzOrganizationService ykzOrganizationService;
+
+	@GetMapping("/getAllYkzOrganizations")
+	@ResponseBody
+	public Result<List<YkzOrganization>> saveAllYkzOrganizations() {
+		return ykzOrganizationService.saveAllYkzOrganizations(null);
+	}
+}

+ 50 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/controller/YkzUserInformationController.java

@@ -0,0 +1,50 @@
+package com.crunii.micro.intf.httpdemo.controller;
+
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.domain.dto.Worker;
+import com.crunii.micro.feign.client.dto.LoginPhoneDto;
+import com.crunii.micro.intf.httpdemo.service.YkzUserInformationService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("/intf/YkzObtainUserInformationController")
+@Slf4j
+public class YkzUserInformationController {
+    @Value("${spring.application.name}")
+    private String appName;
+
+    @Autowired
+    private YkzUserInformationService ykzUserInformationService;
+
+    @ApiOperation(value = "愉快政免密登录")
+    @RequestMapping(path = "/ykzFreeLogin", method = RequestMethod.POST)
+    @ResponseBody
+    public com.crunii.micro.common.dto.Result<Worker> ykzFreeLogin(@RequestBody LoginPhoneDto loginPhoneDto, HttpServletRequest request) throws IOException {
+        // 当前调用的url
+        String callUrl = "/" + appName + request.getRequestURI();
+        return com.crunii.micro.common.dto.Result.ofSuccess(ykzUserInformationService.ykzFreeLogin(request,loginPhoneDto,callUrl,false));
+    }
+
+    @ApiOperation(value = "获取渝快政用户信息")
+    @RequestMapping(path = "/getYkzUserInfo", method = RequestMethod.POST)
+    @ResponseBody
+    public Result<String> getYkzUserInfo() {
+        this.ykzUserInformationService.getYkzUserInfo();
+        return new Result<>();
+    }
+
+    @ApiOperation(value = "刷新渝快政用户信息")
+    @RequestMapping(path = "/saveYkzUserInfo", method = RequestMethod.POST)
+    @ResponseBody
+    public Result<String> saveYkzUserInfo(@RequestBody LoginPhoneDto loginPhoneDto) {
+        return ykzUserInformationService.saveYkzUserInfo(loginPhoneDto);
+    }
+
+}

+ 22 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/Weather.java

@@ -0,0 +1,22 @@
+/**
+ * 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.intf.httpdemo.dto;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2020年1月6日下午1:51:56
+ *
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "天气")
+public class Weather {
+
+	private WeatherInfo weatherinfo;
+}

+ 33 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/WeatherInfo.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.intf.httpdemo.dto;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author 田平 create 2020年1月6日下午1:51:56
+ *
+ */
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "天气信息")
+public class WeatherInfo {
+	private String city;
+	private String cityid;
+	private String temp;
+	private String WD;
+	private String WS;
+	private String SD;
+	private String AP;
+	private String njd;
+	private String WSE;
+	private String time;
+	private String sm;
+	private String isRadar;
+	private String Radar;
+}

+ 16 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/dto/YkzUserLoginDto.java

@@ -0,0 +1,16 @@
+package com.crunii.micro.intf.httpdemo.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "渝快政免密登录")
+public class YkzUserLoginDto {
+    @ApiModelProperty(value = "临时授权码")
+    private String authCode;
+}

+ 26 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/jdbc/YkzObtainUserInformationJdbc.java

@@ -0,0 +1,26 @@
+package com.crunii.micro.intf.httpdemo.jdbc;
+
+import com.crunii.micro.domain.common.jdbc.BaseJdbcTemplate;
+import com.crunii.micro.domain.entity.main.TsmWorker;
+import org.springframework.stereotype.Repository;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Repository
+public class YkzObtainUserInformationJdbc {
+    @Resource(name = "mainJT")
+    protected BaseJdbcTemplate jt;
+    public List<TsmWorker> workerList() {
+        String sql = " select * from tsm_worker where state =1 ";
+        return jt.queryForBeans(TsmWorker.class, sql);
+    }
+    public TsmWorker worker(String mobile) {
+        String sql = " select * from tsm_worker where state =1 and mobile ="+mobile;
+        return jt.queryForBean(TsmWorker.class, sql);
+    }
+    public void updateAccountId(int loginId, Long accountId) {
+        String sql = " update tsm_worker set account_id = "+accountId+" where login_id = "+loginId+"  ";
+        jt.update(sql);
+    }
+}

+ 68 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/job/YkzJob.java

@@ -0,0 +1,68 @@
+package com.crunii.micro.intf.httpdemo.job;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.crunii.micro.domain.entity.main.TsmWorker;
+import com.crunii.micro.intf.httpdemo.jdbc.YkzObtainUserInformationJdbc;
+import com.crunii.micro.intf.httpdemo.service.AccessTokenService;
+import com.dcqc.uc.oauth.sdk.enums.ResultCodeEnum;
+import com.dcqc.uc.oauth.sdk.model.Result;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Log4j2
+@Component
+public class YkzJob {
+    @Resource
+    private YkzObtainUserInformationJdbc ykzObtainUserInformationJdbc;
+    @Autowired
+    private AccessTokenService accessTokenService;
+    @Value("${uc.api.url}")
+    private String ucApiUrl;
+    @Scheduled(cron = "0 57 15 * * ?")
+    public Result YkzObtainUserInformation() {
+        List<TsmWorker> workers = ykzObtainUserInformationJdbc.workerList();
+        Result accessTokens = accessTokenService.getAccessToken();
+        log.info("accessTokens:"+accessTokens);
+        if (accessTokens.getStatus() != 0) {
+            return accessTokens;
+        }
+        String accessToken = (String) accessTokens.getData();
+        String apiUrl = ucApiUrl + "/ykz/user/getByMobile";
+        if(workers.size()>0){
+            for(TsmWorker worker : workers){
+                try {
+                    JSONObject requestBody = new JSONObject()
+                            .fluentPut("data", new JSONObject()
+                                    .fluentPut("mobile", worker.getMobile())
+                            )
+                            .fluentPut("requestId", StrUtil.uuid());
+                    String resp = HttpRequest.post(apiUrl)
+                            .header("Content-Type", "application/json")
+                            .header("Authorization", "Bearer " + accessToken)
+                            .body(requestBody.toJSONString())
+                            .timeout(5000)
+                            .execute()
+                            .body();
+                    cn.hutool.json.JSONObject jsonResponse = JSONUtil.parseObj(resp);
+                    log.info("JSONObject参数:"+jsonResponse);
+                    if (jsonResponse.getBool("success")) {
+
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return new Result<>(500, e.toString());
+                }
+            }
+        }
+        return new Result<>(ResultCodeEnum.SUCCESS);
+    }
+}

+ 11 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/AccessTokenService.java

@@ -0,0 +1,11 @@
+package com.crunii.micro.intf.httpdemo.service;
+
+import com.dcqc.uc.oauth.sdk.model.Result;
+
+/**
+ * @author lmlkyc
+ * @date 2023-10-06 17:37
+ */
+public interface AccessTokenService {
+	Result getAccessToken();
+}

+ 20 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/DemoService.java

@@ -0,0 +1,20 @@
+/**
+ * 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.intf.httpdemo.service;
+
+import org.springframework.web.server.ServerWebExchange;
+
+import com.crunii.micro.common.dto.UserTest;
+
+import reactor.core.publisher.Mono;
+
+/**
+ * @author 田平 create 2020年1月15日下午5:18:13
+ *
+ */
+public interface DemoService {
+
+	public UserTest getUserTest(UserTest user) ;
+}

+ 15 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/HttpLogService.java

@@ -0,0 +1,15 @@
+/**
+ * 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.intf.httpdemo.service;
+
+import com.crunii.micro.common.dto.Result;
+
+/**
+ * @author 田平 create 2020年1月6日下午1:56:03
+ */
+public interface HttpLogService {
+
+	public Result<String> httpLogDemo();
+
+}

+ 12 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/YkzOrganizationService.java

@@ -0,0 +1,12 @@
+package com.crunii.micro.intf.httpdemo.service;
+
+import com.crunii.micro.domain.entity.main.YkzOrganization;
+import com.dcqc.uc.oauth.sdk.model.Result;
+
+import java.util.List;
+
+public interface YkzOrganizationService {
+  Result<List<YkzOrganization>> saveAllYkzOrganizations(String parentOrganizationCode);
+}
+
+

+ 16 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/YkzUserInformationService.java

@@ -0,0 +1,16 @@
+package com.crunii.micro.intf.httpdemo.service;
+
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.domain.dto.Worker;
+import com.crunii.micro.feign.client.dto.LoginPhoneDto;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface YkzUserInformationService {
+    Worker ykzFreeLogin(HttpServletRequest request, LoginPhoneDto loginPhoneDto, String callUrl, boolean b) throws JsonProcessingException;
+
+    void getYkzUserInfo();
+
+    Result<String> saveYkzUserInfo(LoginPhoneDto loginPhoneDto);
+}

+ 64 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/AccessTokenServiceImpl.java

@@ -0,0 +1,64 @@
+package com.crunii.micro.intf.httpdemo.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+
+
+import com.crunii.micro.intf.httpdemo.service.AccessTokenService;
+
+import com.dcqc.uc.oauth.sdk.model.Result;
+import com.dcqc.uc.oauth.sdk.util.JwtHelper;
+import com.dcqc.uc.oauth.sdk.util.SM2ToolUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author lmlkyc
+ * @date 2023-10-06 17:37
+ */
+@Service
+public class AccessTokenServiceImpl implements AccessTokenService {
+	@Autowired
+	private JwtHelper jwtHelper;
+
+	@Value("${uc.api.url}")
+	private String ucApiUrl;
+
+	@Value("${uc.api.appId}")
+	private String appId;
+
+	@Value("${uc.api.secret}")
+	private String appSecret;
+
+
+	@Override
+	public Result getAccessToken() {
+		try {
+		// 将appSecret使用SM2加密
+		String appSecretEncode = SM2ToolUtil.sm2Encode(jwtHelper.getPublicKey(), appSecret);
+		JSONObject jsonObject = new JSONObject()
+				.fluentPut("data", new JSONObject()
+						.fluentPut("appId", appId)
+						.fluentPut("appSecret", appSecretEncode)
+				).fluentPut("requestId", StrUtil.uuid());
+		String resp = HttpUtil.post(ucApiUrl+"/ykz/access_token", jsonObject.toJSONString());
+		cn.hutool.json.JSONObject entries = JSONUtil.parseObj(resp);
+		boolean success = entries.getBool("success");
+		String message = entries.getStr("message");
+		if (success) {
+			cn.hutool.json.JSONObject data = entries.getJSONObject("data");
+			String accessToken = data.getStr("accessToken");
+			return new Result(accessToken,0,message);
+		} else {
+			return new Result(40045,message);
+		}
+	}catch (Exception e) {
+			e.printStackTrace();
+			return new Result(500,e.toString());
+		}
+	}
+}

+ 23 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/DemoServiceImpl.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.intf.httpdemo.service.impl;
+
+import com.crunii.micro.common.dto.UserTest;
+import com.crunii.micro.intf.httpdemo.service.DemoService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 田平 create 2020年1月15日下午5:18:24
+ */
+@Service
+public class DemoServiceImpl implements DemoService {
+
+	@Override
+	public UserTest getUserTest(UserTest user) {
+		UserTest test = new UserTest();
+		test.setUsername(user.getUsername());
+		test.setPassword("中国1231231");
+		return test;
+	}
+}

+ 74 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/HttpLogServiceImpl.java

@@ -0,0 +1,74 @@
+/**
+ * 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.intf.httpdemo.service.impl;
+
+import com.crunii.micro.common.constants.LogConstants;
+import com.crunii.micro.common.dto.Result;
+import com.crunii.micro.common.http.HttpClientHolder;
+import com.crunii.micro.common.spring.rest.RestBuilder;
+import com.crunii.micro.common.utils.JsonUtil;
+import com.crunii.micro.common.utils.NanoIdUtil;
+import com.crunii.micro.intf.httpdemo.service.HttpLogService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2020年1月6日下午1:56:54
+ */
+@Service
+public class HttpLogServiceImpl implements HttpLogService {
+
+	@Value("${core.addr:http://81.68.133.219:9345/service-sysmgr/TestController/testLog}")
+	private String testAddress;
+
+	/**
+	 * 注意使用记录日志的RestTemplate对应的bean名称
+	 * 具体参见
+	 *
+	 * @see com.crunii.micro.common.config.RestTemplateConfiguration
+	 */
+	@Resource(name = "logRestTemplate")
+	private RestTemplate restTemplate;
+
+	@Override
+	public Result<String> httpLogDemo() {
+		//设置日志管理信息
+		Map<String, Object> busiInfo = HttpClientHolder.getBusiInfoAndClean();
+		busiInfo.put(LogConstants.LOGIN_ID, 9998);
+		busiInfo.put(LogConstants.LOGIN_CODE, "tianping");
+		busiInfo.put(LogConstants.WORKER_NAME, "田平");
+		//有时候一个业务方法会反复调用多次外围的http接口,可以这种多次调用的tradeNo一样,方便查询
+		busiInfo.put(LogConstants.TRADE_NO, NanoIdUtil.randomLowerNanoId());
+		busiInfo.put(LogConstants.BUSI_TYPE, "Test");
+		busiInfo.put(LogConstants.BUSI_NO, "Abc123");
+		//通过tradeNo|busiType|busiNo 到t_busi_log查询http调用日志
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_JSON);
+		//设置接收格式,也可以为xml MediaType.APPLICATION_XML
+		headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
+		HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
+		ParameterizedTypeReference<Result<String>> type = new ParameterizedTypeReference<Result<String>>() {};
+		ResponseEntity<Result<String>> exchange = restTemplate.exchange(testAddress, HttpMethod.GET, requestEntity, type);
+		return exchange.getBody();
+	}
+
+	public static void main(String[] args) {
+		String url = "http://localhost:12000/intf/IntfHttpDemoController/httpLogDemo";
+		RestTemplate restTemplate = RestBuilder.buildRestTemplate();
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_JSON);
+		headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
+		HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
+		ParameterizedTypeReference<Result<String>> type = new ParameterizedTypeReference<Result<String>>() {};
+		ResponseEntity<Result<String>> exchange = restTemplate.exchange(url, HttpMethod.GET, requestEntity, type);
+		System.out.println(JsonUtil.getPrettyJsonString(exchange.getBody()));
+	}
+}

+ 229 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/YkzOrganizationServiceImpl.java

@@ -0,0 +1,229 @@
+package com.crunii.micro.intf.httpdemo.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.crunii.micro.domain.entity.main.TsmOrg;
+import com.crunii.micro.domain.entity.main.YkzOrganization;
+import com.crunii.micro.domain.repo.main.TsmOrgRepo;
+import com.crunii.micro.domain.repo.main.YcqOrganizationRepo;
+import com.crunii.micro.intf.httpdemo.service.AccessTokenService;
+import com.crunii.micro.intf.httpdemo.service.YkzOrganizationService;
+import com.dcqc.uc.oauth.sdk.enums.ResultCodeEnum;
+import com.dcqc.uc.oauth.sdk.model.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author lmlkyc
+ * @date 2023-10-06 16:03
+ */
+@Service
+@Slf4j
+public class YkzOrganizationServiceImpl implements YkzOrganizationService {
+
+	@Autowired
+	private AccessTokenService accessTokenService;
+
+	@Autowired
+	private YcqOrganizationRepo ycqOrganizationRepo;
+
+	@Autowired
+	private TsmOrgRepo tsmOrgRepo;
+
+	@Value("${uc.api.url}")
+	private String ucApiUrl;
+
+	@Value("${uc.api.defaultOrganizationCode}")
+	private String defaultOrganizationCode;
+
+	@Override public Result<List<YkzOrganization>> saveAllYkzOrganizations(String parentOrganizationCode) {
+		List<YkzOrganization> allOrganizations = new ArrayList<>();
+		int pageNumber = 1;
+		int pageSize = 100;
+		//每次获取全量
+		ycqOrganizationRepo.deleteAll();
+		//修改tsm_org 愉快政为state=0
+		tsmOrgRepo.updateStateForDataSourcesYkz();
+		Result result = fetchOrganizationsRecursively(parentOrganizationCode, allOrganizations, pageNumber, pageSize);
+		if (result.getStatus().equals(ResultCodeEnum.SUCCESS.getCode())){
+			return SendTsmOrg((List<YkzOrganization>)result.getData());
+		}
+		return result;
+	}
+
+	private Result fetchOrganizationsRecursively(String parentOrganizationCode,List<YkzOrganization> allOrganizations,int pageNumber, int pageSize) {
+//		allOrganizations.clear();
+		while (true) {
+		String apiUrl = ucApiUrl + "/ykz/org/pageByParentOrganizationCode";
+		String organizationCode = parentOrganizationCode != null ? parentOrganizationCode : defaultOrganizationCode;
+			JSONObject requestBody = new JSONObject()
+					.fluentPut("data", new JSONObject()
+							.fluentPut("pageNumber", pageNumber)
+							.fluentPut("organizationCode", organizationCode)
+							.fluentPut("pageSize", pageSize)
+					)
+					.fluentPut("requestId", StrUtil.uuid());
+
+			Result accessTokens = accessTokenService.getAccessToken();
+			log.info("accessTokens:"+accessTokens);
+			if (accessTokens.getStatus() != 0) {
+				return accessTokens;
+			}
+			String accessToken = (String) accessTokens.getData();
+
+			try {
+				String resp = HttpRequest.post(apiUrl)
+						.header("Content-Type", "application/json")
+						.header("Authorization", "Bearer " + accessToken)
+						.body(requestBody.toJSONString())
+						.timeout(5000)
+						.execute()
+						.body();
+				cn.hutool.json.JSONObject jsonResponse = JSONUtil.parseObj(resp);
+				log.info("JSONObject参数:"+jsonResponse);
+				if (jsonResponse.getBool("success")) {
+					cn.hutool.json.JSONObject data = jsonResponse.getJSONObject("data");
+					JSONArray organizationList = data.getJSONArray("list");
+
+					for (int i = 0; i < organizationList.size(); i++) {
+						cn.hutool.json.JSONObject orgData = organizationList.getJSONObject(i);
+						YkzOrganization organization = new YkzOrganization();
+						organization.setName(orgData.getStr("name"));
+						organization.setOrganizationCode(orgData.getStr("organizationCode"));
+						organization.setCreateTime(orgData.getLong("createTime"));
+						organization.setCreditCode(orgData.getStr("creditCode"));
+						organization.setDisplayOrder(orgData.getLong("displayOrder"));
+						organization.setGovAddress(orgData.getStr("govAddress"));
+						organization.setGovBusinessStripCodes(orgData.getStr("govBusinessStripCodes"));
+						organization.setGovDivisionCode(orgData.getStr("govDivisionCode"));
+						organization.setGovInstitutionLevelCode(orgData.getStr("govInstitutionLevelCode"));
+						organization.setGovShortName(orgData.getStr("govShortName"));
+						organization.setOrganizationId(orgData.getLong("id"));
+						organization.setIsDeleted(orgData.getInt("isDeleted"));
+						organization.setIsEnable(orgData.getInt("isEnable"));
+						organization.setOrgType(orgData.getStr("orgType"));
+						organization.setParentId(orgData.getLong("parentId"));
+						organization.setParentOrganizationCode(orgData.getStr("parentOrganizationCode"));
+						organization.setPrincipal(orgData.getStr("principal"));
+						organization.setRemark(orgData.getStr("remark"));
+						organization.setUpdateTime(orgData.getLong("updateTime"));
+
+						allOrganizations.add(organization);
+
+						String childOrganizationCode = orgData.getStr("organizationCode");
+						log.info("执行第一页递归:"+childOrganizationCode);
+						Result<List<YkzOrganization>> result = fetchOrganizationsRecursively(childOrganizationCode, allOrganizations, 1, pageSize);
+						if (result.getStatus() != 200) {
+							return result; // 如果递归调用失败,返回失败结果
+						}
+					}
+					int totalPage = data.getInt("totalPage");
+					log.info("totalPage:"+totalPage);
+					log.info("total:"+data.getInt("total"));
+					if (pageNumber >= totalPage) {
+						break; // 没有下一页了,退出循环
+					}else {
+						pageNumber++;
+						log.info("执行第n页递归:"+pageNumber);
+						return fetchOrganizationsRecursively(organizationCode, allOrganizations, pageNumber, pageSize);
+					}
+				} else {
+					String errorMessage = jsonResponse.getStr("message");
+					return new Result<>(ResultCodeEnum.FAIL, ResultCodeEnum.FAIL.getCode(), errorMessage);
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+				return new Result<>(500, e.toString());
+			}
+		}
+		List<YkzOrganization> ykzOrganizations = ycqOrganizationRepo.saveAll(allOrganizations);
+		log.info("YkzOrganization数据集合:"+ykzOrganizations);
+
+		return new Result<>(ykzOrganizations,ResultCodeEnum.SUCCESS);
+	}
+
+	private Result SendTsmOrg(List<YkzOrganization> ykzOrganizations) {
+		List<TsmOrg> tsmOrgList = new ArrayList<>();
+
+		for (YkzOrganization ykzOrganization : ykzOrganizations) {
+			TsmOrg tsmOrg = new TsmOrg();
+			//orgid
+//			tsmOrg.setOrgId();
+			//组织编码-政务钉钉组织机构code
+			tsmOrg.setOrgCode(ykzOrganization.getOrganizationCode());
+			//组织名称-机构简称
+			tsmOrg.setOrgName(ykzOrganization.getGovShortName());
+			//组织全称-机构全称
+			tsmOrg.setOrgFullName(ykzOrganization.getName());
+			//组织描述-备注
+			tsmOrg.setOrgDesc(ykzOrganization.getRemark());
+			//父组织ID,根组织id为0-机构父级id
+			tsmOrg.setParentOrgId(ykzOrganization.getParentId());
+			//父组织编码-父组织机构code
+			tsmOrg.setParentOrgCode(ykzOrganization.getParentOrganizationCode());
+			//创建时间
+			tsmOrg.setCreateTime(new Date());
+			//更新时间
+//			tsmOrg.setUpdateTime(new Date());
+			//排序-同级排序字段
+			Long longValue = ykzOrganization.getDisplayOrder();
+			if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) {
+				Integer intValue = longValue.intValue();
+				tsmOrg.setSortId(intValue.longValue());
+			}
+			//0无效,1有效-是否启用
+			if (ykzOrganization.getIsDeleted() != null) {
+				if (ykzOrganization.getIsDeleted() == 0) {
+					tsmOrg.setState(ykzOrganization.getIsEnable());
+				} else {
+					tsmOrg.setState(0);
+				}
+			}
+				//组织管理员
+//			tsmOrg.setManagePeople();
+				//组织等级-机构/单位级别
+				tsmOrg.setOrgLevel(ykzOrganization.getGovInstitutionLevelCode());
+				//数据来源
+				tsmOrg.setDataSources("愉快政");
+				//所属区域-行政区划Code
+				tsmOrg.setRegion(ykzOrganization.getGovDivisionCode());
+				//所属行业类别
+//			tsmOrg.setIndustryCategory();
+				//组织类别树
+//			tsmOrg.setOrgCategoryTree();
+				//所属行业类别(团组织)
+//			tsmOrg.setOcylIndustryCategory();
+				//组织类别(团组织)
+//			tsmOrg.setOcylOrgCategory();
+				//是否乡镇团委(团组织)
+//			tsmOrg.setOcylIsXztw();
+				//联系电话(团组织)
+//			tsmOrg.setTelephone();
+				tsmOrgList.add(tsmOrg);
+			}
+		List<TsmOrg> tsmOrgs = tsmOrgRepo.saveAll(tsmOrgList);
+		log.info("tsmOrg筛选前数量:"+tsmOrgs.size());
+		Map<String, TsmOrg> orgCodeToTsmOrg = tsmOrgs.stream().collect(Collectors.toMap(TsmOrg::getOrgCode, org -> org));
+		tsmOrgs.forEach(org -> {
+			String parentOrgCode = org.getParentOrgCode();
+			if (orgCodeToTsmOrg.containsKey(parentOrgCode)){
+				org.setParentOrgId(orgCodeToTsmOrg.get(parentOrgCode).getOrgId());
+			}
+		});
+
+		List<TsmOrg> tsmOrgs1 = tsmOrgRepo.saveAll(tsmOrgs);
+		log.info("tsmOrg筛选后数量:"+tsmOrgs1.size());
+		return new Result<>(tsmOrgs1,ResultCodeEnum.SUCCESS);
+	}
+}

+ 240 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/service/impl/YkzUserInformationServiceImpl.java

@@ -0,0 +1,240 @@
+package com.crunii.micro.intf.httpdemo.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.xxpt.gateway.shared.api.request.OapiGettokenJsonRequest;
+import com.alibaba.xxpt.gateway.shared.api.request.OapiRpcOauth2DingtalkAppUserJsonRequest;
+import com.alibaba.xxpt.gateway.shared.api.response.OapiGettokenJsonResponse;
+import com.alibaba.xxpt.gateway.shared.api.response.OapiRpcOauth2DingtalkAppUserJsonResponse;
+import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient;
+import com.alibaba.xxpt.gateway.shared.client.http.IntelligentGetClient;
+import com.alibaba.xxpt.gateway.shared.client.http.IntelligentPostClient;
+import com.crunii.micro.common.exception.BusinessException;
+import com.crunii.micro.domain.dto.LoginInfoDto;
+import com.crunii.micro.domain.dto.Worker;
+import com.crunii.micro.domain.entity.main.TsmWorker;
+import com.crunii.micro.domain.repo.main.TsmWorkerRepo;
+import com.crunii.micro.feign.client.SysService;
+import com.crunii.micro.feign.client.dto.LoginPhoneDto;
+import com.crunii.micro.intf.httpdemo.jdbc.YkzObtainUserInformationJdbc;
+import com.crunii.micro.intf.httpdemo.service.AccessTokenService;
+import com.crunii.micro.intf.httpdemo.service.YkzUserInformationService;
+import com.crunii.micro.intf.httpdemo.utils.Sm2Util;
+import com.dcqc.uc.oauth.sdk.model.Result;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Service
+@Slf4j
+public class YkzUserInformationServiceImpl implements YkzUserInformationService {
+    private static ExecutableClient executableClient;
+    @Autowired
+    private TsmWorkerRepo tsmWorkerRepo;
+    @Autowired
+    private AccessTokenService accessTokenService;
+    @Autowired
+    private YkzObtainUserInformationJdbc ykzObtainUserInformationJdbc;
+    @Autowired
+    private SysService sysService;
+    @Value("https://youth.cq.cqyl.org.cn:11020/api/LoginController/loginExternalMobile")
+    public String ykzFreeUrl;
+    @Value("${uc.api.url}")
+    private String ucApiUrl;
+    //国密密钥
+
+    public static final String PRIVATE_WEB_KEY = "184c211702d2bc05d8e5a1729c4295df7d96309397174d56f46ca04372ccc0b5";
+
+    static {
+        executableClient = ExecutableClient.getInstance();
+        //DomainName不同环境对应不同域名,渝快政使用下面示例, 政务外网需要查看接口必读完成配置
+        executableClient.setDomainName("zd-openplatform.bigdatacq.com");
+        executableClient.setProtocal("https");
+        //应用App Key
+        executableClient.setAccessKey("qccq-gqtcqsw-F8mjn9Br9cA7v6Rby");
+        //应用App Secret
+        executableClient.setSecretKey("Dju0i1gUWF427e5GDDOcfaUGqyD21V852e1sHe6n");
+        executableClient.init();
+    }
+
+    @Override
+    public Worker ykzFreeLogin(HttpServletRequest request, LoginPhoneDto loginPhoneDto, String callUrl, boolean b) throws JsonProcessingException {
+        //executableClient保证单例
+        IntelligentGetClient intelligentGetClient = executableClient.newIntelligentGetClient("/gettoken.json");
+        OapiGettokenJsonRequest oapiGettokenJsonRequest = new OapiGettokenJsonRequest();
+        //应用的唯一标识key
+        oapiGettokenJsonRequest.setAppkey("qccq-gqtcqsw-F8mjn9Br9cA7v6Rby");
+        //应用的密钥
+        oapiGettokenJsonRequest.setAppsecret("Dju0i1gUWF427e5GDDOcfaUGqyD21V852e1sHe6n");
+        //获取结果
+        OapiGettokenJsonResponse apiResult = intelligentGetClient.get(oapiGettokenJsonRequest);
+        cn.hutool.json.JSONObject jsonResponse = JSONUtil.parseObj(apiResult);
+        if (jsonResponse.getBool("success")) {
+            cn.hutool.json.JSONObject content = jsonResponse.getJSONObject("content");
+            String data = content.get("data").toString();
+            cn.hutool.json.JSONObject jsonObject2 = new cn.hutool.json.JSONObject(data);
+            String accessToken = jsonObject2.get("accessToken").toString();
+            //executableClient保证单例
+            IntelligentPostClient intelligentPostClient = executableClient.newIntelligentPostClient("/rpc/oauth2/dingtalk_app_user.json");
+            OapiRpcOauth2DingtalkAppUserJsonRequest oapiRpcOauth2DingtalkAppUserJsonRequest = new OapiRpcOauth2DingtalkAppUserJsonRequest();
+            //登录access_token
+            oapiRpcOauth2DingtalkAppUserJsonRequest.setAccess_token(accessToken);
+            //临时授权码  授权码用国密进行加密 需解密
+            //2024/01/24改动
+//            String jsonStr = "";
+            String decrypt = "";
+            try {
+                decrypt = Sm2Util.decrypt(PRIVATE_WEB_KEY, true, loginPhoneDto.getAuthCode());
+                log.info("encode解密参数:" + decrypt);
+            } catch (Exception e) {
+                log.error("解密失败", e);
+                throw new BusinessException("数据解析失败,请联系系统管理员!");
+            }
+            // 使用 Jackson 库来解析 JSON 字符串
+//            ObjectMapper objectMapper = new ObjectMapper();
+//            JsonNode jsonNode = objectMapper.readTree(jsonStr);
+//            // 获取 BatchCodeId 和 Count 参数的值
+//            String authCode = "";
+//            authCode = jsonNode.get("AuthCode").asText();
+            oapiRpcOauth2DingtalkAppUserJsonRequest.setAuth_code(decrypt );
+            //获取结果
+            OapiRpcOauth2DingtalkAppUserJsonResponse result = intelligentPostClient.post(oapiRpcOauth2DingtalkAppUserJsonRequest);
+            cn.hutool.json.JSONObject userInfoResponse = JSONUtil.parseObj(result);
+            if (userInfoResponse.getBool("success")) {
+                cn.hutool.json.JSONObject usercontent = userInfoResponse.getJSONObject("content");
+                String userContent = usercontent.get("content").toString();
+                cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(userContent);
+                String userData = jsonObject.get("data").toString();
+                cn.hutool.json.JSONObject jsonObjectData = new cn.hutool.json.JSONObject(userData);
+                String accountId = jsonObjectData.get("accountId").toString();
+                System.out.println(accountId);
+                if (!StringUtils.hasText(accountId)) {
+                    throw new BusinessException("获取愉快政用户信息失败!");
+                }
+                Map<String, Object> props = new HashMap<>(2);
+                props.put("accountId", accountId);
+                TsmWorker curr = tsmWorkerRepo.getUniqueByProps(props);
+                if (Objects.isNull(curr)) {
+                    throw new BusinessException("该用户未注册青春重庆!");
+                }
+                LoginInfoDto dto = new LoginInfoDto();
+                dto.setMobile(curr.getMobile());
+                dto.setYkz("1");
+                if (StringUtils.hasText(loginPhoneDto.getScope())) {
+                    dto.setScope("WEB");
+                } else {
+                    dto.setScope("PHONE");
+                }
+                System.out.println(curr.getMobile());
+                com.crunii.micro.common.dto.Result<Worker> resultWorker = sysService.loginExternalMobile(dto);
+                System.out.println(JSONUtil.toJsonStr(resultWorker));
+                com.alibaba.fastjson.JSONObject returnStr = com.alibaba.fastjson.JSONObject.parseObject(JSONUtil.toJsonStr(resultWorker));
+                if (returnStr.get("success").toString().equals("true")) {
+                    String userInfoData = returnStr.get("data").toString();
+                    ObjectMapper mapper = new ObjectMapper(); // 创建ObjectMapper对象
+                    Worker worker = mapper.readValue(userInfoData, Worker.class); // 将JSON字符串转换为Person对象
+                    worker.setAccountId(accountId);
+                    return worker;
+                }
+            }
+        }
+        throw new BusinessException("获取愉快政用户信息失败!");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void getYkzUserInfo() {
+        List<TsmWorker> userList = ykzObtainUserInformationJdbc.workerList();
+        log.info("用户数量:" + userList.size());
+        List<TsmWorker> list = new ArrayList<>();
+        String apiUrl = ucApiUrl + "/ykz/user/getByAccountId";
+        Result accessTokens = accessTokenService.getAccessToken();
+        if (accessTokens.getStatus() != 0) {
+        }
+        String accessToken = (String) accessTokens.getData();
+        for (TsmWorker worker : userList) {
+            try {
+                JSONObject requestBody = new JSONObject()
+                        .fluentPut("data", new JSONObject()
+                                .fluentPut("mobile", worker.getMobile())
+                        )
+                        .fluentPut("requestId", StrUtil.uuid());
+                String resp = HttpRequest.post(apiUrl)
+                        .header("Content-Type", "application/json")
+                        .header("Authorization", "Bearer " + accessToken)
+                        .body(requestBody.toJSONString())
+                        .timeout(5000)
+                        .execute()
+                        .body();
+                cn.hutool.json.JSONObject jsonResponse = JSONUtil.parseObj(resp);
+                if (jsonResponse.getBool("success")) {
+                    cn.hutool.json.JSONObject data = jsonResponse.getJSONObject("data");
+                    String accountId = data.get("accountId").toString();
+                    if (!accountId.equals(worker.getAccountId())) {
+                        worker.setAccountId(Long.valueOf(accountId));
+                        list.add(worker);
+                    }
+                    //ykzObtainUserInformationJdbc.updateAccountId(worker.getLoginId(),Long.valueOf(accountId));
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        userList.clear();
+        if (CollectionUtils.isNotEmpty(list)) {
+            log.info("要更改的数据量:" + list.size());
+            tsmWorkerRepo.saveAllAndFlush(list);
+        }
+    }
+
+    @Override
+    public com.crunii.micro.common.dto.Result saveYkzUserInfo(LoginPhoneDto loginPhoneDto) {
+        if (org.apache.commons.lang3.StringUtils.isEmpty(loginPhoneDto.getMobile())) {
+            throw new BusinessException("请传入需要手机号!");
+        }
+        TsmWorker worker = ykzObtainUserInformationJdbc.worker(loginPhoneDto.getMobile());
+        String apiUrl = ucApiUrl + "/ykz/user/getByAccountId";
+        Result accessTokens = accessTokenService.getAccessToken();
+        if (accessTokens.getStatus() != 0) {
+        }
+        String accessToken = (String) accessTokens.getData();
+        try {
+            JSONObject requestBody = new JSONObject()
+                    .fluentPut("data", new JSONObject()
+                            .fluentPut("mobile", worker.getMobile())
+                    )
+                    .fluentPut("requestId", StrUtil.uuid());
+            String resp = HttpRequest.post(apiUrl)
+                    .header("Content-Type", "application/json")
+                    .header("Authorization", "Bearer " + accessToken)
+                    .body(requestBody.toJSONString())
+                    .timeout(5000)
+                    .execute()
+                    .body();
+            cn.hutool.json.JSONObject jsonResponse = JSONUtil.parseObj(resp);
+            if (jsonResponse.getBool("success")) {
+                cn.hutool.json.JSONObject data = jsonResponse.getJSONObject("data");
+                String accountId = data.get("accountId").toString();
+                if (!accountId.equals(worker.getAccountId())) {
+                    ykzObtainUserInformationJdbc.updateAccountId(worker.getLoginId(), Long.valueOf(accountId));
+                }
+                return com.crunii.micro.common.dto.Result.ofSuccess("绑定成功!");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return com.crunii.micro.common.dto.Result.ofFail("绑定失败!");
+    }
+}

+ 52 - 0
interfaces/intf-http-demo/src/main/java/com/crunii/micro/intf/httpdemo/utils/Sm2Util.java

@@ -0,0 +1,52 @@
+/**
+ * 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.intf.httpdemo.utils;
+
+import cn.hutool.core.util.HexUtil;
+import cn.hutool.crypto.ECKeyUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.SM2;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+
+/**
+ * @author shenhs create 2023/6/2 19:43
+ */
+public class Sm2Util {
+
+    public static String encrypt(String publicKey, boolean isHex, String str) {
+        if (isHex) {
+            ECPublicKeyParameters ecPublicKeyParameters = ECKeyUtil.toSm2PublicParams(publicKey);
+            SM2 sm2 = new SM2(null, ecPublicKeyParameters);
+            byte[] encStr = sm2.encrypt(str, KeyType.PublicKey);
+            return HexUtil.encodeHexStr(encStr);
+        } else {
+            SM2 sm2 = new SM2(null, publicKey);
+            return sm2.encryptBase64(str, KeyType.PublicKey);
+        }
+    }
+
+    public static String decrypt(String privateKey, boolean isHex, String str) {
+        if (isHex) {
+            ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toSm2PrivateParams(privateKey);
+            SM2 sm2 = new SM2(privateKeyParameters, null);
+            // 在密文前面固定加04,以解决解密时报错:Invalid point encoding 0x5c
+            if (!str.startsWith("04")) {
+                str = "04" + str;
+            }
+            return sm2.decryptStr(str, KeyType.PrivateKey);
+        } else {
+            SM2 sm2 = new SM2(privateKey, null);
+            return sm2.decryptStr(str, KeyType.PrivateKey);
+        }
+    }
+
+    public static void main(String[] args) {
+        SM2 sm2 = new SM2();
+        sm2.initKeys();
+        System.out.println("pk:" + sm2.getPublicKeyBase64());
+        System.out.println("prk:" + sm2.getPrivateKeyBase64());
+    }
+}

+ 145 - 0
interfaces/intf-http-demo/src/main/resources/application.yml

@@ -0,0 +1,145 @@
+spring:
+  profiles:
+    active: local
+  application:
+    name: intf-http-demo
+  data:
+    redis:
+      repositories:
+        enabled: false
+  main:
+    allow-bean-definition-overriding: true
+    allow-circular-references: true
+  mvc:
+    pathmatch:
+      matching-strategy: ant_path_matcher
+server:
+  tomcat:
+    basedir: ${tmp.dir:/tmp}
+  port: 12000
+---
+#!注意生产环境配置false!
+swagger:
+  enabled: false
+springfox:
+  documentation:
+    enabled: ${swagger.enabled}
+spring:
+  config:
+    activate:
+      on-profile: local
+    import:
+      - nacos:${spring.application.name}.properties
+      - nacos:${spring.application.name}.yml
+      - nacos:feign-client.yml
+      - nacos:management.yml
+      - nacos:hibernate.properties
+      - nacos:redis.yml
+      - nacos:db-druid-main-postgresql.yml
+      #- nacos:db-druid-main-mysql.yml
+  cloud:
+    nacos:
+      config:
+        server-addr: localhost:8848
+        timeout: 5000
+        username: 'nacos'
+        password: 'nacos'
+        namespace: local
+      discovery:
+        namespace: local
+        username: 'nacos'
+        password: 'nacos'
+        server-addr: localhost:8848
+        metadata:
+          apiVersion: default
+
+---
+#!注意生产环境配置false!
+swagger:
+  enabled: false
+springfox:
+  documentation:
+    enabled: ${swagger.enabled}
+spring:
+  config:
+    activate:
+      on-profile: dev
+    import:
+      - nacos:${spring.application.name}.properties
+      - nacos:${spring.application.name}.yml
+      - nacos:management.yml
+      - nacos:hibernate.properties
+      - nacos:redis.yml
+      - nacos:service-app.yml
+      - nacos:service-sso.yml
+      #      - nacos:db-druid-main-postgresql.yml
+      - nacos:db-druid-main-dm.yml
+      - nacos:service-obs.yml
+      - nacos:application-common.yml
+      - nacos:service-sso.yml
+      #- nacos:db-druid-main-mysql.yml
+  cloud:
+    nacos:
+      config:
+        server-addr: 221.229.99.73:62848
+        timeout: 5000
+        username: 'nacos'
+        password: '9sxrenvJP'
+        namespace: lml
+      discovery:
+        namespace: lml
+        username: 'nacos'
+        password: '9sxrenvJP'
+        server-addr: 221.229.99.73:62848
+        metadata:
+          apiVersion: default
+---
+#!注意生产环境配置false!
+swagger:
+  enabled: false
+springfox:
+  documentation:
+    enabled: ${swagger.enabled}
+spring:
+  config:
+    activate:
+      on-profile: h3cTest
+    import:
+      - nacos:${spring.application.name}.properties
+      - nacos:${spring.application.name}.yml
+      - nacos:management.yml
+      - nacos:hibernate.properties
+      - nacos:redis.yml
+      - nacos:service-app.yml
+      - nacos:service-sso.yml
+      #      - nacos:db-druid-main-postgresql.yml
+      - nacos:db-druid-main-dm.yml
+      - nacos:service-obs.yml
+      - nacos:application-common.yml
+      - nacos:service-sso.yml
+      #- nacos:db-druid-main-mysql.yml
+  cloud:
+    nacos:
+      config:
+        server-addr: 172.16.25.10:8848
+        timeout: 50000
+        username: 'nacos'
+        password: '9sxrenvJP'
+        namespace: h3cTest
+      discovery:
+        namespace: h3cTest
+        username: 'nacos'
+        password: '9sxrenvJP'
+        server-addr: 172.16.25.10:8848
+        metadata:
+          apiVersion: default
+---
+uc:
+  jwt:
+    # uc给的公钥
+    publicKeyCerPath: jwt.sm2.cer
+  api:
+    url: https://uc-openplatform.bigdatacq.com:4403
+    appId: c272db622c3be4af9eea2c58b8dd6ba2
+    secret: c272db622c3be4af96c8db480c6fe2e9
+    defaultOrganizationCode: 575324d0-2257-4f53-8e5f-ca72d992abe9

+ 12 - 0
interfaces/intf-http-demo/src/main/resources/ftls/salerQuery.ftl

@@ -0,0 +1,12 @@
+<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.inf.channel.bigdata.ztesoft.com/">
+	<soapenv:Header/>
+	<soapenv:Body>
+		<ser:channelSalesInfoQuery>
+			<arg0>
+				<queryObj>${queryObj}</queryObj>
+				<queryType>${queryType}</queryType>
+				<signCode>channelkey202211</signCode>
+			</arg0>
+		</ser:channelSalesInfoQuery>
+	</soapenv:Body>
+</soapenv:Envelope>

+ 11 - 0
interfaces/intf-http-demo/src/main/resources/ftls/tempTest.ftl

@@ -0,0 +1,11 @@
+<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.server.bsn.cdma.module.uip.cqcis.com">
+	<soapenv:Header/>
+	<soapenv:Body>
+		<ser:bsnStopOpenCdmaAccept>
+			<in0><![CDATA[<?xml version="1.0"  encoding="UTF-8"?>
+				<request>
+					    <abc>${id}</abc>
+				</request>]]></in0>
+		</ser:bsnStopOpenCdmaAccept>
+	</soapenv:Body>
+</soapenv:Envelope>

BIN
interfaces/intf-http-demo/src/main/resources/jwt.sm2.cer


+ 145 - 0
interfaces/intf-http-demo/src/main/resources/logback-spring.xml

@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
+scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
+debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
+<springProfile name="!dev,!staging"></springProfile>
+-->
+<configuration scan="false" scanPeriod="60 seconds" debug="false">
+    <springProperty scope="context" name="projName" source="spring.application.name" defaultValue="app"/>
+
+    <property name="logPath" value="logs"/>
+    <!-- 保存天数 -->
+    <property name="saveDays" value="${file.saveDays:-7}"/>
+
+    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="rolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logPath}/${projName}.log</file>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logPath}/${projName}.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>${saveDays}</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+        <queueSize>1024</queueSize>
+        <!-- 不添加linux类名为?-->
+        <includeCallerData>true</includeCallerData>
+        <!-- 添加附加的appender,最多只能添加一个 -->
+        <appender-ref ref="rolling"/>
+    </appender>
+
+    <!-- 单独的日志文件控制 -->
+    <!--
+    <appender name="singleRolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logPath}/single.log</file>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logPath}/single.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>${saveDays}</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+	<logger name="single" level="info" additivity="false">
+		<appender-ref ref="singleRolling" />
+	</logger>
+ 	-->
+
+    <logger name="com.crunii" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="org.springframework" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="org.hibernate" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="redis.clients" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="springfox.documentation.swagger.readers.operation.OperationImplicitParameterReader" level="error" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <!--
+    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
+    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
+    -->
+    <root level="info">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </root>
+</configuration>

+ 16 - 0
interfaces/intf-http-demo/src/main/resources/spring/ctx.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc"
+	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
+	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
+		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
+		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
+		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
+		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
+		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
+
+
+</beans>

+ 94 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/AgentSalesInfo.java

@@ -0,0 +1,94 @@
+package com.crunii.micro.intf.httpdemo.test;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@Setter
+@Getter
+@ToString
+@ApiModel(description = "销售员数据")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "salesInfo")
+public class AgentSalesInfo {
+	@XmlElement
+	@ApiModelProperty(value = "销售员编码")
+	public String salesCode;
+	@ApiModelProperty(value = "销售员名称")
+	@XmlElement
+	public String salesName;
+	@ApiModelProperty(value = "销售员工号")
+	@XmlElement
+	public String staffCode;
+	@ApiModelProperty(value = "证件号码")
+	@XmlElement
+	public String salesCentNbr;
+	@ApiModelProperty(value = "移动电话")
+	@XmlElement
+	public String mobilePhone;
+	@ApiModelProperty(value = "生效时间")
+	@XmlElement
+	public String effDate;
+	@ApiModelProperty(value = "失效时间")
+	@XmlElement
+	public String expDate;
+	@ApiModelProperty(value = "状态")
+	@XmlElement
+	public String statusCd;
+	@ApiModelProperty(value = "销售点编码")
+	@XmlElement
+	public String channelNbr;
+	@ApiModelProperty(value = "销售点名称")
+	@XmlElement
+	public String channelName;
+	@ApiModelProperty(value = "经营主体编码")
+	@XmlElement
+	public String operatorsNbr;
+	@ApiModelProperty(value = "经营主体名称")
+	@XmlElement
+	public String operatorsName;
+	@ApiModelProperty(value = "销售点一级分类编码")
+	@XmlElement
+	public String salesFirstType;
+	@ApiModelProperty(value = "销售点一级分类名称")
+	@XmlElement
+	public String salesFirstTypeName;
+	@ApiModelProperty(value = "销售点二级分类编码")
+	@XmlElement
+	public String salesSecondType;
+	@ApiModelProperty(value = "销售点二级分类名称")
+	@XmlElement
+	public String salesSecondTypeName;
+	@ApiModelProperty(value = "销售点三级分类编码")
+	@XmlElement
+	public String salesThirdType;
+	@ApiModelProperty(value = "销售点三级分类名称")
+	@XmlElement
+	public String salesThirdTypeName;
+	@ApiModelProperty(value = "分公司名称")
+	@XmlElement
+	public String areaName;
+	@ApiModelProperty(value = "渠道类型")
+	@XmlElement
+	public String channelType;
+	@ApiModelProperty(value = "备注")
+	@XmlElement
+	public String remark;
+}
+
+
+
+
+
+
+
+
+
+

+ 22 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/BaseTestHttpDemo.java

@@ -0,0 +1,22 @@
+package com.crunii.micro.intf.httpdemo.test;
+
+import com.crunii.micro.common.utils.JasyptEncryptorUtil;
+import com.crunii.micro.intf.httpdemo.Application;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+@RunWith(SpringRunner.class)
+@ActiveProfiles(value = "local")
+@SpringBootTest(classes = { Application.class }, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@Transactional(timeout = 1800)
+public class BaseTestHttpDemo {
+	static {
+		JasyptEncryptorUtil.setConfigClientProps();
+		JasyptEncryptorUtil.setAdminClientProps();
+		System.setProperty("es.set.netty.runtime.available.processors", "false");
+	}
+
+}

+ 21 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/BsnRspTest.java

@@ -0,0 +1,21 @@
+package com.crunii.micro.intf.httpdemo.test;
+
+import lombok.Data;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@Data
+@XmlRootElement(name = "a")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class BsnRspTest {
+
+	@XmlElement
+	public String name;
+
+	@XmlElement
+	public Integer age;
+
+}

+ 27 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/ChannelQuerySaleInfoResp.java

@@ -0,0 +1,27 @@
+package com.crunii.micro.intf.httpdemo.test;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@Data
+@XmlRootElement(name = "return")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ChannelQuerySaleInfoResp {
+	@ApiModelProperty(value = "响应编码")
+	@XmlElement
+	public String result;
+
+	@ApiModelProperty(value = "响应描述")
+	@XmlElement
+	public String message;
+
+	@ApiModelProperty(value = "销售员信息")
+	@XmlElement(name = "salesInfo")
+	public AgentSalesInfo salesInfo;
+
+}

+ 48 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/TempTest.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.intf.httpdemo.test;
+
+import com.crunii.micro.common.http.HttpClientUtil;
+import com.crunii.micro.common.template.FreemarkerTempConfiguration;
+import com.crunii.micro.common.xml.XmlUtil;
+import com.crunii.micro.common.xml.jaxb.JaxbUtil;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2022-12-27 11:36:33
+ */
+public class TempTest {
+
+	public static void main(String[] args) throws Exception {
+		FreemarkerTempConfiguration conf = new FreemarkerTempConfiguration();
+		PathMatchingResourcePatternResolver bf = new PathMatchingResourcePatternResolver();
+		conf.setResR(bf);
+		conf.setResourcePath("classpath:ftls/*");
+		Configuration config = conf.getObject();
+		Template template = config.getTemplate("salerQuery.ftl");
+		Map<String, Object> map = new HashMap<>();
+		map.put("queryObj", "Y50011256140");
+		map.put("queryType", "1");
+		StringWriter sw = new StringWriter(256);
+		template.process(map, sw);
+		String body = sw.toString();
+		String url = "http://136.25.33.91:10030/chnl-inf/services/channelQryService";
+		StringEntity entity = new StringEntity(body, ContentType.APPLICATION_SOAP_XML);
+		String recvXml = HttpClientUtil.reqStrByEntity(url, entity, "POST");
+		//获取某个节点的xml内容
+		String nodeXml = XmlUtil.getNodeXml(recvXml, "return");
+		//xml直接转对象
+		ChannelQuerySaleInfoResp rsp = JaxbUtil.xml2Bean(nodeXml, ChannelQuerySaleInfoResp.class);
+		System.out.println(rsp);
+	}
+}

+ 44 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/TempTest1.java

@@ -0,0 +1,44 @@
+/**
+ * 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.intf.httpdemo.test;
+
+import com.crunii.micro.common.template.FreemarkerTempConfiguration;
+import com.crunii.micro.common.utils.SoapUtil;
+import com.crunii.micro.common.xml.XmlUtil;
+import com.crunii.micro.common.xml.jaxb.JaxbUtil;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2022-12-27 11:36:33
+ */
+public class TempTest1 {
+
+	public static void main(String[] args) throws Exception {
+		FreemarkerTempConfiguration conf = new FreemarkerTempConfiguration();
+		PathMatchingResourcePatternResolver bf = new PathMatchingResourcePatternResolver();
+		conf.setResR(bf);
+		conf.setResourcePath("classpath:ftls/*");
+		Configuration config = conf.getObject();
+		Template template = config.getTemplate("tempTest.ftl");
+		Map<String, Object> map = new HashMap<>();
+		map.put("id", "ABC123123");
+		StringWriter sw = new StringWriter(256);
+		template.process(map, sw);
+		String body = sw.toString();
+		String url = "http://81.68.133.219:7355/services/BsnServicePort";
+		String recvXml = SoapUtil.callSoap(body, url);
+		System.out.println("--------------------业务xml--------------------");
+		String busiXml = XmlUtil.getNodeTextByPath(recvXml, "//*[local-name()='out']");
+		System.out.println("busiXml:" + busiXml);
+		BsnRspTest bsnRspTest = JaxbUtil.xml2Bean(busiXml, BsnRspTest.class);
+		System.out.println(bsnRspTest);
+	}
+}

+ 49 - 0
interfaces/intf-http-demo/src/test/java/com/crunii/micro/intf/httpdemo/test/service/TemplateTest.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.intf.httpdemo.test.service;
+
+import com.crunii.micro.common.http.HttpClientUtil;
+import com.crunii.micro.common.xml.XmlUtil;
+import com.crunii.micro.common.xml.jaxb.JaxbUtil;
+import com.crunii.micro.intf.httpdemo.test.BaseTestHttpDemo;
+import com.crunii.micro.intf.httpdemo.test.ChannelQuerySaleInfoResp;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 田平 create 2022-12-27 12:27:13
+ */
+public class TemplateTest extends BaseTestHttpDemo {
+
+	@Resource(name = "freemarkerTempConfiguration")
+	private Configuration configuration;
+
+	@Test
+	public void testTemplate() throws Exception {
+		Template template = configuration.getTemplate("salerQuery.ftl");
+		Map<String, Object> map = new HashMap<>();
+		map.put("queryObj", "Y50011256140");
+		map.put("queryType", "1");
+		StringWriter sw = new StringWriter(256);
+		template.process(map, sw);
+		String body = sw.toString();
+		String url = "http://136.25.33.91:10030/chnl-inf/services/channelQryService";
+		StringEntity entity = new StringEntity(body, ContentType.APPLICATION_SOAP_XML);
+		String recvXml = HttpClientUtil.reqStrByEntity(url, entity, "POST");
+		//获取某个节点的xml内容
+		String nodeXml = XmlUtil.getNodeXml(recvXml, "return");
+		//xml直接转对象
+		ChannelQuerySaleInfoResp rsp = JaxbUtil.xml2Bean(nodeXml, ChannelQuerySaleInfoResp.class);
+		System.out.println(rsp);
+	}
+}

+ 132 - 0
interfaces/intf-http-demo/src/test/resources/logback-spring-test.xml

@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
+scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
+debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
+<springProfile name="!dev,!staging"></springProfile>
+-->
+<configuration scan="false" scanPeriod="60 seconds" debug="false">
+    <springProperty scope="context" name="projName" source="spring.application.name" defaultValue="app"/>
+
+    <property name="logPath" value="logs"/>
+    <!-- 保存天数 -->
+    <property name="saveDays" value="${file.saveDays:-7}"/>
+
+    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="rolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logPath}/${projName}.log</file>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logPath}/${projName}.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>${saveDays}</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+        <queueSize>1024</queueSize>
+        <!-- 不添加linux类名为?-->
+        <includeCallerData>true</includeCallerData>
+        <!-- 添加附加的appender,最多只能添加一个 -->
+        <appender-ref ref="rolling"/>
+    </appender>
+
+    <!-- 单独的日志文件控制 -->
+    <!--
+    <appender name="singleRolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logPath}/single.log</file>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[${projName}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%thread] %C.%M:%L|%m%n</pattern>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logPath}/single.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>${saveDays}</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+	<logger name="single" level="info" additivity="false">
+		<appender-ref ref="singleRolling" />
+	</logger>
+ 	-->
+
+    <logger name="com.crunii" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="org.springframework" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="org.hibernate" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <logger name="redis.clients" level="info" additivity="false">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </logger>
+
+    <!--
+    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
+    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
+    -->
+    <root level="info">
+        <if condition='p("console.log.enable").equalsIgnoreCase("true")||p("console.log.enable").equalsIgnoreCase("")'>
+            <then>
+                <appender-ref ref="stdout"/>
+            </then>
+        </if>
+        <if condition='p("daily.log.enable").equalsIgnoreCase("true")'>
+            <then>
+                <appender-ref ref="async"/>
+            </then>
+        </if>
+    </root>
+</configuration>

+ 84 - 0
interfaces/pom.xml

@@ -0,0 +1,84 @@
+<?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>
+
+    <artifactId>interfaces</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>intf-http-demo</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>
+
+            <!-- interfaces -->
+            <dependency>
+                <groupId>com.crunii.microservice</groupId>
+                <artifactId>intf-http-demo</artifactId>
+                <version>${micro.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.crunii.microservice</groupId>
+            <artifactId>micro-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.crunii.microservice</groupId>
+            <artifactId>micro-core</artifactId>
+        </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${boot.maven.version}</version>
+                <configuration>
+                    <mainClass>${start-class}</mainClass>
+                    <classifier>exec</classifier>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 42 - 0
micro-common-component/micro-common-excel/pom.xml

@@ -0,0 +1,42 @@
+<?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-excel</artifactId>
+
+    <description>
+        micro-common-excel
+    </description>
+
+	<dependencies>
+
+        <dependency>
+            <groupId>com.crunii.microservice</groupId>
+            <artifactId>micro-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.1.1</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>commons-compress</artifactId>
+                    <groupId>org.apache.commons</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-compress</artifactId>
+            <version>1.21</version>
+        </dependency>
+    </dependencies>
+</project>

+ 25 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/annotation/CellMerge.java

@@ -0,0 +1,25 @@
+package com.crunii.common.excel.annotation;
+
+
+import com.crunii.common.excel.core.CellMergeStrategy;
+
+import java.lang.annotation.*;
+
+/**
+ * excel 列单元格合并(合并列相同项)
+ *
+ * 需搭配 {@link CellMergeStrategy} 策略使用
+ *
+ * @author lsa
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface CellMerge {
+
+	/**
+	 * col index
+	 */
+	int index() default -1;
+
+}

+ 30 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/annotation/ExcelDictFormat.java

@@ -0,0 +1,30 @@
+package com.crunii.common.excel.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 字典格式化
+ *
+ * @author lsa
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelDictFormat {
+
+    /**
+     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+     */
+    String dictType() default "";
+
+    /**
+     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+     */
+    String readConverterExp() default "";
+
+    /**
+     * 分隔符,读取字符串组内容
+     */
+    String separator() default ",";
+
+}

+ 52 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelBigNumberConvert.java

@@ -0,0 +1,52 @@
+package com.crunii.common.excel.convert;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+
+/**
+ * 大数值转换
+ * Excel 数值长度位15位 大于15位的数值转换位字符串
+ *
+ * @author lsa
+ */
+@Slf4j
+public class ExcelBigNumberConvert implements Converter<Long> {
+
+    @Override
+    public Class<Long> supportJavaTypeKey() {
+        return Long.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+        return Convert.toLong(cellData.getData());
+    }
+
+    @Override
+    public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+        if (ObjectUtil.isNotNull(object)) {
+            String str = Convert.toStr(object);
+            if (str.length() > 15) {
+                return new WriteCellData<>(str);
+            }
+        }
+        WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
+        cellData.setType(CellDataTypeEnum.NUMBER);
+        return cellData;
+    }
+
+}

+ 52 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelDateConverter.java

@@ -0,0 +1,52 @@
+package com.crunii.common.excel.convert;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @Description: excel导出时间格式转换
+ *(根据DateTimeFormat注解设置的pattern,默认yyyy-MM-dd HH:mm:ss)
+ *
+ * @Author: shanbin
+ * @Date: 2023/5/10 14:47
+ */
+public class ExcelDateConverter implements Converter<Date> {
+
+    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return Converter.super.supportJavaTypeKey();
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return Converter.super.supportExcelTypeKey();
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        DateTimeFormat anno = getAnnotation(contentProperty.getField());
+        String pattern = anno.pattern();
+        if(ObjectUtil.isEmpty(anno.pattern())){
+            pattern = YYYY_MM_DD_HH_MM_SS;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        String dateValue = sdf.format(value);
+        return new WriteCellData<>(dateValue);
+    }
+
+    private DateTimeFormat getAnnotation(Field field) {
+        return AnnotationUtil.getAnnotation(field, DateTimeFormat.class);
+    }
+}

+ 76 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/convert/ExcelDictConvert.java

@@ -0,0 +1,76 @@
+//package com.crunii.common.excel.convert;
+//
+//import cn.hutool.core.annotation.AnnotationUtil;
+//import cn.hutool.core.convert.Convert;
+//import cn.hutool.core.util.ObjectUtil;
+//import com.crunii.common.excel.annotation.ExcelDictFormat;
+//import com.crunii.common.excel.utils.ExcelUtil;
+//import com.alibaba.excel.converters.Converter;
+//import com.alibaba.excel.enums.CellDataTypeEnum;
+//import com.alibaba.excel.metadata.GlobalConfiguration;
+//import com.alibaba.excel.metadata.data.ReadCellData;
+//import com.alibaba.excel.metadata.data.WriteCellData;
+//import com.alibaba.excel.metadata.property.ExcelContentProperty;
+//import cn.telecom.aicq.common.core.domain.R;
+//import cn.telecom.aicq.common.core.service.DictService;
+//import cn.telecom.aicq.common.core.utils.SpringUtils;
+//import cn.telecom.aicq.common.core.utils.StringUtils;
+//import lombok.extern.slf4j.Slf4j;
+//
+//import java.lang.reflect.Field;
+//
+///**
+// * 字典格式化转换处理
+// *
+// * @author lsa
+// */
+//@Slf4j
+//public class ExcelDictConvert implements Converter<Object> {
+//
+//    @Override
+//    public Class<Object> supportJavaTypeKey() {
+//        return Object.class;
+//    }
+//
+//    @Override
+//    public CellDataTypeEnum supportExcelTypeKey() {
+//        return null;
+//    }
+//
+//    @Override
+//    public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+//        ExcelDictFormat anno = getAnnotation(contentProperty.getField());
+//        String type = anno.dictType();
+//        String label = cellData.getStringValue();
+//        String value;
+//        if (StringUtils.isBlank(type)) {
+//            value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
+//        } else {
+//            R<String> ret = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
+//            value = ret.getData();
+//        }
+//        return Convert.convert(contentProperty.getField().getType(), value);
+//    }
+//
+//    @Override
+//    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+//        if (ObjectUtil.isNull(object)) {
+//            return new WriteCellData<>("");
+//        }
+//        ExcelDictFormat anno = getAnnotation(contentProperty.getField());
+//        String type = anno.dictType();
+//        String value = Convert.toStr(object);
+//        String label;
+//        if (StringUtils.isBlank(type)) {
+//            label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
+//        } else {
+//            R<String> ret = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
+//            label = ret.getData();
+//        }
+//        return new WriteCellData<>(label);
+//    }
+//
+//    private ExcelDictFormat getAnnotation(Field field) {
+//        return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
+//    }
+//}

+ 114 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/CellMergeStrategy.java

@@ -0,0 +1,114 @@
+package com.crunii.common.excel.core;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.merge.AbstractMergeStrategy;
+import com.crunii.common.excel.annotation.CellMerge;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 列值重复合并策略
+ *
+ * @author lsa
+ */
+@AllArgsConstructor
+@Slf4j
+public class CellMergeStrategy extends AbstractMergeStrategy {
+
+	private List<?> list;
+	private boolean hasTitle;
+
+	@Override
+	protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
+		List<CellRangeAddress> cellList = handle(list, hasTitle);
+		// judge the list is not null
+		if (CollectionUtils.isNotEmpty(cellList)) {
+			// the judge is necessary
+			if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {
+				for (CellRangeAddress item : cellList) {
+					sheet.addMergedRegion(item);
+				}
+			}
+		}
+	}
+
+	@SneakyThrows
+	private static List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {
+		List<CellRangeAddress> cellList = new ArrayList<>();
+		if (CollectionUtils.isEmpty(list)) {
+			return cellList;
+		}
+		Class<?> clazz = list.get(0).getClass();
+		Field[] fields = clazz.getDeclaredFields();
+		// 有注解的字段
+		List<Field> mergeFields = new ArrayList<>();
+		List<Integer> mergeFieldsIndex = new ArrayList<>();
+		for (int i = 0; i < fields.length; i++) {
+			Field field = fields[i];
+			if (field.isAnnotationPresent(CellMerge.class)) {
+				CellMerge cm = field.getAnnotation(CellMerge.class);
+				mergeFields.add(field);
+				mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
+			}
+		}
+		// 行合并开始下标
+		int rowIndex = hasTitle ? 1 : 0;
+		Map<Field, RepeatCell> map = new HashMap<>();
+		// 生成两两合并单元格
+		for (int i = 0; i < list.size(); i++) {
+			for (int j = 0; j < mergeFields.size(); j++) {
+				Field field = mergeFields.get(j);
+				String name = field.getName();
+				String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
+				Method readMethod = clazz.getMethod(methodName);
+				Object val = readMethod.invoke(list.get(i));
+
+				int colNum = mergeFieldsIndex.get(j);
+				if (!map.containsKey(field)) {
+					map.put(field, new RepeatCell(val, i));
+				} else {
+					RepeatCell repeatCell = map.get(field);
+					Object cellValue = repeatCell.getValue();
+					if (cellValue == null || "".equals(cellValue)) {
+						// 空值跳过不合并
+						continue;
+					}
+					if (cellValue != val) {
+						if (i - repeatCell.getCurrent() > 1) {
+							cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
+						}
+						map.put(field, new RepeatCell(val, i));
+					} else if (i == list.size() - 1) {
+						if (i > repeatCell.getCurrent()) {
+							cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
+						}
+					}
+				}
+			}
+		}
+		return cellList;
+	}
+
+	@Data
+	@AllArgsConstructor
+	static class RepeatCell {
+
+		private Object value;
+
+		private int current;
+
+	}
+}

+ 106 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/DefaultExcelListener.java

@@ -0,0 +1,106 @@
+package com.crunii.common.excel.core;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.exception.ExcelAnalysisException;
+import com.alibaba.excel.exception.ExcelDataConvertException;
+import com.crunii.micro.common.utils.StreamUtils;
+import com.crunii.micro.common.utils.ValidatorUtils;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Excel 导入监听
+ *
+ * @author Yjoioooo
+ * @author lsa
+ */
+@Slf4j
+@NoArgsConstructor
+public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
+
+    /**
+     * 是否Validator检验,默认为是
+     */
+    private Boolean isValidate = Boolean.TRUE;
+
+    /**
+     * excel 表头数据
+     */
+    private Map<Integer, String> headMap;
+
+    /**
+     * 导入回执
+     */
+    private ExcelResult<T> excelResult;
+
+    public DefaultExcelListener(boolean isValidate) {
+        this.excelResult = new DefautExcelResult<>();
+        this.isValidate = isValidate;
+    }
+
+    /**
+     * 处理异常
+     *
+     * @param exception ExcelDataConvertException
+     * @param context   Excel 上下文
+     */
+    @Override
+    public void onException(Exception exception, AnalysisContext context) throws Exception {
+        String errMsg = null;
+        if (exception instanceof ExcelDataConvertException) {
+            // 如果是某一个单元格的转换异常 能获取到具体行号
+            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
+            Integer rowIndex = excelDataConvertException.getRowIndex();
+            Integer columnIndex = excelDataConvertException.getColumnIndex();
+            errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",
+                rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
+            if (log.isDebugEnabled()) {
+                log.error(errMsg);
+            }
+        }
+        if (exception instanceof ConstraintViolationException) {
+            ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
+            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
+            String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", ");
+            errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
+            if (log.isDebugEnabled()) {
+                log.error(errMsg);
+            }
+        }
+        excelResult.getErrorMessageList().add(errMsg);
+        throw new ExcelAnalysisException(errMsg);
+    }
+
+    @Override
+    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
+        this.headMap = headMap;
+        log.debug("解析到一条表头数据: {}", JSONUtil.toJsonStr(headMap));
+    }
+
+    @Override
+    public void invoke(T data, AnalysisContext context) {
+        if (isValidate) {
+            ValidatorUtils.validateMsgException(data);
+        }
+        excelResult.getList().add(data);
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        log.debug("所有数据解析完成!");
+    }
+
+    @Override
+    public ExcelResult<T> getExcelResult() {
+        return excelResult;
+    }
+
+}

+ 81 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/DefautExcelResult.java

@@ -0,0 +1,81 @@
+package com.crunii.common.excel.core;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 默认excel返回对象
+ *
+ * @author Yjoioooo
+ * @author lsa
+ */
+public class DefautExcelResult<T> implements ExcelResult<T> {
+
+    /**
+     * 数据对象list
+     */
+    @Setter
+    private List<T> list;
+
+    /**
+     * 错误信息列表
+     */
+    @Setter
+    private List<String> errorList;
+
+    public DefautExcelResult() {
+        this.list = new ArrayList<>();
+        this.errorList = new ArrayList<>();
+    }
+
+    public DefautExcelResult(List<T> list, List<String> errorList) {
+        this.list = list;
+        this.errorList = errorList;
+    }
+
+    public DefautExcelResult(ExcelResult<T> excelResult) {
+        this.list = excelResult.getList();
+        this.errorList = excelResult.getErrorMessageList();
+    }
+
+    @Override
+    public List<T> getList() {
+        return list;
+    }
+
+    /**
+     * 错误列表
+     */
+    @Override
+    public List<T> getErrorList() {
+        return null;
+    }
+
+    @Override
+    public List<String> getErrorMessageList() {
+        return errorList;
+    }
+
+    /**
+     * 获取导入回执
+     *
+     * @return 导入回执
+     */
+    @Override
+    public String getAnalysis() {
+        int successCount = list.size();
+        int errorCount = errorList.size();
+        if (successCount == 0) {
+            return "读取失败,未解析到数据";
+        } else {
+            if (errorCount == 0) {
+                return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
+            } else {
+                return "";
+            }
+        }
+    }
+}

+ 14 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/ExcelListener.java

@@ -0,0 +1,14 @@
+package com.crunii.common.excel.core;
+
+import com.alibaba.excel.read.listener.ReadListener;
+
+/**
+ * Excel 导入监听
+ *
+ * @author lsa
+ */
+public interface ExcelListener<T> extends ReadListener<T> {
+
+    ExcelResult<T> getExcelResult();
+
+}

+ 30 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/core/ExcelResult.java

@@ -0,0 +1,30 @@
+package com.crunii.common.excel.core;
+
+import java.util.List;
+
+/**
+ * excel返回对象
+ *
+ * @author lsa
+ */
+public interface ExcelResult<T> {
+
+    /**
+     * 对象列表
+     */
+    List<T> getList();
+
+    /**
+     * 错误列表
+     */
+    List<T> getErrorList();
+    /**
+     * 错误列表
+     */
+    List<String> getErrorMessageList();
+
+    /**
+     * 导入回执
+     */
+    String getAnalysis();
+}

+ 26 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelMultipleSheetsWriteData.java

@@ -0,0 +1,26 @@
+package com.crunii.common.excel.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description: 动态表头多sheet的Excel导出
+ *
+ * @Author: shanbin
+ * @Date: 2023/8/10 16:40
+ */
+@Data
+public class NoModelMultipleSheetsWriteData implements Serializable {
+
+    /**
+     * 文件名称
+     */
+    private String fileName;
+
+    /**
+     * sheet数据集
+     */
+    List<NoModelSheetData> sheetDataList;
+}

+ 37 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelSheetData.java

@@ -0,0 +1,37 @@
+package com.crunii.common.excel.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: excel单页数据
+ *
+ * @Author: shanbin
+ * @Date: 2023/8/10 16:47
+ */
+@Data
+public class NoModelSheetData implements Serializable {
+
+    /**
+     * sheet名称
+     */
+    private String sheetName;
+
+    /**
+     * 表头数组
+     */
+    private String[] headNameCn;
+
+    /**
+     * 对应数据字段数组 顺序必须与headNameCn一致
+     */
+    private String[] headNameStr;
+
+    /**
+     * 数据集合
+     */
+    private List<Map<String, Object>> dataList;
+}

+ 22 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/dto/NoModelWriteData.java

@@ -0,0 +1,22 @@
+package com.crunii.common.excel.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+* @description: TODO
+* @author liuchuan
+* @date 2022/11/17 21:25
+* @version 1.0
+*/
+@Data
+public class NoModelWriteData implements Serializable {
+    private String fileName;//文件名
+    private String[] headMap;//表头数组
+    private String[] dataStrMap;//对应数据字段数组 顺序必须与headMap一致
+    private List<Map<String, Object>> dataList;//数据集合
+
+}

+ 450 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/utils/ExcelUtil.java

@@ -0,0 +1,450 @@
+package com.crunii.common.excel.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.resource.ClassPathResource;
+import cn.hutool.core.util.IdUtil;
+import com.crunii.common.excel.core.DefaultExcelListener;
+import com.crunii.common.excel.dto.NoModelMultipleSheetsWriteData;
+import com.crunii.common.excel.dto.NoModelSheetData;
+import com.crunii.common.excel.dto.NoModelWriteData;
+import com.crunii.common.excel.core.CellMergeStrategy;
+import com.crunii.common.excel.core.ExcelListener;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.fill.FillConfig;
+import com.alibaba.excel.write.metadata.fill.FillWrapper;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import com.crunii.common.excel.convert.ExcelBigNumberConvert;
+import com.crunii.common.excel.core.ExcelResult;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.*;
+
+/**
+ * Excel相关处理
+ *
+ * @author lsa
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class ExcelUtil {
+
+    /**
+     * 同步导入(适用于小数据量)
+     *
+     * @param is 输入流
+     * @return 转换后集合
+     */
+    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
+        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
+    }
+
+
+    /**
+     * 使用校验监听器 异步导入 同步返回
+     *
+     * @param is         输入流
+     * @param clazz      对象类型
+     * @param isValidate 是否 Validator 检验 默认为是
+     * @return 转换后集合
+     */
+    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
+        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
+        EasyExcel.read(is, clazz, listener).sheet().doRead();
+        return listener.getExcelResult();
+    }
+
+    /**
+     * 使用自定义监听器 异步导入 自定义返回
+     *
+     * @param is       输入流
+     * @param clazz    对象类型
+     * @param listener 自定义监听器
+     * @return 转换后集合
+     */
+    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
+        EasyExcel.read(is, clazz, listener).sheet().doRead();
+        return listener.getExcelResult();
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param clazz     实体类
+     * @param response  响应体
+     */
+    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
+        exportExcel(list, sheetName, clazz, false, response);
+    }
+
+    /**
+     * 导出excel
+     * @param list
+     * @param sheetName
+     * @param clazz
+     * @param excludeColumnFiledNames
+     * @param response
+     * @param <T>
+     */
+    public static <T> void exportExcelExclude(List<T> list, String sheetName, Class<T> clazz, Set<String> excludeColumnFiledNames, HttpServletResponse response) {
+        exportExcel(list, sheetName, clazz, false,excludeColumnFiledNames,null ,response);
+    }
+
+    /**
+     * 导出excel
+     * @param list
+     * @param sheetName
+     * @param clazz
+     * @param includeColumnFiledNames
+     * @param response
+     * @param <T>
+     */
+    public static <T> void exportExcelInclude(List<T> list, String sheetName, Class<T> clazz, Set<String> includeColumnFiledNames, HttpServletResponse response) {
+        exportExcel(list, sheetName, clazz, false,null,includeColumnFiledNames ,response);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param clazz     实体类
+     * @param merge     是否合并单元格
+     * @param response  响应体
+     */
+    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
+        try {
+            resetResponse(sheetName, response);
+            ServletOutputStream os = response.getOutputStream();
+            ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
+                .autoCloseStream(false)
+                // 自动适配
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .sheet(sheetName);
+            if (merge) {
+                // 合并处理器
+                builder.registerWriteHandler(new CellMergeStrategy(list, true));
+            }
+            builder.doWrite(list);
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
+
+    /**
+        * @description: 导出list<map<String,Object>>类型的数据
+        * @param
+        * @return:
+        * @author liuchuan
+        * @date: 2022/11/17 21:43
+    */
+    public static void noModleWrite(NoModelWriteData data, HttpServletResponse response) {
+        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异常");
+
+        }
+    }
+
+    /**
+     * 多sheet动态表头excel导出
+     *
+     * @param data 导出数据配置
+     * @param response http响应
+     */
+    public static void exportExcel(NoModelMultipleSheetsWriteData data, HttpServletResponse response) {
+        try {
+            resetResponse(data.getFileName(), response);
+            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+                .autoCloseStream(false)
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .build();
+            List<NoModelSheetData> sheetDataList = data.getSheetDataList();
+            for (int i = 0; i < sheetDataList.size(); i++) {
+                NoModelSheetData sheetData = sheetDataList.get(i);
+                //生成sheet页
+                WriteSheet sheet = EasyExcel.writerSheet(i, sheetData.getSheetName())
+                    .head(head(sheetData.getHeadNameCn()))
+                    .build();
+                //获取模型信息,向sheet写入数据
+                excelWriter.write(dataList(sheetData.getDataList(), sheetData.getHeadNameStr()), sheet);
+            }
+            excelWriter.finish();
+        } catch (Exception e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
+
+    //设置表头
+    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;
+    }
+
+    //设置导出的数据内容
+    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;
+    }
+
+    public static void dynamicHeadWrite(HttpServletResponse response, List<String> names, String sheetName, String fileName){
+        try {
+
+            //设置返回数据的值跟动态列一一对应
+//            List<List<String>> datas = setData(list,fieldEn);
+            resetResponse(sheetName, response);
+            EasyExcel.write(response.getOutputStream())
+                .head(headData(CollectionUtil.isNotEmpty(names) ? names.toArray(new String[0]) : new String[0]))
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                .sheet(sheetName)
+                .doWrite(new ArrayList());
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            response.reset();
+            response.setCharacterEncoding("utf-8");
+            response.setContentType("application/json");
+            try {
+                response.getWriter().println("打印失败");
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+    /**
+     * 数据动态头传入
+     */
+    private static List<List<String>> headData(String[] header) {
+        List<String> head0 = null;
+        List<List<String>> list = new LinkedList<List<String>>();
+        for (String h : header) {
+            head0 = new LinkedList<>();
+            head0.add(h);
+            list.add(head0);
+        }
+        return list;
+    }
+
+    /**
+     * 导出excel
+     * @param list
+     * @param sheetName
+     * @param clazz
+     * @param merge
+     * @param excludeColumnFiledNames
+     * @param includeColumnFiledNames
+     * @param response
+     * @param <T>
+     */
+    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, Set<String> excludeColumnFiledNames,Set<String> includeColumnFiledNames,HttpServletResponse response) {
+        try {
+            resetResponse(sheetName, response);
+            ServletOutputStream os = response.getOutputStream();
+            ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
+                .autoCloseStream(false)
+                // 自动适配
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .sheet(sheetName);
+            if (CollectionUtil.isNotEmpty(excludeColumnFiledNames)) {
+                builder.excludeColumnFieldNames(excludeColumnFiledNames);
+            }
+            if (CollectionUtil.isNotEmpty(includeColumnFiledNames)) {
+                builder.includeColumnFieldNames(includeColumnFiledNames);
+            }
+            if (merge) {
+                // 合并处理器
+                builder.registerWriteHandler(new CellMergeStrategy(list, true));
+            }
+            builder.doWrite(list);
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
+
+    /**
+     * 单表多数据模板导出 模板格式为 {.属性}
+     *
+     * @param filename     文件名
+     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+     *                     例如: excel/temp.xlsx
+     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
+     * @param data         模板需要的数据
+     */
+    public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
+        try {
+            resetResponse(filename, response);
+            ClassPathResource templateResource = new ClassPathResource(templatePath);
+            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+                .withTemplate(templateResource.getStream())
+                .autoCloseStream(false)
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .build();
+            WriteSheet writeSheet = EasyExcel.writerSheet().build();
+            if (CollUtil.isEmpty(data)) {
+                throw new IllegalArgumentException("数据为空");
+            }
+            // 单表多数据导出 模板格式为 {.属性}
+            for (Object d : data) {
+                excelWriter.fill(d, writeSheet);
+            }
+            excelWriter.finish();
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
+
+    /**
+     * 多表多数据模板导出 模板格式为 {key.属性}
+     *
+     * @param filename     文件名
+     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+     *                     例如: excel/temp.xlsx
+     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
+     * @param data         模板需要的数据
+     */
+    public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
+        try {
+            resetResponse(filename, response);
+            ClassPathResource templateResource = new ClassPathResource(templatePath);
+            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+                .withTemplate(templateResource.getStream())
+                .autoCloseStream(false)
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .build();
+            WriteSheet writeSheet = EasyExcel.writerSheet().build();
+            if (CollUtil.isEmpty(data)) {
+                throw new IllegalArgumentException("数据为空");
+            }
+            for (Map.Entry<String, Object> map : data.entrySet()) {
+                // 设置列表后续还有数据
+                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
+                if (map.getValue() instanceof Collection) {
+                    // 多表导出必须使用 FillWrapper
+                    excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
+                } else {
+                    excelWriter.fill(map.getValue(), writeSheet);
+                }
+            }
+            excelWriter.finish();
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
+
+    /**
+     * 重置响应体
+     */
+    private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
+        String filename = encodingFilename(sheetName);
+        response.reset();
+        FileUtils.setAttachmentResponseHeader(response, filename);
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+    }
+
+    /**
+     * 解析导出值 0=男,1=女,2=未知
+     *
+     * @param propertyValue 参数值
+     * @param converterExp  翻译注解
+     * @param separator     分隔符
+     * @return 解析后值
+     */
+    public static String convertByExp(String propertyValue, String converterExp, String separator) {
+        StringBuilder propertyString = new StringBuilder();
+        String[] convertSource = converterExp.split(",");
+        for (String item : convertSource) {
+            String[] itemArray = item.split("=");
+            if (StringUtils.containsAny(propertyValue, separator)) {
+                for (String value : propertyValue.split(separator)) {
+                    if (itemArray[0].equals(value)) {
+                        propertyString.append(itemArray[1] + separator);
+                        break;
+                    }
+                }
+            } else {
+                if (itemArray[0].equals(propertyValue)) {
+                    return itemArray[1];
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 反向解析值 男=0,女=1,未知=2
+     *
+     * @param propertyValue 参数值
+     * @param converterExp  翻译注解
+     * @param separator     分隔符
+     * @return 解析后值
+     */
+    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
+        StringBuilder propertyString = new StringBuilder();
+        String[] convertSource = converterExp.split(",");
+        for (String item : convertSource) {
+            String[] itemArray = item.split("=");
+            if (StringUtils.containsAny(propertyValue, separator)) {
+                for (String value : propertyValue.split(separator)) {
+                    if (itemArray[1].equals(value)) {
+                        propertyString.append(itemArray[0] + separator);
+                        break;
+                    }
+                }
+            } else {
+                if (itemArray[1].equals(propertyValue)) {
+                    return itemArray[0];
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 编码文件名
+     */
+    public static String encodingFilename(String filename) {
+        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
+    }
+
+}

+ 54 - 0
micro-common-component/micro-common-excel/src/main/java/com/crunii/common/excel/utils/FileUtils.java

@@ -0,0 +1,54 @@
+package com.crunii.common.excel.utils;
+
+import cn.hutool.core.io.FileUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 文件处理工具类
+ *
+ * @author lsa
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class FileUtils extends FileUtil {
+
+    /**
+     * 下载文件名重新编码
+     *
+     * @param response     响应对象
+     * @param realFileName 真实文件名
+     * @return
+     */
+    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
+        String percentEncodedFileName = percentEncode(realFileName);
+
+        StringBuilder contentDispositionValue = new StringBuilder();
+        contentDispositionValue.append("attachment; filename=")
+            .append(percentEncodedFileName)
+            .append(";")
+            .append("filename*=")
+            .append("utf-8''")
+            .append(percentEncodedFileName);
+
+        response.addHeader("Access-Control-Allow-Origin", "*");
+        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
+        response.setHeader("Content-disposition", contentDispositionValue.toString());
+        response.setHeader("download-filename", percentEncodedFileName);
+    }
+
+    /**
+     * 百分号编码工具方法
+     *
+     * @param s 需要百分号编码的字符串
+     * @return 百分号编码后的字符串
+     */
+    public static String percentEncode(String s) throws UnsupportedEncodingException {
+        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+        return encode.replaceAll("\\+", "%20");
+    }
+}

+ 1 - 0
micro-common-component/micro-common-excel/src/main/resources/META-INF/spring.factories

@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=

+ 51 - 0
micro-common-component/micro-common-swagger/pom.xml

@@ -0,0 +1,51 @@
+<?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-swagger</artifactId>
+
+    <description>
+        micro-common-swagger系统接口
+    </description>
+
+	<dependencies>
+
+        <!-- SpringBoot Web -->
+        <dependency>
+            <groupId>com.crunii.microservice</groupId>
+            <artifactId>micro-common-web</artifactId>
+            <version>${micro.version}</version>
+        </dependency>
+
+        <!-- knife4j -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-micro-spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>mapstruct</artifactId>
+                    <groupId>org.mapstruct</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 129 - 0
micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/SwaggerAutoConfiguration.java

@@ -0,0 +1,129 @@
+package com.crunii.micro.common.swagger.config;
+
+import com.crunii.micro.common.swagger.config.properties.SwaggerProperties;
+import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+import io.swagger.models.auth.In;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
+import springfox.documentation.spring.web.plugins.Docket;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Swagger 文档配置
+ *
+ * @author lsa
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableKnife4j
+@EnableConfigurationProperties(SwaggerProperties.class)
+@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
+public class SwaggerAutoConfiguration {
+
+    public static final String TOKEN_NAME = "token";
+    @Value("${spring.application.name}")
+    private String appName;
+
+    /**
+     * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
+     */
+    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
+
+    private static final String BASE_PATH = "/**";
+
+    @Bean
+    public Docket api(SwaggerProperties swaggerProperties) {
+        // base-path处理
+        if (swaggerProperties.getBasePath().isEmpty()) {
+            swaggerProperties.getBasePath().add(BASE_PATH);
+        }
+        // noinspection unchecked
+        List<Predicate<String>> basePath = new ArrayList<>();
+        swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
+
+        // exclude-path处理
+        if (swaggerProperties.getExcludePath().isEmpty()) {
+            swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
+        }
+
+        List<Predicate<String>> excludePath = new ArrayList<>();
+        swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
+
+        ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30).groupName("ycq")
+            .enable(swaggerProperties.getEnabled())
+            .host(swaggerProperties.getHost())
+            .apiInfo(apiInfo(swaggerProperties))
+            .select()
+            .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
+
+        swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
+        swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
+
+        return builder.build()
+            .securitySchemes(securitySchemes())
+            .securityContexts(securityContexts())
+            .pathMapping(StringUtils.substring(appName, appName.indexOf("-") + 1));
+    }
+
+    /**
+     * 安全模式,这里指定token通过Authorization头请求头传递
+     */
+    private List<SecurityScheme> securitySchemes() {
+        List<SecurityScheme> apiKeyList = new ArrayList<>();
+        List<SecurityScheme> apiKeyList1 = new ArrayList<>();
+        String header = TOKEN_NAME;
+        apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue()));
+        return apiKeyList;
+    }
+
+    /**
+     * 安全上下文
+     */
+    private List<SecurityContext> securityContexts() {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+            SecurityContext.builder()
+                .securityReferences(defaultAuth())
+                .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
+                .build());
+        return securityContexts;
+    }
+
+    /**
+     * 默认的全局鉴权策略
+     */
+    private List<SecurityReference> defaultAuth() {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference(TOKEN_NAME, authorizationScopes));
+        return securityReferences;
+    }
+
+    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
+        return new ApiInfoBuilder()
+            .title(swaggerProperties.getTitle())
+            .description(swaggerProperties.getDescription())
+            .license(swaggerProperties.getLicense())
+            .licenseUrl(swaggerProperties.getLicenseUrl())
+            .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
+            .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
+            .version(swaggerProperties.getVersion())
+            .build();
+    }
+}

+ 41 - 0
micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/SwaggerBeanPostProcessor.java

@@ -0,0 +1,41 @@
+package com.crunii.micro.common.swagger.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+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;
+
+/**
+ * swagger 在 springboot 2.6.x 不兼容问题的处理
+ *
+ * @author lsa
+ */
+@SuppressWarnings("all")
+public class SwaggerBeanPostProcessor implements 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) {
+        mappings.removeIf(mapping -> mapping.getPatternParser() != null);
+    }
+
+    private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
+        try {
+            Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
+            field.setAccessible(true);
+            return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+}

+ 143 - 0
micro-common-component/micro-common-swagger/src/main/java/com/crunii/micro/common/swagger/config/properties/SwaggerProperties.java

@@ -0,0 +1,143 @@
+package com.crunii.micro.common.swagger.config.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * swagger 配置属性
+ *
+ * @author lsa
+ */
+@Data
+@ConfigurationProperties("swagger")
+public class SwaggerProperties {
+
+    /**
+     * 是否开启swagger
+     */
+    private Boolean enabled;
+
+    /**
+     * swagger会解析的包路径
+     **/
+    private String basePackage = "";
+
+    /**
+     * swagger会解析的url规则
+     **/
+    private List<String> basePath = new ArrayList<>();
+
+    /**
+     * 在basePath基础上需要排除的url规则
+     **/
+    private List<String> excludePath = new ArrayList<>();
+
+    /**
+     * 标题
+     **/
+    private String title = "";
+
+    /**
+     * 描述
+     **/
+    private String description = "";
+
+    /**
+     * 版本
+     **/
+    private String version = "";
+
+    /**
+     * 许可证
+     **/
+    private String license = "";
+
+    /**
+     * 许可证URL
+     **/
+    private String licenseUrl = "";
+
+    /**
+     * 服务条款URL
+     **/
+    private String termsOfServiceUrl = "";
+
+    /**
+     * host信息
+     **/
+    private String host = "";
+
+    /**
+     * 联系人信息
+     */
+    private Contact contact = new Contact();
+
+    /**
+     * 全局统一鉴权配置
+     **/
+    private Authorization authorization = new Authorization();
+
+    @Data
+    @NoArgsConstructor
+    public static class Contact {
+
+        /**
+         * 联系人
+         **/
+        private String name = "";
+        /**
+         * 联系人url
+         **/
+        private String url = "";
+        /**
+         * 联系人email
+         **/
+        private String email = "";
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    public static class Authorization {
+
+        /**
+         * 鉴权策略ID,需要和SecurityReferences ID保持一致
+         */
+        private String name = "";
+
+        /**
+         * 需要开启鉴权URL的正则
+         */
+        private String authRegex = "^.*$";
+
+        /**
+         * 鉴权作用域列表
+         */
+        private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
+
+        private List<String> tokenUrlList = new ArrayList<>();
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    public static class AuthorizationScope {
+
+        /**
+         * 作用域名称
+         */
+        private String scope = "";
+
+        /**
+         * 作用域描述
+         */
+        private String description = "";
+
+    }
+
+}

+ 3 - 0
micro-common-component/micro-common-swagger/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.crunii.micro.common.swagger.config.SwaggerAutoConfiguration,\
+  com.crunii.micro.common.swagger.config.SwaggerBeanPostProcessor

+ 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));
+	}
+
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.