liuchen
10 months ago
commit
4f87766aba
23 changed files with 1129 additions and 0 deletions
@ -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/ |
@ -0,0 +1,66 @@ |
|||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>2.7.15</version> |
|||
<relativePath/> <!-- lookup parent from repository --> |
|||
</parent> |
|||
<groupId>com.win.mq</groupId> |
|||
<artifactId>mq</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
<name>mq</name> |
|||
<description>win mq</description> |
|||
<properties> |
|||
<java.version>17</java.version> |
|||
</properties> |
|||
<dependencies> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-starter</artifactId> |
|||
<version>2.2.2</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.apache.tomcat</groupId> |
|||
<artifactId>annotations-api</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>1.18.28</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
@ -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("闻音启动成功"); |
|||
} |
|||
} |
@ -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 <T> 数据泛型 |
|||
*/ |
|||
@Data |
|||
public class CommonResult<T> 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 <T> 返回的泛型 |
|||
* @return 新的 CommonResult 对象 |
|||
*/ |
|||
public static <T> CommonResult<T> error(CommonResult<?> result) { |
|||
return error(result.getCode(), result.getMsg()); |
|||
} |
|||
|
|||
public static <T> CommonResult<T> error(Integer code, String message) { |
|||
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!"); |
|||
CommonResult<T> result = new CommonResult<>(); |
|||
result.code = code; |
|||
result.msg = message; |
|||
return result; |
|||
} |
|||
|
|||
public static <T> CommonResult<T> error(ErrorCode errorCode) { |
|||
return error(errorCode.getCode(), errorCode.getMsg()); |
|||
} |
|||
|
|||
public static <T> CommonResult<T> success(T data) { |
|||
CommonResult<T> 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 <T> CommonResult<T> error(ServiceException serviceException) { |
|||
return error(serviceException.getCode(), serviceException.getMessage()); |
|||
} |
|||
|
|||
} |
@ -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; |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
|
|||
} |
@ -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<String> 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<String> 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<String>) 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<Object[]> order(String message) { |
|||
String[] tags = fileUtil.readTags("order.txt"); |
|||
if(tags == null || tags.length == 0) { |
|||
return CommonResult.error(GlobalErrorCodeConstants.CONFIG_ERROR); |
|||
} |
|||
List<String> messageList = new ArrayList<>(); |
|||
for(String tag : tags) { |
|||
String messageId = rocketMQProducer.sendMessage("order" + tag, message); |
|||
messageList.add(messageId); |
|||
} |
|||
return CommonResult.success(messageList.toArray()); |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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, "未知错误"); |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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<String> { |
|||
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<String> requestEntity = new HttpEntity<>(message, headers);
|
|||
// 发起POST请求
|
|||
//ResponseEntity<String> 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<String>/*, RocketMQPushConsumerLifecycleListener*/ { |
|||
public void onMessage(String message) { |
|||
log.info("RocketMQConsumerScp received message: " + message); |
|||
} |
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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[]{""}; |
|||
} |
|||
} |
|||
|
|||
} |
@ -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<String> getActiveProfile() { |
|||
return Arrays.asList(context.getEnvironment().getActiveProfiles()); |
|||
} |
|||
|
|||
} |
@ -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<String, String> 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<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(map, headers); |
|||
// 发起POST请求
|
|||
ResponseEntity<String> 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<String, String> map = new LinkedMultiValueMap<>(); |
|||
map.put("refreshToken", Collections.singletonList(this.accessTokenVO.getRefreshToken())); |
|||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(map, headers); |
|||
// 发起POST请求
|
|||
ResponseEntity<String> 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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
|
|||
} |
@ -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; |
|||
|
|||
} |
@ -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 |
@ -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 |
@ -0,0 +1,108 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<configuration> |
|||
<springProperty scope="context" name="logPath" source="logging.file.path" defaultValue="logs"/> |
|||
<!-- 日志存放路径 --> |
|||
<property name="log.path" value="${logPath}" /> |
|||
|
|||
<!--0. 日志格式和颜色渲染 --> |
|||
<!-- 彩色日志依赖的渲染类 --> |
|||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> |
|||
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> |
|||
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> |
|||
<!-- 彩色日志格式 --> |
|||
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" /> |
|||
<!-- 日志输出格式 --> |
|||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> |
|||
|
|||
<!-- 控制台输出 --> |
|||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 --> |
|||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> |
|||
<level>debug</level> |
|||
</filter> |
|||
<encoder> |
|||
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern> |
|||
<!-- 设置字符集 --> |
|||
<charset>UTF-8</charset> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<!-- 系统日志输出 --> |
|||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/sys-info.log</file> |
|||
<!-- 循环政策:基于时间创建日志文件 --> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<!-- 日志文件名格式 --> |
|||
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> |
|||
<!-- 日志最大的历史 60天 --> |
|||
<maxHistory>60</maxHistory> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> |
|||
<!-- 过滤的级别 --> |
|||
<level>INFO</level> |
|||
<!-- 匹配时的操作:接收(记录) --> |
|||
<onMatch>ACCEPT</onMatch> |
|||
<!-- 不匹配时的操作:拒绝(不记录) --> |
|||
<onMismatch>DENY</onMismatch> |
|||
</filter> |
|||
</appender> |
|||
|
|||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/sys-error.log</file> |
|||
<!-- 循环政策:基于时间创建日志文件 --> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<!-- 日志文件名格式 --> |
|||
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> |
|||
<!-- 日志最大的历史 60天 --> |
|||
<maxHistory>60</maxHistory> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> |
|||
<!-- 过滤的级别 --> |
|||
<level>ERROR</level> |
|||
<!-- 匹配时的操作:接收(记录) --> |
|||
<onMatch>ACCEPT</onMatch> |
|||
<!-- 不匹配时的操作:拒绝(不记录) --> |
|||
<onMismatch>DENY</onMismatch> |
|||
</filter> |
|||
</appender> |
|||
|
|||
<!-- 用户访问日志输出 --> |
|||
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/sys-user.log</file> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<!-- 按天回滚 daily --> |
|||
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> |
|||
<!-- 日志最大的历史 60天 --> |
|||
<maxHistory>60</maxHistory> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<!-- 系统模块日志级别控制 --> |
|||
<logger name="com.hc" level="info" /> |
|||
<!-- Spring日志级别控制 --> |
|||
<logger name="org.springframework" level="warn" /> |
|||
|
|||
<root level="error"> |
|||
<appender-ref ref="console" /> |
|||
</root> |
|||
|
|||
<!--系统操作日志--> |
|||
<root level="info"> |
|||
<appender-ref ref="file_info" /> |
|||
<appender-ref ref="file_error" /> |
|||
</root> |
|||
|
|||
<!--系统用户操作日志--> |
|||
<logger name="sys-user" level="info"> |
|||
<appender-ref ref="sys-user"/> |
|||
</logger> |
|||
</configuration> |
@ -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() { |
|||
} |
|||
|
|||
} |
Loading…
Reference in new issue