diff --git a/distributed-demos/distribuited-lock-demos/pom.xml b/distributed-demos/distribuited-lock-demos/pom.xml index 4ebba5f057e55d20f340730149437fcd7fbc8dee..6a262a8479a6f9b514eb050fdf43d937ea989cf9 100644 --- a/distributed-demos/distribuited-lock-demos/pom.xml +++ b/distributed-demos/distribuited-lock-demos/pom.xml @@ -13,6 +13,7 @@ pom redis-lock-demo + redisson-sample diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/pom.xml b/distributed-demos/distribuited-lock-demos/redisson-sample/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..12b30a3e3ec4b2aced4759ab880ff018c9163915 --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/pom.xml @@ -0,0 +1,35 @@ + + + + distribuited-lock-demos + com.gsean.demos + 1.0-SNAPSHOT + + 4.0.0 + + redisson-sample + + + 8 + 8 + + + + + org.redisson + redisson + 3.15.6 + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.alibaba + fastjson + + + + \ No newline at end of file diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/RedissonSampleApp.java b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/RedissonSampleApp.java new file mode 100644 index 0000000000000000000000000000000000000000..052d662221eee4e849eb4618f92e2a33877531ec --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/RedissonSampleApp.java @@ -0,0 +1,19 @@ +package com.gsean.redisson; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/15 16:50 + * @modificed by + **/ +@SpringBootApplication +public class RedissonSampleApp { + + public static void main(String[] args) { + SpringApplication.run(RedissonSampleApp.class,args); + } + +} diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/config/RedisConfig.java b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/config/RedisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..8631a1502d596d0b132b73e1b98fadf1a4a53707 --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/config/RedisConfig.java @@ -0,0 +1,45 @@ +package com.gsean.redisson.config; + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/15 17:46 + * @modificed by + **/ +@Configuration +public class RedisConfig { + + @Autowired + private Environment env; + + /** + * 自定义注入配置操作Redisson的客户端实例 + * @return + */ + @Bean + public RedissonClient config(){ + //创建配置实例 + Config config=new Config(); + //可以设置传输模式为EPOLL,也可以设置为NIO等等 + //config.setTransportMode(TransportMode.NIO); + //设置服务节点部署模式:集群模式;单一节点模式;主从模式;哨兵模式等等 + //config.useClusterServers().addNodeAddress(env.getProperty("redisson.host.config"),env.getProperty("redisson.host.config")); + config + .useSingleServer() + .setAddress(env.getProperty("redisson.host.config")) + .setPassword(env.getProperty("spring.redis.password")) + .setKeepAlive(true); + //创建并返回操作Redisson的客户端实例 + return Redisson.create(config); + } + +} diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/service/UserService.java b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/service/UserService.java new file mode 100644 index 0000000000000000000000000000000000000000..dc3b4b7393cc7d105b31a48056a4ff53bda7d315 --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/service/UserService.java @@ -0,0 +1,15 @@ +package com.gsean.redisson.service; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/15 18:33 + * @modificed by + **/ +public interface UserService { + + + + Long addUserAndGet(); + +} diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/utils/LockUtil.java b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/utils/LockUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..af5e5f40bbef985d3ff0fd7ff39a1fbc4e733bf6 --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/java/com/gsean/redisson/utils/LockUtil.java @@ -0,0 +1,69 @@ +package com.gsean.redisson.utils; + +import java.util.concurrent.TimeUnit; +import javax.xml.ws.RequestWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author linxin.xiong + * @description 分布式锁工具类 + * @date created in 2021/6/21 11:22 + * @modificed by + */ +@Slf4j +@Component +public class LockUtil { + + private static RedissonClient redissonClient; + + @Autowired + public void setRedissonClient(RedissonClient redissonClient) { + LockUtil.redissonClient = redissonClient; + } + + + /** + * 获取分布式redis锁并执行runnable. + * 多个线程获取锁失败时会等待指定时间,仍未获取到锁则跳过执行. + * + * @param lockName lockName + * @param runnable operations after locked + * @param waitTime the maximum time to acquire the lock + * @param leaseTime lock expiration time + */ + public static void tryLockAndExecute(String lockName, Runnable runnable, long waitTime, long leaseTime) { + RLock lock = redissonClient.getLock(lockName); + boolean locked = false; + try { + locked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); + } catch (InterruptedException ex) { + log.error("LockUtil#tryLockAndExecute() interrupted... lockName={}", lockName, ex); + Thread.currentThread().interrupt(); + } + if (locked) { + // 不捕获异常,由调用层控制吞并异常或事务回滚 + try { + runnable.run(); + } finally { + lock.unlock(); + } + } + } + + /** + * 获取分布式redis锁并执行runnable. + * 多个线程获取锁失败时没有等待时间,直接跳过,适用于只需获取一次锁的场景,如定时任务. + * + * @param lockName lockName + * @param runnable operations after locked + */ + public static void tryLockWithoutWaiting(String lockName, Runnable runnable) { + tryLockAndExecute(lockName,runnable,0,60); + } + +} diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/resources/application.yml b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..aeb46817860585fbf1a8e0ccb3e76ab54a41da5e --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/main/resources/application.yml @@ -0,0 +1,9 @@ +redisson: + host: + config: redis://127.0.0.1:6379 + +spring: + redis: + password: 123456 + + diff --git a/distributed-demos/distribuited-lock-demos/redisson-sample/src/test/java/com/gsean/redisson/RedissonSampleAppTest.java b/distributed-demos/distribuited-lock-demos/redisson-sample/src/test/java/com/gsean/redisson/RedissonSampleAppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..614e828fcea6e790f95342a1f542e4136df1b45b --- /dev/null +++ b/distributed-demos/distribuited-lock-demos/redisson-sample/src/test/java/com/gsean/redisson/RedissonSampleAppTest.java @@ -0,0 +1,43 @@ +package com.gsean.redisson; + +import static org.junit.Assert.*; + +import com.alibaba.fastjson.JSON; +import io.lettuce.core.RedisClient; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/15 17:55 + * @modificed by + **/ +@SpringBootTest +@RunWith(SpringJUnit4ClassRunner.class) +public class RedissonSampleAppTest { + + + @Autowired + private RedissonClient redissonClient; + + + /** + * + * 获取配置 + * @author: Jincheng.Guo11 + * @date: 2021/11/15 18:09 + * @return: void + * @throws: java.lang.Exception + * @modificed by: + */ + @Test + public void testA(){ + System.out.println(JSON.toJSONString(redissonClient.getConfig())); + } + +} \ No newline at end of file diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/pom.xml b/mq-demos/rocketmq-demos/rocketmq-sample/pom.xml index db7e13d065cbf709a17a2f26e367c6039ad3f974..e1816955ffc538242196c3f23a064534fe579002 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/pom.xml +++ b/mq-demos/rocketmq-demos/rocketmq-sample/pom.xml @@ -27,6 +27,7 @@ org.springframework.boot spring-boot-starter-web + \ No newline at end of file diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/MessageQueueChannels.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/MessageQueueChannels.java index f9ec1a6817e0f37333f2e167fd413de3cdacc339..da8e065e1320e4a7012101b75a907185c922af83 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/MessageQueueChannels.java +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/MessageQueueChannels.java @@ -12,25 +12,25 @@ import org.springframework.messaging.SubscribableChannel; */ public interface MessageQueueChannels { - String WECHAT_CODE_PRE_LOAD_OUTPUT = "gsean-test-output"; - String WECHAT_CODE_PRE_LOAD_INPUT = "gsean-test-input"; + String USER_PAYLOD_OUTPUT = "user-payload-output"; + String USER_PAYLOD_INPUT = "user-payload-input"; /** - * 二维码预加载事件发送通道. + * 用户负载事件发送通道. * * @return MessageChannel */ - @Output(WECHAT_CODE_PRE_LOAD_OUTPUT) - MessageChannel wechatCodePreLoadOutput(); + @Output(USER_PAYLOD_OUTPUT) + MessageChannel userPaylodOutput(); /** - * 二维码预加载事件接收通道. + * 用户负载事件接收通道. * * @return SubscribableChannel */ - @Input(WECHAT_CODE_PRE_LOAD_INPUT) - SubscribableChannel wechatCodePreLoadInput(); + @Input(USER_PAYLOD_INPUT) + SubscribableChannel userPayloadInput(); } diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueConsumer.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueConsumer.java index e44dabfa3f57d34c7a27f546f768d35b98165c1a..b00d373bf550e89407553e707160e1906be5b287 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueConsumer.java +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueConsumer.java @@ -1,32 +1,43 @@ -//package com.gsean.rocket.config; -// -//import lombok.RequiredArgsConstructor; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.cloud.stream.annotation.EnableBinding; -//import org.springframework.cloud.stream.annotation.StreamListener; -//import org.springframework.stereotype.Service; -// -///** -// * 消息队列消费. -// * -// * @author linxin.xiong -// */ -//@Slf4j -//@Service -//@EnableBinding({MessageQueueChannels.class}) -//@RequiredArgsConstructor -//public class QueueConsumer { -// -// -// /** -// * 处理二维码预加载消息. -// * -// * @param message 二维码预加载 -// */ -// @StreamListener(MessageQueueChannels.WECHAT_CODE_PRE_LOAD_INPUT) -// public void wechatCodeUrlPreLoadMessageHandler(String message) { -// System.out.println(message); -// } -// -// -//} +package com.gsean.rocket.config; + +import com.alibaba.fastjson.JSON; +import com.gsean.rocket.dto.UserPayload; +import java.util.function.Consumer; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.stereotype.Component; + +/** + * 消息队列消费. + * + * @author linxin.xiong + */ +@Slf4j +@Component +public class QueueConsumer { + + + /** + * 用户负载消费消息. + * + * @param message 二维码预加载 + */ + @StreamListener(MessageQueueChannels.USER_PAYLOD_INPUT) + public void userPayloadMessageHandler(@Payload UserPayload message) { + log.info("用户负载消费:{}", JSON.toJSONString(message)); +// if(message.getId().equals(1L)){ +// log.error("消费失败"); +// throw new RuntimeException("消费失败"); +// } + log.info("消费成功"); + } + + + + + + + +} diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueProducer.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueProducer.java index d0b5b21e120cc95f604269b83453bd278d93f22e..511eb66d852a12d8a5405c88ff8586502674305b 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueProducer.java +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/QueueProducer.java @@ -1,7 +1,9 @@ package com.gsean.rocket.config; +import com.gsean.rocket.dto.UserPayload; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageConst; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; @@ -15,22 +17,31 @@ import org.springframework.stereotype.Service; @Slf4j @Service public class QueueProducer { + @Autowired + private MessageQueueChannels messageQueueChannels; - @Resource(name = MessageQueueChannels.WECHAT_CODE_PRE_LOAD_OUTPUT) - private MessageChannel wechatCodePreLoadOutput; - -// @Resource(name = com.geely.app.config.rocketmq.MessageQueueChannels.FABULOUS_CREATE_OUTPUT) -// private MessageChannel fabulousCreateOutput; + public void sendUserPayload(UserPayload payload) { + boolean send = messageQueueChannels.userPaylodOutput() + .send(MessageBuilder.withPayload(payload).build()); + log.info("发送用户负载状态:{}",send); + } - public void sendWechatCodePreLoadMessage(Object msg) { - try { - boolean send = wechatCodePreLoadOutput.send(MessageBuilder.withPayload(msg).build()); - if (!send) { - log.error("预生成分享二维码消息发送失败..."); - } - } catch (Exception ex) { - log.error("预生成分享二维码消息发送异常...", ex); - } + /** + * 发送延迟消息 + * + * @param payload + * @author: Jincheng.Guo11 + * @date: 2021/11/19 17:00 + * @return: void + * @throws: java.lang.Exception + * @modificed by: + */ + public void sendDelayUserPayload(UserPayload payload) { + boolean send = messageQueueChannels.userPaylodOutput() + .send(MessageBuilder.withPayload(payload) + .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") + .build()); + log.info("发送延时用户负载状态:{}",send); } diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/TransactionListenerImpl.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/TransactionListenerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..87594f2c1cf0bc69e58e6937e85ba8e29e61e47b --- /dev/null +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/config/TransactionListenerImpl.java @@ -0,0 +1,36 @@ +package com.gsean.rocket.config; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; +import org.springframework.messaging.Message; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/19 11:43 + * @modificed by + **/ +@Slf4j +@RocketMQTransactionListener(txProducerGroup = "test") +public class TransactionListenerImpl implements RocketMQLocalTransactionListener { + + @Override + public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) { + // 从消息 Header 中解析到 args 参数,并使用 JSON 反序列化 + String args = JSON.parseObject(message.getHeaders().get("args", String.class), + String.class); + // ... local transaction process, return rollback, commit or unknown + log.info("[executeLocalTransaction][执行本地事务,消息:{} args:{}]", message, args); + return RocketMQLocalTransactionState.UNKNOWN; + } + + @Override + public RocketMQLocalTransactionState checkLocalTransaction(Message message) { + // ... check transaction status and return rollback, commit or unknown + log.info("[checkLocalTransaction][回查消息:{}]", message); + return RocketMQLocalTransactionState.COMMIT; + } +} diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/dto/UserPayload.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/dto/UserPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..a49524d8150a4fdd474667a02da5e6e8cc687331 --- /dev/null +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/java/com/gsean/rocket/dto/UserPayload.java @@ -0,0 +1,21 @@ +package com.gsean.rocket.dto; + +import java.time.LocalDate; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/16 14:24 + * @modificed by + **/ +@Data +@Accessors(chain = true) +public class UserPayload { + + private Long id; + private String name; + private LocalDate birth; + +} diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v1.yml b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v1.yml new file mode 100644 index 0000000000000000000000000000000000000000..b2f6c980a56f7487afc1ae84b048a21b0d4d4520 --- /dev/null +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v1.yml @@ -0,0 +1,33 @@ +# RocketMQ配置 简单消息配置 +spring: + cloud: + # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 + stream: + # Spring Cloud Stream RocketMQ 配置项 + rocketmq: + # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 + binder: + # RocketMQ Namesrv 地址 +# name-server: 10.113.75.163:9876;10.113.75.164:9876; +# name-server: 127.0.0.1:9876 + name-server: 10.113.72.232:9876 + bindings: + user-paylod-input: + consumer: + enabled: true #默认是true,开发可以false +# broadcasting: true #是否开启广播消费,默认集群消费 +# delay-level-when-next-consume: -1 + + # Binding 配置项,对应 BindingProperties Map + bindings: + #定义name为gsean-test-output的binding + user-payload-output: + destination: USERPAYLOAD-TOPIC + content-type: application/json + #定义name为gsean-test-input的binding + user-payload-input: + destination: USERPAYLOAD-TOPIC + group: user-payload-group-01 + content-type: application/json + + diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v2.yml b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v2.yml new file mode 100644 index 0000000000000000000000000000000000000000..3f5072d548a6f82c427555b3f824f1b7bc91be65 --- /dev/null +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application-v2.yml @@ -0,0 +1,31 @@ +# RocketMQ配置 事务消息配置 +spring: + cloud: + # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 + stream: + # Spring Cloud Stream RocketMQ 配置项 + rocketmq: + # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 + binder: + # RocketMQ Namesrv 地址 + name-server: 10.113.75.163:9876;10.113.75.164:9876; + bindings: + user-payload-output: + producer: + group: test + sync: true + transactional: true + + # Binding 配置项,对应 BindingProperties Map + bindings: + #定义name为gsean-test-output的binding + user-payload-output: + destination: TRANSACTION-TOPIC + content-type: application/json + #定义name为gsean-test-input的binding + user-payload-input: + destination: TRANSACTION-TOPIC + group: user-payload-group-02 + content-type: application/json + + diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application.yml b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application.yml index 45b8c222b447a33164c1a423f990d6d4971df8b9..8b22b1756ac35995bcf7662f29c2988b42b4c0db 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application.yml +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/main/resources/application.yml @@ -1,49 +1,6 @@ +server: + port: ${random.int[10000,19999]} -# 短信验证码请求域名(网关地址) -code: - url: http://gateway.cloud-dev.geega.com - -# 助力分享二维码前端页面地址 -fabulous: - share: - page: "package/pages/questions" - -item: - id: "116800100090001" - -store: - id: "2" - -# 小程序appId和appsecret -wechat: - # appId: wx12927de7e3a4dbf2 - # appsecret: 35555a9e98c806654df1176b9b30d3ce - appId: wxce8456066fec3a6a - appsecret: a7cb5f3890e4f4eb3e856aaad3f61823 - -# RocketMQ配置 spring: - cloud: - # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 - stream: - # Spring Cloud Stream RocketMQ 配置项 - rocketmq: - # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 - binder: - # RocketMQ Namesrv 地址 - name-server: 10.113.75.163:9876;10.113.75.164:9876; - # Binding 配置项,对应 BindingProperties Map - bindings: - #定义name为gsean-test-output的binding - gsean-test-output: - destination: TEST-TOPIC - content-type: application/json - #定义name为gsean-test-input的binding - gsean-test-input: - destination: TEST-TOPIC - group: test-group - content-type: application/json - - -server: - port: 8082 + profiles: + active: v1 diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/MyTest.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/MyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4fa9908d54b4e998ab1cac2edec2c44b047fa7f2 --- /dev/null +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/MyTest.java @@ -0,0 +1,25 @@ +package com.gsean.rocket; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.test.annotation.Repeat; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/16 14:52 + * @modificed by + **/ +public class MyTest { + + + + + + @Test + @Repeat(3) + public void test1(){ + System.out.println("hello"); + } + +} diff --git a/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/RocketMqAppTest.java b/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/RocketMqAppTest.java index 6be2ee099b1ce474783a5a1a0e1fdffe61dee735..79e441859fa434b07a19763c991943b33d66f4e3 100644 --- a/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/RocketMqAppTest.java +++ b/mq-demos/rocketmq-demos/rocketmq-sample/src/test/java/com/gsean/rocket/RocketMqAppTest.java @@ -1,10 +1,14 @@ package com.gsean.rocket; import com.gsean.rocket.config.QueueProducer; +import com.gsean.rocket.dto.UserPayload; +import java.time.LocalDate; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; /** @@ -15,14 +19,37 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; **/ @SpringBootTest @ExtendWith(SpringExtension.class) +@ActiveProfiles("v1") class RocketMqAppTest { @Autowired private QueueProducer queueProducer; + + @Test - public void testA(){ - queueProducer.sendWechatCodePreLoadMessage("hello gsean"); + public void testSendUserPayload(){ +// Stream.iterate(0,x->x+1).limit(3).forEachOrdered(item->{ + UserPayload userPayload = new UserPayload().setId(1L).setName("gsean"+1).setBirth(LocalDate.now()); + queueProducer.sendUserPayload(userPayload); +// }); + } + /** + * + * 延时消息测试 + * @author: Jincheng.Guo11 + * @date: 2021/11/19 17:01 + * @return: void + * @throws: java.lang.Exception + * @modificed by: + */ + @Test + public void testDelayMsg(){ + UserPayload userPayload = new UserPayload().setId(1L).setName("delay").setBirth(LocalDate.now()); + queueProducer.sendDelayUserPayload(userPayload); } + + + } \ No newline at end of file diff --git a/redis-demos/redis-sampleV2/src/main/java/com/gsean/redis/config/RedisConfig.java b/redis-demos/redis-sampleV2/src/main/java/com/gsean/redis/config/RedisConfig.java index 0ec00221a49bcf368fb016a72dcaf7b6c1dac184..8e1b379c43c76a092c7be2b90cd9c101439b21b5 100644 --- a/redis-demos/redis-sampleV2/src/main/java/com/gsean/redis/config/RedisConfig.java +++ b/redis-demos/redis-sampleV2/src/main/java/com/gsean/redis/config/RedisConfig.java @@ -13,6 +13,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** @@ -29,6 +30,8 @@ public class RedisConfig { @Autowired private LettuceConnectionFactory connectionFactory; + + @Bean public RedisTemplate redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); diff --git a/swagger-demo/swagger-sample/src/main/java/com/gsean/swagger/SwaggerSampleApplication.java b/swagger-demo/swagger-sample/src/main/java/com/gsean/swagger/SwaggerSampleApplication.java index 49891e9e9710e936fe2b6c64f7836ef44f792cb1..9a95ce7e84a468a92544c2339e6c02b06cb1ae47 100644 --- a/swagger-demo/swagger-sample/src/main/java/com/gsean/swagger/SwaggerSampleApplication.java +++ b/swagger-demo/swagger-sample/src/main/java/com/gsean/swagger/SwaggerSampleApplication.java @@ -19,7 +19,5 @@ import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfi public class SwaggerSampleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SwaggerSampleApplication.class, args); - Swagger2DocumentationConfiguration bean = run.getBean(Swagger2DocumentationConfiguration.class); - System.out.println(bean==null); } } diff --git a/swagger-demo/swagger-ui-demo/pom.xml b/swagger-demo/swagger-ui-demo/pom.xml index 6d998ee34ff6fb566f5ee01c68e44f5dec3fbfce..e9ce1047dd7de6e00c32e1444bbf47a054525a3e 100644 --- a/swagger-demo/swagger-ui-demo/pom.xml +++ b/swagger-demo/swagger-ui-demo/pom.xml @@ -64,5 +64,14 @@ swagger-bootstrap-ui 1.9.1 + + org.apache.commons + commons-lang3 + + + org.springframework.boot + spring-boot-starter-test + runtime + \ No newline at end of file diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumModelPropertyBuilderPlugin.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumModelPropertyBuilderPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..4b3a5517849bdef8af5fa4520aeab5ee709fd738 --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumModelPropertyBuilderPlugin.java @@ -0,0 +1,84 @@ +package com.gsean.swaggerui.config; + +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.google.common.base.Optional; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import springfox.documentation.builders.ModelPropertyBuilder; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; +import springfox.documentation.spi.schema.contexts.ModelPropertyContext; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 10:10 + * @modificed by + **/ +@Component +public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin +{ + + @Override + public void apply(ModelPropertyContext context) { + Optional optional = context.getBeanPropertyDefinition(); + if (!optional.isPresent()) { + return; + } + + final Class fieldType = optional.get().getField().getRawType(); + + addDescForEnum(context, fieldType); + } + + @Override + public boolean supports(DocumentationType documentationType) { + return true; + } + + private void addDescForEnum(ModelPropertyContext context, Class fieldType) { + if (Enum.class.isAssignableFrom(fieldType)) { + SwaggerDisplayEnum annotation = AnnotationUtils + .findAnnotation(fieldType, SwaggerDisplayEnum.class); + if (annotation != null) { + String index = annotation.index(); + String name = annotation.name(); + + Object[] enumConstants = fieldType.getEnumConstants(); + + List displayValues = + Arrays.stream(enumConstants) + .filter(Objects::nonNull) + .map(item -> { + Class currentClass = item.getClass(); + + Field indexField = ReflectionUtils.findField(currentClass, index); + ReflectionUtils.makeAccessible(indexField); + Object value = ReflectionUtils.getField(indexField, item); + + Field descField = ReflectionUtils.findField(currentClass, name); + ReflectionUtils.makeAccessible(descField); + Object desc = ReflectionUtils.getField(descField, item); + return value + ":" + desc; + + }).collect(Collectors.toList()); + + + ModelPropertyBuilder builder = context.getBuilder(); + Field descField = ReflectionUtils.findField(builder.getClass(), "description"); + ReflectionUtils.makeAccessible(descField); + String joinText = ReflectionUtils.getField(descField, builder) + + " (" + String.join("; ", displayValues) + ")"; + + builder.description(joinText).type(context.getResolver().resolve(Integer.class)); + } + } + + } +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumParameterBuilderPlugin.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumParameterBuilderPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..f0bf5aa9ae653635c53e1ab3c971d80113727648 --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/EnumParameterBuilderPlugin.java @@ -0,0 +1,123 @@ +package com.gsean.swaggerui.config; + +import com.fasterxml.classmate.ResolvedType; +import com.google.common.base.Joiner; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import springfox.documentation.builders.OperationBuilder; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.service.AllowableListValues; +import springfox.documentation.service.Parameter; +import springfox.documentation.service.ResolvedMethodParameter; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.OperationBuilderPlugin; +import springfox.documentation.spi.service.ParameterBuilderPlugin; +import springfox.documentation.spi.service.contexts.OperationContext; +import springfox.documentation.spi.service.contexts.ParameterContext; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 10:13 + * @modificed by + **/ +@Component +public class EnumParameterBuilderPlugin implements ParameterBuilderPlugin, OperationBuilderPlugin { + private static final Joiner joiner = Joiner.on(","); + + @Override + public void apply(OperationContext context) { + Map> map = new HashMap<>(); + List parameters = context.getParameters(); + parameters.forEach(parameter -> { + ResolvedType parameterType = parameter.getParameterType(); + Class clazz = parameterType.getErasedType(); + if (Enum.class.isAssignableFrom(clazz)) { + SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(clazz, SwaggerDisplayEnum.class); + if (annotation != null) { + String index = annotation.index(); + String name = annotation.name(); + Object[] enumConstants = clazz.getEnumConstants(); + + List displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> { + Class currentClass = item.getClass(); + + Field indexField = ReflectionUtils.findField(currentClass, index); + ReflectionUtils.makeAccessible(indexField); + Object value = ReflectionUtils.getField(indexField, item); + + Field descField = ReflectionUtils.findField(currentClass, name); + ReflectionUtils.makeAccessible(descField); + Object desc = ReflectionUtils.getField(descField, item); + return value + ":" + desc; + + }).collect(Collectors.toList()); + + map.put(parameter.defaultName().or(""), displayValues); + + OperationBuilder operationBuilder = context.operationBuilder(); + Field parametersField = ReflectionUtils.findField(operationBuilder.getClass(), "parameters"); + ReflectionUtils.makeAccessible(parametersField); + List list = (List) ReflectionUtils.getField(parametersField, operationBuilder); + + map.forEach((k, v) -> { + for (Parameter currentParameter : list) { + if (StringUtils.equals(currentParameter.getName(), k)) { + Field description = ReflectionUtils.findField(currentParameter.getClass(), "description"); + ReflectionUtils.makeAccessible(description); + Object field = ReflectionUtils.getField(description, currentParameter); + ReflectionUtils.setField(description, currentParameter, field + " , " + joiner.join(v)); + break; + } + } + }); + } + } + }); + } + + @Override + public void apply(ParameterContext context) { + Class type = context.resolvedMethodParameter().getParameterType().getErasedType(); + if (Enum.class.isAssignableFrom(type)) { + SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(type, SwaggerDisplayEnum.class); + if (annotation != null) { + + String index = annotation.index(); + String name = annotation.name(); + Object[] enumConstants = type.getEnumConstants(); + List displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> { + Class currentClass = item.getClass(); + + Field indexField = ReflectionUtils.findField(currentClass, index); + ReflectionUtils.makeAccessible(indexField); + Object value = ReflectionUtils.getField(indexField, item); + + Field descField = ReflectionUtils.findField(currentClass, name); + ReflectionUtils.makeAccessible(descField); + Object desc = ReflectionUtils.getField(descField, item); + return value.toString(); + + }).collect(Collectors.toList()); + + ParameterBuilder parameterBuilder = context.parameterBuilder(); + AllowableListValues values = new AllowableListValues(displayValues, "LIST"); + parameterBuilder.allowableValues(values); + } + } + } + + @Override + public boolean supports(DocumentationType documentationType) { + return true; + } +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/MvcConfiguration.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/MvcConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..03e5b75335a428e74c46e0f0b98928f1c484610e --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/MvcConfiguration.java @@ -0,0 +1,161 @@ +package com.gsean.swaggerui.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import com.gsean.swaggerui.plugin.EnumMvcConverterFactory; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.format.FormatterRegistry; +import org.springframework.util.ObjectUtils; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author shenjianeng + * @date 2020/4/19 + */ +@Configuration +public class MvcConfiguration implements WebMvcConfigurer { + private final String YYYY_MM_DD = "yyyy-MM-dd"; + + private final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + private final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private final String HH_MM_SS = "HH:mm:ss"; + + @Bean + public EnumMvcConverterFactory enumMvcConverterFactory() { + return new EnumMvcConverterFactory(); + } + + @Override + public void addFormatters(FormatterRegistry registry) { + // org.springframework.core.convert.support.GenericConversionService.ConvertersForPair.add + // this.converters.addFirst(converter); + // 所以我们自定义的会放在前面 + registry.addConverterFactory(enumMvcConverterFactory()); + registry.addConverter(localDateTimeConverter()); + registry.addConverter(localDateConverter()); + registry.addConverter(localTimeConverter()); + } + + /** + * Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json + */ + @Bean + public ObjectMapper customDateObjectMapper(){ + + + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + + //LocalDateTime系列序列化和反序列化模块,继承自jsr310,这里修改了日期格式 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS))); + javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(YYYY_MM_DD))); + javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(HH_MM_SS))); + javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS))); + javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(YYYY_MM_DD))); + javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(HH_MM_SS))); + + //Date序列化和反序列化 + javaTimeModule.addSerializer(Date.class, new JsonSerializer() { + @Override + public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + SimpleDateFormat formatter = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS); + String formattedDate = formatter.format(date); + jsonGenerator.writeString(formattedDate); + } + }); + javaTimeModule.addDeserializer(Date.class, new JsonDeserializer() { + @Override + public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS); + String date = jsonParser.getText(); + try { + return format.parse(date); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + }); + + objectMapper.registerModule(javaTimeModule); + return objectMapper; + } + + + /** + * LocalDate转换器,用于转换RequestParam和PathVariable参数 + */ + private Converter localDateConverter() { + return new Converter() { + @Override + public LocalDate convert(String source) { + if(!ObjectUtils.isEmpty(source)){ + return LocalDate.parse(source, DateTimeFormatter.ofPattern(YYYY_MM_DD)); + } + return null ; + + } + }; + } + + /** + * LocalDateTime转换器,用于转换RequestParam和PathVariable参数 + */ + private Converter localDateTimeConverter() { + return new Converter() { + @Override + public LocalDateTime convert(String source) { + if(!ObjectUtils.isEmpty(source)) { + return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS)); + } + return null ; + } + }; + } + + /** + * LocalTime转换器,用于转换RequestParam和PathVariable参数 + */ + private Converter localTimeConverter() { + return new Converter() { + @Override + public LocalTime convert(String source) { + if(!ObjectUtils.isEmpty(source)) { + return LocalTime.parse(source, DateTimeFormatter.ofPattern(HH_MM_SS)); + } + return null; + } + }; + } +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/SwaggerDisplayEnum.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/SwaggerDisplayEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..6c1c6214aa9baab4315148ea877fc5171a579e2c --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/config/SwaggerDisplayEnum.java @@ -0,0 +1,21 @@ +package com.gsean.swaggerui.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 10:09 + * @modificed by + **/ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SwaggerDisplayEnum { + + String index() default "index"; + String name() default "name"; + +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/controller/HelloController.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/controller/HelloController.java index e2b40080750d5fd73322f3df1d696c20a9cc8df5..249728e75d376dd76ce7bae6c8eaaec35c8f5a46 100644 --- a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/controller/HelloController.java +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/controller/HelloController.java @@ -1,10 +1,17 @@ package com.gsean.swaggerui.controller; +import com.gsean.swaggerui.enums.Status; import com.gsean.swaggerui.module.User; +import com.gsean.swaggerui.params.StatusParam; +import com.gsean.swaggerui.params.UserDTO; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import java.time.LocalDate; +import java.time.LocalDateTime; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -16,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController; * Version: 1.0 */ @RestController +@Api(value = "hello接口管理") public class HelloController { @GetMapping("/hello") @@ -25,7 +33,25 @@ public class HelloController { @PostMapping("/hello2") @ApiOperation(value = "二接口") - public User hello2(@RequestBody User user){ - return user; + public User hello2(@RequestBody UserDTO user){ + User resUser = new User().setId(user.getId()).setName(user.getName()).setStatus(user.getStatus()) + .setBirthDate(user.getBirthDate()).setCreatedTime(user.getCreatedTime()); + return resUser; + } + + + @GetMapping("/hello3") + @ApiOperation(value = "枚举请求体") + public User hello3(@RequestParam Status status){ + User gsaen = new User().setId(1).setName("gsaen").setStatus(status).setBirthDate(LocalDate.now()).setCreatedTime( + LocalDateTime.now()); + return gsaen; + } + + @GetMapping("/hello4") + @ApiOperation(value = "枚举请求体v2") + public User hello4(StatusParam statusParam,@RequestParam Status status){ + User gsaen = new User().setId(statusParam.getId()).setName("gsaen").setStatus(status).setBirthDate(LocalDate.now()); + return gsaen; } } diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/enums/Status.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/enums/Status.java new file mode 100644 index 0000000000000000000000000000000000000000..cb855fbc5522cb0910b3c2229adf5f2e4192be0c --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/enums/Status.java @@ -0,0 +1,48 @@ +package com.gsean.swaggerui.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonValue; +import com.gsean.swaggerui.config.SwaggerDisplayEnum; +import com.gsean.swaggerui.plugin.EnumConvertMethod; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import org.springframework.lang.Nullable; + +@Getter +@SwaggerDisplayEnum(index = "id", name = "des") +@JsonFormat(shape = JsonFormat.Shape.OBJECT) +public enum Status { + + ACTIVE(1,"可用"), + UNACTIVE(2, "不可用"); + +// @JsonValue + private Integer id; + private String des; + + Status(Integer id,String des) { + this.id=id; + this.des = des; + } + + + private static final Map mappings; + + static { + Map temp = new HashMap<>(); + for (Status courseType : values()) { + temp.put(courseType.id, courseType); + } + mappings = Collections.unmodifiableMap(temp); + } + + @EnumConvertMethod + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + @Nullable + public static Status resolve(int index) { + return mappings.get(index); + } +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/module/User.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/module/User.java index 6ec8f519517becbfa1c9c5145e04e084debe5e80..422a6c6b7d73d6272dffec07ed1b2e3016ba765d 100644 --- a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/module/User.java +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/module/User.java @@ -1,7 +1,19 @@ package com.gsean.swaggerui.module; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.gsean.swaggerui.enums.Status; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.time.LocalDate; +import java.time.LocalDateTime; import lombok.Data; +import lombok.experimental.Accessors; /** * ProjectName:gsean-springboot-demos @@ -12,9 +24,21 @@ import lombok.Data; * Version: 1.0 */ @Data - +@ApiModel(value = "用户") +@Accessors(chain = true) public class User { @ApiModelProperty(name = "name",value = "名称") private String name; + @ApiModelProperty(value = "id") private Integer id; + @ApiModelProperty(value = "状态") + private Status status; + @ApiModelProperty(value = "生日") +// @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8") + private LocalDate birthDate; + @ApiModelProperty(value = "创建日期") +// @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + private LocalDateTime createdTime; + + } diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/StatusParam.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/StatusParam.java new file mode 100644 index 0000000000000000000000000000000000000000..94652ce5574e5cbeaf827ae20d1baf91e0b37e62 --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/StatusParam.java @@ -0,0 +1,21 @@ +package com.gsean.swaggerui.params; + +import com.gsean.swaggerui.enums.Status; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 11:13 + * @modificed by + **/ +@ApiModel("状态请求体") +@Getter +public class StatusParam { + @ApiModelProperty(value = "id",example = "1",required = true) + private Integer id; + + +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/UserDTO.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/UserDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..650298e35429d91d9435c5edf8cbe24ee22c129d --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/params/UserDTO.java @@ -0,0 +1,44 @@ +package com.gsean.swaggerui.params; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.gsean.swaggerui.enums.Status; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.time.LocalDate; +import java.time.LocalDateTime; +import lombok.Data; +import lombok.Getter; +import lombok.experimental.Accessors; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 13:25 + * @modificed by + **/ +@Data +@ApiModel(value = "用户") +@Accessors(chain = true) +public class UserDTO { + @ApiModelProperty(name = "name",value = "名称") + private String name; + @ApiModelProperty(value = "id") + private Integer id; + @ApiModelProperty(value = "状态") + private Status status; + @ApiModelProperty(value = "生日",example = "2021-12-11") +// @JsonDeserialize(using = LocalDateDeserializer.class) +// @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8") + private LocalDate birthDate; + @ApiModelProperty(value = "创建日期") +// @JsonDeserialize(using = LocalDateTimeDeserializer.class) +// @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8") + private LocalDateTime createdTime; + +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumConvertMethod.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumConvertMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..1d385d5dc17a5f4b732e7ba444852ac863603e0b --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumConvertMethod.java @@ -0,0 +1,20 @@ +package com.gsean.swaggerui.plugin; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 在自定义枚举类的工厂方法上标记该注解,用于Spring MVC 转换器转换枚举 + * {@link com.github.shen.mvc.plugin.EnumMvcConverterFactory} + * + * @author shenjianeng + * @date 2020/4/19 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface EnumConvertMethod { +} diff --git a/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumMvcConverterFactory.java b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumMvcConverterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a5ceb6dc2090af3a7c1c6e72fada5358925de76f --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/main/java/com/gsean/swaggerui/plugin/EnumMvcConverterFactory.java @@ -0,0 +1,79 @@ +package com.gsean.swaggerui.plugin; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.reflect.MethodUtils; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * springMVC 枚举类的转换器 + * 如果枚举类中有工厂方法(静态方法)被标记为{@link EnumConvertMethod },则调用该方法转为枚举对象 + * + * @author shenjianeng + * @date 2020/4/19 + */ +@SuppressWarnings("all") +public class EnumMvcConverterFactory implements ConverterFactory> { + + private final ConcurrentMap>, EnumMvcConverterHolder> holderMapper = new ConcurrentHashMap<>(); + + + @Override + public > Converter getConverter(Class targetType) { + EnumMvcConverterHolder holder = holderMapper.computeIfAbsent(targetType, EnumMvcConverterHolder::createHolder); + return (Converter) holder.converter; + } + + + @AllArgsConstructor + static class EnumMvcConverterHolder { + @Nullable + final EnumMvcConverter converter; + + static EnumMvcConverterHolder createHolder(Class targetType) { + List methodList = MethodUtils.getMethodsListWithAnnotation(targetType, EnumConvertMethod.class, false, true); + if (CollectionUtils.isEmpty(methodList)) { + return new EnumMvcConverterHolder(null); + } + Assert.isTrue(methodList.size() == 1, "@EnumConvertMethod 只能标记在一个工厂方法(静态方法)上"); + Method method = methodList.get(0); + Assert.isTrue(Modifier.isStatic(method.getModifiers()), "@EnumConvertMethod 只能标记在工厂方法(静态方法)上"); + return new EnumMvcConverterHolder(new EnumMvcConverter<>(method)); + } + + } + + static class EnumMvcConverter> implements Converter { + + private final Method method; + + public EnumMvcConverter(Method method) { + this.method = method; + this.method.setAccessible(true); + } + + @Override + public T convert(String source) { + if (source.isEmpty()) { + // reset the enum value to null. + return null; + } + try { + return (T) method.invoke(null, Integer.valueOf(source)); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + } + + +} diff --git a/swagger-demo/swagger-ui-demo/src/main/resources/application.yml b/swagger-demo/swagger-ui-demo/src/main/resources/application.yml index be69f30d13f1f9298fb89d5ec46821c8499d9746..338b4ba6d827f1ebf6f1018d02eab8c927b6d4fc 100644 --- a/swagger-demo/swagger-ui-demo/src/main/resources/application.yml +++ b/swagger-demo/swagger-ui-demo/src/main/resources/application.yml @@ -1,2 +1,8 @@ server: - port: 20201 \ No newline at end of file + port: 20201 + +#spring: +# jackson: +# # 格式化返回时间 yyyy-MM-dd HH:mm:ss +# date-format: yyyy-MM-dd HH:mm:ss +# time-zone: GMT+8 \ No newline at end of file diff --git a/swagger-demo/swagger-ui-demo/src/test/java/com/gsean/swaggerui/SwaggerUIApplicationTest.java b/swagger-demo/swagger-ui-demo/src/test/java/com/gsean/swaggerui/SwaggerUIApplicationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e32f1c7aa204161103ffa39f592e78505d1c7d43 --- /dev/null +++ b/swagger-demo/swagger-ui-demo/src/test/java/com/gsean/swaggerui/SwaggerUIApplicationTest.java @@ -0,0 +1,45 @@ +package com.gsean.swaggerui; + +import static org.junit.Assert.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gsean.swaggerui.controller.HelloController; +import com.gsean.swaggerui.enums.Status; +import com.gsean.swaggerui.params.UserDTO; +import java.time.LocalDate; +import java.time.LocalDateTime; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @authoer Jincheng.Guo11 + * @description + * @date:created in 2021/11/22 14:51 + * @modificed by + **/ +@SpringBootTest +@RunWith(SpringRunner.class) +public class SwaggerUIApplicationTest { + @Autowired + private ObjectMapper objectMapper; + + + + + @Test + public void testA() throws Exception{ + UserDTO gsean = new UserDTO().setId(1).setName("gsean").setStatus(Status.ACTIVE) + .setBirthDate(LocalDate.now()).setCreatedTime( + LocalDateTime.now()); + //序列化测试 + String jsonStr = objectMapper.writeValueAsString(gsean); + System.out.println(jsonStr); + UserDTO userDTO = objectMapper.readValue(jsonStr, UserDTO.class); + System.out.println(userDTO.getBirthDate()); + + } + +} \ No newline at end of file