From 4f87766aba9d8a37dfe6b763cbf30f25a842212c Mon Sep 17 00:00:00 2001 From: liuchen <23082234@qq.com> Date: Thu, 25 Jan 2024 16:46:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 33 ++++++ pom.xml | 66 +++++++++++ src/main/java/com/win/mq/Application.java | 18 +++ .../java/com/win/mq/common/CommonResult.java | 112 ++++++++++++++++++ .../java/com/win/mq/config/RMQConfigure.java | 28 +++++ .../com/win/mq/config/RestTemplateConfig.java | 31 +++++ .../java/com/win/mq/config/SmfsConfigure.java | 34 ++++++ .../win/mq/controller/ProduceController.java | 98 +++++++++++++++ .../java/com/win/mq/exception/ErrorCode.java | 26 ++++ .../exception/GlobalErrorCodeConstants.java | 44 +++++++ .../com/win/mq/exception/ServerException.java | 58 +++++++++ .../win/mq/exception/ServiceException.java | 58 +++++++++ .../com/win/mq/rocket/RocketMQConsumer.java | 52 ++++++++ .../com/win/mq/rocket/RocketMQProducer.java | 26 ++++ src/main/java/com/win/mq/utils/FileUtil.java | 64 ++++++++++ .../java/com/win/mq/utils/ProfileUtil.java | 26 ++++ .../win/mq/utils/sfms/AccessTokenUtil.java | 105 ++++++++++++++++ .../com/win/mq/utils/sfms/AccessTokenVO.java | 46 +++++++ .../com/win/mq/utils/sfms/RefreshTokenVO.java | 33 ++++++ src/main/resources/application-dev.yml | 38 ++++++ src/main/resources/application.yml | 12 ++ src/main/resources/logback-spring.xml | 108 +++++++++++++++++ .../java/com/win/mq/MqApplicationTests.java | 13 ++ 23 files changed, 1129 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/win/mq/Application.java create mode 100644 src/main/java/com/win/mq/common/CommonResult.java create mode 100644 src/main/java/com/win/mq/config/RMQConfigure.java create mode 100644 src/main/java/com/win/mq/config/RestTemplateConfig.java create mode 100644 src/main/java/com/win/mq/config/SmfsConfigure.java create mode 100644 src/main/java/com/win/mq/controller/ProduceController.java create mode 100644 src/main/java/com/win/mq/exception/ErrorCode.java create mode 100644 src/main/java/com/win/mq/exception/GlobalErrorCodeConstants.java create mode 100644 src/main/java/com/win/mq/exception/ServerException.java create mode 100644 src/main/java/com/win/mq/exception/ServiceException.java create mode 100644 src/main/java/com/win/mq/rocket/RocketMQConsumer.java create mode 100644 src/main/java/com/win/mq/rocket/RocketMQProducer.java create mode 100644 src/main/java/com/win/mq/utils/FileUtil.java create mode 100644 src/main/java/com/win/mq/utils/ProfileUtil.java create mode 100644 src/main/java/com/win/mq/utils/sfms/AccessTokenUtil.java create mode 100644 src/main/java/com/win/mq/utils/sfms/AccessTokenVO.java create mode 100644 src/main/java/com/win/mq/utils/sfms/RefreshTokenVO.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/test/java/com/win/mq/MqApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6d8752f --- /dev/null +++ b/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.15 + + + com.win.mq + mq + 0.0.1-SNAPSHOT + mq + win mq + + 17 + + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.2.2 + + + org.apache.tomcat + annotations-api + + + + + + org.projectlombok + lombok + 1.18.28 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/com/win/mq/Application.java b/src/main/java/com/win/mq/Application.java new file mode 100644 index 0000000..51d39e0 --- /dev/null +++ b/src/main/java/com/win/mq/Application.java @@ -0,0 +1,18 @@ +package com.win.mq; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 启动程序 + * + * @author win + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + System.out.println("闻音启动成功"); + } +} diff --git a/src/main/java/com/win/mq/common/CommonResult.java b/src/main/java/com/win/mq/common/CommonResult.java new file mode 100644 index 0000000..58fa653 --- /dev/null +++ b/src/main/java/com/win/mq/common/CommonResult.java @@ -0,0 +1,112 @@ +package com.win.mq.common; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.win.mq.exception.ErrorCode; +import com.win.mq.exception.GlobalErrorCodeConstants; +import com.win.mq.exception.ServiceException; +import lombok.Data; +import org.springframework.util.Assert; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 通用返回 + * + * @param 数据泛型 + */ +@Data +public class CommonResult implements Serializable { + + /** + * 错误码 + * + * @see ErrorCode#getCode() + */ + private Integer code; + /** + * 返回数据 + */ + private T data; + /** + * 错误提示,用户可阅读 + * + * @see ErrorCode#getMsg() () + */ + private String msg; + + /** + * 将传入的 result 对象,转换成另外一个泛型结果的对象 + * + * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 + * + * @param result 传入的 result 对象 + * @param 返回的泛型 + * @return 新的 CommonResult 对象 + */ + public static CommonResult error(CommonResult result) { + return error(result.getCode(), result.getMsg()); + } + + public static CommonResult error(Integer code, String message) { + Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!"); + CommonResult result = new CommonResult<>(); + result.code = code; + result.msg = message; + return result; + } + + public static CommonResult error(ErrorCode errorCode) { + return error(errorCode.getCode(), errorCode.getMsg()); + } + + public static CommonResult success(T data) { + CommonResult result = new CommonResult<>(); + result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); + result.data = data; + result.msg = ""; + return result; + } + + public static boolean isSuccess(Integer code) { + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); + } + + @JsonIgnore // 避免 jackson 序列化 + public boolean isSuccess() { + return isSuccess(code); + } + + @JsonIgnore // 避免 jackson 序列化 + public boolean isError() { + return !isSuccess(); + } + + // ========= 和 Exception 异常体系集成 ========= + + /** + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 + */ + public void checkError() throws ServiceException { + if (isSuccess()) { + return; + } + // 业务异常 + throw new ServiceException(code, msg); + } + + /** + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 + * 如果没有,则返回 {@link #data} 数据 + */ + @JsonIgnore // 避免 jackson 序列化 + public T getCheckedData() { + checkError(); + return data; + } + + public static CommonResult error(ServiceException serviceException) { + return error(serviceException.getCode(), serviceException.getMessage()); + } + +} diff --git a/src/main/java/com/win/mq/config/RMQConfigure.java b/src/main/java/com/win/mq/config/RMQConfigure.java new file mode 100644 index 0000000..bdc4e72 --- /dev/null +++ b/src/main/java/com/win/mq/config/RMQConfigure.java @@ -0,0 +1,28 @@ +package com.win.mq.config; + + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.common.MixAll; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; + +import java.io.File; + +import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY; + +@Data +@Configuration +@ConfigurationProperties(prefix = "rocketmq.config") +public class RMQConfigure { + + private String dataPath; + +} \ No newline at end of file diff --git a/src/main/java/com/win/mq/config/RestTemplateConfig.java b/src/main/java/com/win/mq/config/RestTemplateConfig.java new file mode 100644 index 0000000..13877cb --- /dev/null +++ b/src/main/java/com/win/mq/config/RestTemplateConfig.java @@ -0,0 +1,31 @@ +package com.win.mq.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.StandardCharsets; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + RestTemplate restTemplate = new RestTemplate(factory); + // 支持中文编码 + restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); + return restTemplate; + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(5000);//单位为ms + factory.setConnectTimeout(5000);//单位为ms + return factory; + } + +} diff --git a/src/main/java/com/win/mq/config/SmfsConfigure.java b/src/main/java/com/win/mq/config/SmfsConfigure.java new file mode 100644 index 0000000..a58204e --- /dev/null +++ b/src/main/java/com/win/mq/config/SmfsConfigure.java @@ -0,0 +1,34 @@ +package com.win.mq.config; + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "sfms") +public class SmfsConfigure { + + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 采购订单请求地址 + */ + private String purchaseOrder; + /** + * 访问token + */ + private String accessToken; + /** + * 刷新token + */ + private String refreshToken; + +} \ No newline at end of file diff --git a/src/main/java/com/win/mq/controller/ProduceController.java b/src/main/java/com/win/mq/controller/ProduceController.java new file mode 100644 index 0000000..5d415b6 --- /dev/null +++ b/src/main/java/com/win/mq/controller/ProduceController.java @@ -0,0 +1,98 @@ +package com.win.mq.controller; + +import com.win.mq.common.CommonResult; +import com.win.mq.config.RMQConfigure; +import com.win.mq.exception.GlobalErrorCodeConstants; +import com.win.mq.rocket.RocketMQProducer; +import com.win.mq.utils.FileUtil; +import com.win.mq.utils.ProfileUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.DigestUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/producer") +public class ProduceController { + + @Autowired + private RMQConfigure configure; + @Autowired + private FileUtil fileUtil; + @Autowired + private RocketMQProducer rocketMQProducer; + + /** + * 对外提供一个接口,通过header中的interfaceName反射机制调用方法,方法必须写到这个controller中,并且不用加PostMapping注解。 + * + * @param request request + * @param body 请求主体 + * @return 结果 + */ + @PostMapping("/api") + @SuppressWarnings("unchecked") + public CommonResult api(HttpServletRequest request, @RequestBody String body) { + String interfaceName = request.getHeader("interface"); + String sign = request.getHeader("sign"); + String timeStr = request.getHeader("timestamp"); + if(timeStr == null || timeStr.isEmpty()) { + return CommonResult.error(GlobalErrorCodeConstants.TIMESTAMP_ERROR); + } + long timestamp = 0; + try { + timestamp = Long.parseLong(timeStr); + } catch (NumberFormatException e) { + return CommonResult.error(GlobalErrorCodeConstants.TIMESTAMP_ERROR); + } + String tmp = interfaceName + body + timestamp; + String computeSign = DigestUtils.md5DigestAsHex(tmp.getBytes()); + log.info("{}, interfaceName: {}", "interfaceName", interfaceName); + log.info("{}, sign: {}", "sign", sign); + log.info("{}, timestamp: {}", "timestamp", timestamp); + log.info("{}, tmp: {}", "tmp", tmp); + log.info("{}, computeSign: {}", "computeSign", computeSign); + long tenTimestamp = timestamp + (10 * 60 * 1000); // 计算10分钟后的时间戳 + long currentTimestamp = System.currentTimeMillis(); // 获取当前时间戳 + //过期 + if(timestamp > currentTimestamp || tenTimestamp < currentTimestamp) { + return CommonResult.error(GlobalErrorCodeConstants.EXPIRE_ERROR); + } + List activeProfile = ProfileUtil.getActiveProfile(); + //dev环境不校验签名 + if(!activeProfile.contains("dev") && !StringUtils.equals(sign, computeSign.toUpperCase())) { + return CommonResult.error(GlobalErrorCodeConstants.SIGN_ERROR); + } + try { + Method method = this.getClass().getMethod(interfaceName, String.class); + return (CommonResult) method.invoke(this, body); + } catch (NoSuchMethodException e) { + return CommonResult.error(GlobalErrorCodeConstants.INTERFACE_ERROR); + } catch (Exception e) { + return CommonResult.error(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR); + } + } + + public CommonResult order(String message) { + String[] tags = fileUtil.readTags("order.txt"); + if(tags == null || tags.length == 0) { + return CommonResult.error(GlobalErrorCodeConstants.CONFIG_ERROR); + } + List messageList = new ArrayList<>(); + for(String tag : tags) { + String messageId = rocketMQProducer.sendMessage("order" + tag, message); + messageList.add(messageId); + } + return CommonResult.success(messageList.toArray()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/win/mq/exception/ErrorCode.java b/src/main/java/com/win/mq/exception/ErrorCode.java new file mode 100644 index 0000000..e289b2f --- /dev/null +++ b/src/main/java/com/win/mq/exception/ErrorCode.java @@ -0,0 +1,26 @@ +package com.win.mq.exception; + +import lombok.Data; + +/** + * 错误码对象 + * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 + */ +@Data +public class ErrorCode { + + /** + * 错误码 + */ + private final Integer code; + /** + * 错误提示 + */ + private final String msg; + + public ErrorCode(Integer code, String message) { + this.code = code; + this.msg = message; + } + +} diff --git a/src/main/java/com/win/mq/exception/GlobalErrorCodeConstants.java b/src/main/java/com/win/mq/exception/GlobalErrorCodeConstants.java new file mode 100644 index 0000000..5d8c33a --- /dev/null +++ b/src/main/java/com/win/mq/exception/GlobalErrorCodeConstants.java @@ -0,0 +1,44 @@ +package com.win.mq.exception; + +/** + * 全局错误码枚举 + * 0-999 系统异常编码保留 + * + * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status + * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 + * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 + * + * @author 闻荫源码 + */ +public interface GlobalErrorCodeConstants { + + ErrorCode SUCCESS = new ErrorCode(0, "成功"); + + ErrorCode SIGN_ERROR = new ErrorCode(300, "签名不正确"); + ErrorCode TIMESTAMP_ERROR = new ErrorCode(301, "时间戳不正确"); + ErrorCode EXPIRE_ERROR = new ErrorCode(302, "请求已过期"); + ErrorCode INTERFACE_ERROR = new ErrorCode(303, "接口不正确"); + ErrorCode CONFIG_ERROR = new ErrorCode(304, "请先维护推送设置"); + ErrorCode SEND_ERROR = new ErrorCode(305, "发送消息失败"); + // ========== 客户端错误段 ========== + + ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确"); + ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录"); + ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限"); + ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到"); + ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); + ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许 + ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试"); + + // ========== 服务端错误段 ========== + + ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); + ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启"); + + // ========== 自定义错误段 ========== + ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 + ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); + + ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); + +} diff --git a/src/main/java/com/win/mq/exception/ServerException.java b/src/main/java/com/win/mq/exception/ServerException.java new file mode 100644 index 0000000..9825083 --- /dev/null +++ b/src/main/java/com/win/mq/exception/ServerException.java @@ -0,0 +1,58 @@ +package com.win.mq.exception; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 服务器异常 Exception + */ +@Data +@EqualsAndHashCode(callSuper = true) +public final class ServerException extends RuntimeException { + + /** + * 全局错误码 + * + */ + private Integer code; + /** + * 错误提示 + */ + private String message; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServerException() { + } + + public ServerException(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.message = errorCode.getMsg(); + } + + public ServerException(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer getCode() { + return code; + } + + public ServerException setCode(Integer code) { + this.code = code; + return this; + } + + @Override + public String getMessage() { + return message; + } + + public ServerException setMessage(String message) { + this.message = message; + return this; + } + +} diff --git a/src/main/java/com/win/mq/exception/ServiceException.java b/src/main/java/com/win/mq/exception/ServiceException.java new file mode 100644 index 0000000..2c2cae8 --- /dev/null +++ b/src/main/java/com/win/mq/exception/ServiceException.java @@ -0,0 +1,58 @@ +package com.win.mq.exception; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 业务逻辑异常 Exception + */ +@Data +@EqualsAndHashCode(callSuper = true) +public final class ServiceException extends RuntimeException { + + /** + * 业务错误码 + * + */ + private Integer code; + /** + * 错误提示 + */ + private String message; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() { + } + + public ServiceException(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.message = errorCode.getMsg(); + } + + public ServiceException(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer getCode() { + return code; + } + + public ServiceException setCode(Integer code) { + this.code = code; + return this; + } + + @Override + public String getMessage() { + return message; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + +} diff --git a/src/main/java/com/win/mq/rocket/RocketMQConsumer.java b/src/main/java/com/win/mq/rocket/RocketMQConsumer.java new file mode 100644 index 0000000..889fd0f --- /dev/null +++ b/src/main/java/com/win/mq/rocket/RocketMQConsumer.java @@ -0,0 +1,52 @@ +package com.win.mq.rocket; + +import com.win.mq.config.SmfsConfigure; +import com.win.mq.utils.sfms.AccessTokenUtil; +import com.win.mq.utils.sfms.AccessTokenVO; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Component +public class RocketMQConsumer { + + @Autowired + private AccessTokenUtil accessTokenUtil; + @Autowired + private SmfsConfigure smfsConfigure; + @Autowired + private RestTemplate restTemplate; + + @Component + @RocketMQMessageListener(topic = "order", consumerGroup = "wms", selectorExpression="wms") + class RocketMQConsumerWms implements RocketMQListener { + public void onMessage(String message) { + //获取token,调用采购订单接口 + //AccessTokenVO accessTokenVO = accessTokenUtil.getAccessTokenVO(); + //log.info(accessTokenVO.toString()); + //HttpHeaders headers = new HttpHeaders(); + //headers.setContentType(MediaType.APPLICATION_JSON); + //headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + accessTokenVO.getAccessToken()); + //headers.add("tenant-id", "1"); + // 将JSON参数转换成HttpEntity对象 + //HttpEntity requestEntity = new HttpEntity<>(message, headers); + // 发起POST请求 + //ResponseEntity responseEntity = restTemplate.postForEntity(smfsConfigure.getPurchaseOrder(), requestEntity, String.class); + log.info("RocketMQConsumerWms received message: " + message); + } + } + + @Component + @RocketMQMessageListener(topic = "order", consumerGroup = "scp", selectorExpression="scp") + class RocketMQConsumerScp implements RocketMQListener/*, RocketMQPushConsumerLifecycleListener*/ { + public void onMessage(String message) { + log.info("RocketMQConsumerScp received message: " + message); + } + } + +} diff --git a/src/main/java/com/win/mq/rocket/RocketMQProducer.java b/src/main/java/com/win/mq/rocket/RocketMQProducer.java new file mode 100644 index 0000000..a8136eb --- /dev/null +++ b/src/main/java/com/win/mq/rocket/RocketMQProducer.java @@ -0,0 +1,26 @@ +package com.win.mq.rocket; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Component +public class RocketMQProducer { + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + public String sendMessage(String topic, String message) { + long timeStamp = new Date().getTime(); + SendResult sendResult = rocketMQTemplate.syncSendOrderly(topic, message + "_" + timeStamp, String.valueOf(timeStamp)); + return sendResult.getMsgId(); + } +} \ No newline at end of file diff --git a/src/main/java/com/win/mq/utils/FileUtil.java b/src/main/java/com/win/mq/utils/FileUtil.java new file mode 100644 index 0000000..43115c1 --- /dev/null +++ b/src/main/java/com/win/mq/utils/FileUtil.java @@ -0,0 +1,64 @@ +package com.win.mq.utils; + +import com.win.mq.config.RMQConfigure; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +@Slf4j +@Component +public class FileUtil { + @Autowired + private RMQConfigure configure; + private String[] defaultTags; + + public String[] readTags(String fileName) { + File file = new File(configure.getDataPath() + File.separator + fileName); + //默认不带标签 + String[] tags = null; + if(file.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(configure.getDataPath() + File.separator + fileName))) { + String line = reader.readLine(); + tags = line.split(","); + for(int i = 0; i < tags.length; i++) { + String tag = tags[i]; + tags[i] = ":" + tag; + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + return tags; + } + + private String[] getDefaultTags() { + //不为空直接返回 + if(this.defaultTags != null) { + return this.defaultTags; + } + File file = new File(configure.getDataPath() + File.separator + "project.txt"); + if(file.exists()) { + String[] tags = null; + try (BufferedReader reader = new BufferedReader(new FileReader(configure.getDataPath() + File.separator + "project.txt"))) { + String line = reader.readLine(); + tags = line.split(","); + for(int i = 0; i < tags.length; i++) { + String tag = tags[i]; + tags[i] = ":" + tag; + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + this.defaultTags = tags; + return this.defaultTags; + } else { + return new String[]{""}; + } + } + +} diff --git a/src/main/java/com/win/mq/utils/ProfileUtil.java b/src/main/java/com/win/mq/utils/ProfileUtil.java new file mode 100644 index 0000000..8c55de5 --- /dev/null +++ b/src/main/java/com/win/mq/utils/ProfileUtil.java @@ -0,0 +1,26 @@ +package com.win.mq.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +public class ProfileUtil implements ApplicationContextAware { + + private static ApplicationContext context = null; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } + + // 获取当前环境参数 exp: dev,prod,test + public static List getActiveProfile() { + return Arrays.asList(context.getEnvironment().getActiveProfiles()); + } + +} diff --git a/src/main/java/com/win/mq/utils/sfms/AccessTokenUtil.java b/src/main/java/com/win/mq/utils/sfms/AccessTokenUtil.java new file mode 100644 index 0000000..07e7920 --- /dev/null +++ b/src/main/java/com/win/mq/utils/sfms/AccessTokenUtil.java @@ -0,0 +1,105 @@ +package com.win.mq.utils.sfms; + +import com.alibaba.fastjson.JSONObject; +import com.win.mq.config.SmfsConfigure; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDateTime; +import java.util.Collections; + +@Slf4j +@Component +public class AccessTokenUtil { + + private AccessTokenVO accessTokenVO; + @Autowired + private SmfsConfigure smfsConfigure; + @Autowired + private RestTemplate restTemplate; + + public AccessTokenVO getAccessTokenVO() { + if(this.accessTokenVO == null) { + this.accessTokenVO = loginSfms(); + } + LocalDateTime now = LocalDateTime.now(); + if(now.isAfter(this.accessTokenVO.getExpiresTime())) { + this.accessTokenVO = this.refreshAccessToken(); + } + return accessTokenVO; + } + + /** + * 登录获取accessToken + * @return accessToken + */ + private AccessTokenVO loginSfms() { + HttpHeaders headers = new HttpHeaders(); + headers.add("tenant-id", "1"); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + map.put("client_id", Collections.singletonList("win-sso-demo-by-password")); + map.put("client_secret", Collections.singletonList("test")); + map.put("grant_type", Collections.singletonList("password")); + map.put("username", Collections.singletonList(smfsConfigure.getUsername())); + map.put("password", Collections.singletonList(smfsConfigure.getPassword())); + HttpEntity> requestEntity = new HttpEntity<>(map, headers); + // 发起POST请求 + ResponseEntity responseEntity = restTemplate.postForEntity(smfsConfigure.getAccessToken(), requestEntity, String.class); + // 获取返回结果 + //if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) { + // System.out.println("返回结果:" + responseEntity.getBody()); + //} else { + // System.err.println("请求失败!"); + //} + JSONObject object = JSONObject.parseObject(responseEntity.getBody()); + if(object.getInteger("code") != 0) { + log.error(object.getString("msg")); + return null; + } + JSONObject accessTokenObject = JSONObject.parseObject(object.getString("data")); + AccessTokenVO accessTokenVO = new AccessTokenVO(); + accessTokenVO.setAccessToken(accessTokenObject.getString("access_token")); + accessTokenVO.setRefreshToken(accessTokenObject.getString("refresh_token")); + accessTokenVO.setTokenType(accessTokenObject.getString("token_type")); + accessTokenVO.setExpiresIn(accessTokenObject.getLong("expires_in")); + accessTokenVO.setScope(accessTokenObject.getString("scope")); + LocalDateTime now = LocalDateTime.now(); + now = now.plusNanos((accessTokenVO.getExpiresIn() - 1000) * 1_000_000); // 增加毫秒数并得到新的日期时间 + accessTokenVO.setExpiresTime(now); + return accessTokenVO; + } + + /** + * 登录获取accessToken + * @return accessToken + */ + private AccessTokenVO refreshAccessToken() { + HttpHeaders headers = new HttpHeaders(); + headers.add("tenant-id", "1"); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + map.put("refreshToken", Collections.singletonList(this.accessTokenVO.getRefreshToken())); + HttpEntity> requestEntity = new HttpEntity<>(map, headers); + // 发起POST请求 + ResponseEntity responseEntity = restTemplate.postForEntity(smfsConfigure.getRefreshToken(), requestEntity, String.class); + JSONObject object = JSONObject.parseObject(responseEntity.getBody()); + if(object.getInteger("code") != 0) { + log.error(object.getString("msg")); + return this.getAccessTokenVO(); + } + JSONObject refreshTokenObject = JSONObject.parseObject(object.getString("data")); + this.accessTokenVO.setAccessToken(refreshTokenObject.getString("access_token")); + this.accessTokenVO.setRefreshToken(refreshTokenObject.getString("refresh_token")); + LocalDateTime now = LocalDateTime.now(); + now = now.plusNanos((accessTokenVO.getExpiresIn() - 1000) * 1_000_000); // 增加毫秒数并得到新的日期时间 + this.accessTokenVO.setExpiresTime(now); + return this.accessTokenVO; + } + +} diff --git a/src/main/java/com/win/mq/utils/sfms/AccessTokenVO.java b/src/main/java/com/win/mq/utils/sfms/AccessTokenVO.java new file mode 100644 index 0000000..190a30d --- /dev/null +++ b/src/main/java/com/win/mq/utils/sfms/AccessTokenVO.java @@ -0,0 +1,46 @@ +package com.win.mq.utils.sfms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class AccessTokenVO { + + /** + * 访问令牌 + */ + private String accessToken; + + /** + * 刷新令牌 + */ + private String refreshToken; + + /** + * 令牌类型 + */ + private String tokenType; + + /** + * 过期时间,单位:秒 + */ + private Long expiresIn; + + /** + * 授权范围,如果多个授权范围,使用空格分隔 + */ + private String scope; + + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/src/main/java/com/win/mq/utils/sfms/RefreshTokenVO.java b/src/main/java/com/win/mq/utils/sfms/RefreshTokenVO.java new file mode 100644 index 0000000..659dcd0 --- /dev/null +++ b/src/main/java/com/win/mq/utils/sfms/RefreshTokenVO.java @@ -0,0 +1,33 @@ +package com.win.mq.utils.sfms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RefreshTokenVO { + /** + * 用户编号 + */ + private Long userId; + + /** + * 访问令牌 + */ + private String accessToken; + + /** + * 刷新令牌 + */ + private String refreshToken; + + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..7de795e --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,38 @@ +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 18080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 100 + threads: + # tomcat最大线程数,默认为200 + max: 100 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 10 + +rocketmq: + name-server: dev.ccwin-in.com:23887 + producer: + group: wenyin + config: + dataPath: /opt/rocketmq-console/data #存储设置目录 + +sfms: + username: admin #用户名 + password: 123456 #密码 + purchase-order: http://localhost:12080/admin-api/wms/purchase-main/create #采购订单 + access-token: http://localhost:12080/admin-api/system/oauth2/token #登陆token + refresh-token: http://localhost:12080/admin-api/system/auth/refresh-token #刷新token + +logging: + file: + path: logs + level: + com.win: debug + org.springframework: warn diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..1b99bab --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,12 @@ +spring: + application: + name: win + profiles: + active: dev + main: + allow-bean-definition-overriding: true + servlet: + multipart: + enabled: true + max-file-size: 200MB + max-request-size: 1000MB \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..c191ed4 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + debug + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/win/mq/MqApplicationTests.java b/src/test/java/com/win/mq/MqApplicationTests.java new file mode 100644 index 0000000..08e3549 --- /dev/null +++ b/src/test/java/com/win/mq/MqApplicationTests.java @@ -0,0 +1,13 @@ +package com.win.mq; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MqApplicationTests { + + @Test + void contextLoads() { + } + +}