init
This commit is contained in:
39
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/YunExecutor.java
Normal file
39
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/YunExecutor.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.yunxin;
|
||||
|
||||
import com.alibaba.ttl.threadpool.TtlExecutors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 线程变量定义
|
||||
* <p>created on 2023/3/3 11:16</p>
|
||||
* @author ZL
|
||||
*/
|
||||
public class YunExecutor {
|
||||
|
||||
private final static int CPU_NUM = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
public static Executor YUN_EXECUTOR;
|
||||
|
||||
static {
|
||||
YUN_EXECUTOR = new ThreadPoolExecutor(CPU_NUM,
|
||||
CPU_NUM << 2,
|
||||
5,
|
||||
TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<>(5),
|
||||
init("yunxinThreadPoll-%d"),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
// YUN_EXECUTOR = TtlExecutors.getTtlExecutor(roomExecutor);
|
||||
}
|
||||
|
||||
private static ThreadFactory init(String nameFormat){
|
||||
return new ThreadFactoryBuilder().setNameFormat(nameFormat).build();
|
||||
}
|
||||
|
||||
private static ThreadPoolExecutor initExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, TimeUnit timeUnit,
|
||||
BlockingQueue<Runnable> workQueue, String nameFormat){
|
||||
return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue,
|
||||
init(nameFormat));
|
||||
}
|
||||
}
|
||||
121
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/Yunxin.java
Normal file
121
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/Yunxin.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package com.ruoyi.yunxin;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.yunxin.client.ImMessageClient;
|
||||
import com.ruoyi.yunxin.config.YunxinProperties;
|
||||
import com.ruoyi.yunxin.enums.YxImTypeEnum;
|
||||
import com.ruoyi.yunxin.req.*;
|
||||
import com.ruoyi.yunxin.req.type.YxTextData;
|
||||
import com.ruoyi.yunxin.resp.SendMsgResp;
|
||||
import com.ruoyi.yunxin.resp.YxCommonR;
|
||||
import com.ruoyi.yunxin.resp.YxDataR;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class Yunxin {
|
||||
|
||||
private final static String SYS_NOTICE_ID = "2";
|
||||
|
||||
@Autowired
|
||||
private YunxinProperties yunxinProperties;
|
||||
@Resource
|
||||
private ImMessageClient messageClient;
|
||||
|
||||
/**
|
||||
* 发送系统消息
|
||||
* @param toUid 接收者ID
|
||||
* @param data 数据
|
||||
* @return
|
||||
*/
|
||||
public YxDataR<SendMsgResp> sendToNotice(Long toUid, Object data){
|
||||
SendMsgReq req = new SendMsgReq();
|
||||
req.setFrom(SYS_NOTICE_ID);
|
||||
req.setTo(toUid+"");
|
||||
req.setBody(JSON.toJSONString(data));
|
||||
req.setOption(JSON.toJSONString(new Option()));
|
||||
req.setType(100);
|
||||
return messageClient.sendMsg(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量发送 系统消息
|
||||
* @param toUid
|
||||
* @return
|
||||
*/
|
||||
public YxDataR<YxCommonR> batchSendToNotice(List<Long> toUid, Object data){
|
||||
SendBatchMsgReq req = new SendBatchMsgReq();
|
||||
req.setFromAccid(SYS_NOTICE_ID);
|
||||
req.setToAccids(toUid.stream().map(i -> String.valueOf(toUid)).collect(Collectors.toList()));
|
||||
req.setBody(JSON.toJSONString(data));
|
||||
req.setOption(JSON.toJSONString(new Option()));
|
||||
req.setType(100);
|
||||
return messageClient.sendBatchMsg(req);
|
||||
}
|
||||
|
||||
public YxDataR<YxCommonR> batchSendToNotice(List<Long> toUid, Object body, Option option, YxImTypeEnum type){
|
||||
SendBatchMsgReq req = new SendBatchMsgReq();
|
||||
req.setFromAccid(SYS_NOTICE_ID);
|
||||
req.setToAccids(toUid.stream().map(i -> String.valueOf(toUid)).collect(Collectors.toList()));
|
||||
req.setBody(JSON.toJSONString(body));
|
||||
if(option != null){
|
||||
req.setOption(JSON.toJSONString(new Option()));
|
||||
}
|
||||
req.setType(type.getCode());
|
||||
return messageClient.sendBatchMsg(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定用户的自定义消息
|
||||
*/
|
||||
public YxDataR<SendMsgResp> sendToUserNotice(Long toUid, Long fromUid, Object data){
|
||||
SendMsgReq req = new SendMsgReq();
|
||||
req.setFrom(fromUid+"");
|
||||
req.setTo(toUid+"");
|
||||
req.setBody(JSON.toJSONString(data));
|
||||
req.setOption(JSON.toJSONString(new Option()));
|
||||
return messageClient.sendMsg(req);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public YxDataR<YxCommonR> batchSendToTextMessage(Long fromUid, List<Long> toUid, String data){
|
||||
SendBatchMsgReq req = new SendBatchMsgReq();
|
||||
req.setFromAccid(fromUid+"");
|
||||
req.setToAccids(toUid.stream().map(i -> String.valueOf(toUid)).collect(Collectors.toList()));
|
||||
req.setBody(JSON.toJSONString(new YxTextData(data)));
|
||||
return messageClient.sendBatchMsg(req);
|
||||
}
|
||||
|
||||
public YxDataR<YxCommonR> sendAttachMsg(Long fromUid, Long toUid, Object data){
|
||||
SendAttachMsgReq sendAttachMsgReq = new SendAttachMsgReq();
|
||||
sendAttachMsgReq.setFrom(fromUid+"");
|
||||
sendAttachMsgReq.setTo(toUid+"");
|
||||
sendAttachMsgReq.setAttach(JSON.toJSONString(data));
|
||||
sendAttachMsgReq.setOption(JSON.toJSONString(new Option()));
|
||||
return messageClient.sendAttachMsg(sendAttachMsgReq);
|
||||
}
|
||||
|
||||
public YxDataR<YxCommonR> sendBatchAttachMsgNotice(List<Long> toUid, Object data){
|
||||
SendBatchAttachMsgReq req = new SendBatchAttachMsgReq();
|
||||
req.setFromAccid(SYS_NOTICE_ID);
|
||||
req.setToAccids(toUid.stream().map(i -> String.valueOf(toUid)).collect(Collectors.toList()));
|
||||
req.setAttach(JSON.toJSONString(data));
|
||||
return messageClient.sendBatchAttachMsg(req);
|
||||
}
|
||||
|
||||
|
||||
public YxDataR<YxCommonR> sendAttachMsgNotice(Long toUid, Object data){
|
||||
SendAttachMsgReq sendAttachMsgReq = new SendAttachMsgReq();
|
||||
sendAttachMsgReq.setFrom(SYS_NOTICE_ID);
|
||||
sendAttachMsgReq.setTo(toUid+"");
|
||||
sendAttachMsgReq.setAttach(JSON.toJSONString(data));
|
||||
sendAttachMsgReq.setOption(JSON.toJSONString(new Option()));
|
||||
return messageClient.sendAttachMsg(sendAttachMsgReq);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ruoyi.yunxin.client;
|
||||
|
||||
import com.dtflys.forest.annotation.*;
|
||||
import com.ruoyi.yunxin.convertor.CustomFormConvertor;
|
||||
import com.ruoyi.yunxin.interceptor.GlodonTokenInterceptor;
|
||||
import com.ruoyi.yunxin.req.SendAttachMsgReq;
|
||||
import com.ruoyi.yunxin.req.SendBatchAttachMsgReq;
|
||||
import com.ruoyi.yunxin.req.SendBatchMsgReq;
|
||||
import com.ruoyi.yunxin.req.SendMsgReq;
|
||||
import com.ruoyi.yunxin.resp.SendMsgResp;
|
||||
import com.ruoyi.yunxin.resp.YxCommonR;
|
||||
import com.ruoyi.yunxin.resp.YxDataR;
|
||||
|
||||
@BaseRequest(baseURL = "${baseUrl}", interceptor = GlodonTokenInterceptor.class)
|
||||
public interface ImMessageClient {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@Post(url = "/nimserver/msg/sendMsg.action")
|
||||
@BodyType(type = "form", encoder = CustomFormConvertor.class)
|
||||
YxDataR<SendMsgResp> sendMsg(@Body SendMsgReq req);
|
||||
|
||||
@Post(url = "/nimserver/msg/sendBatchMsg.action")
|
||||
YxDataR<YxCommonR> sendBatchMsg(@Body SendBatchMsgReq req);
|
||||
|
||||
/**
|
||||
* 发送自定义系统消息
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@Post(url = "/nimserver/msg/sendAttachMsg.action")
|
||||
YxDataR<YxCommonR> sendAttachMsg(@Body SendAttachMsgReq req);
|
||||
|
||||
@Post(url = "/nimserver/msg/sendBatchAttachMsg.action")
|
||||
YxDataR<YxCommonR> sendBatchAttachMsg(@Body SendBatchAttachMsgReq req);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.yunxin.client;
|
||||
|
||||
import com.dtflys.forest.annotation.BaseRequest;
|
||||
import com.dtflys.forest.annotation.Body;
|
||||
import com.dtflys.forest.annotation.Post;
|
||||
import com.ruoyi.yunxin.interceptor.GlodonTokenInterceptor;
|
||||
import com.ruoyi.yunxin.req.BlockReq;
|
||||
import com.ruoyi.yunxin.req.CreateUserReq;
|
||||
import com.ruoyi.yunxin.req.UnblockReq;
|
||||
import com.ruoyi.yunxin.req.UpdateTokenReq;
|
||||
import com.ruoyi.yunxin.resp.YxCommonR;
|
||||
|
||||
@BaseRequest(baseURL = "${baseUrl}", interceptor = GlodonTokenInterceptor.class)
|
||||
public interface ImUserClient {
|
||||
|
||||
|
||||
@Post(url = "/nimserver/user/create.action")
|
||||
YxCommonR createUser(@Body CreateUserReq req);
|
||||
|
||||
@Post(url = "/nimserver/user/update.action")
|
||||
YxCommonR updateToken(@Body UpdateTokenReq req);
|
||||
|
||||
@Post(url = "/nimserver/user/block.action")
|
||||
YxCommonR block(@Body BlockReq req);
|
||||
|
||||
@Post(url = "/nimserver/user/unblock.action")
|
||||
YxCommonR unblock(@Body UnblockReq req);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.yunxin.client;
|
||||
|
||||
import com.dtflys.forest.annotation.BaseRequest;
|
||||
import com.dtflys.forest.annotation.Body;
|
||||
import com.dtflys.forest.annotation.Post;
|
||||
import com.ruoyi.yunxin.interceptor.GlodonTokenInterceptor;
|
||||
import com.ruoyi.yunxin.req.CreateUserReq;
|
||||
import com.ruoyi.yunxin.req.UpdateUinfoReq;
|
||||
import com.ruoyi.yunxin.resp.YxCommonR;
|
||||
import com.ruoyi.yunxin.resp.YxInfoR;
|
||||
|
||||
@BaseRequest(baseURL = "${baseUrl}", interceptor = GlodonTokenInterceptor.class)
|
||||
public interface ImUserRefClient {
|
||||
|
||||
|
||||
@Post(url = "/nimserver/user/updateUinfo.action")
|
||||
YxCommonR updateUinfo(@Body UpdateUinfoReq query);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.yunxin.config;
|
||||
|
||||
import com.dtflys.forest.converter.json.ForestJacksonConverter;
|
||||
import com.dtflys.forest.converter.json.ForestJsonConverter;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class ForestConfig {
|
||||
|
||||
@Bean
|
||||
public ForestJsonConverter forestJacksonConverter() {
|
||||
ForestJacksonConverter converter = new ForestJacksonConverter();
|
||||
ObjectMapper mapper = converter.getMapper();
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
// converter.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.yunxin.config;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
|
||||
@Component
|
||||
public class ForestInit {
|
||||
|
||||
@Autowired
|
||||
private YunxinProperties yunxinProperties;
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
ForestConfiguration configuration = Forest.config();
|
||||
configuration.setVariableValue("baseUrl", (method) -> yunxinProperties.getBaseUrl());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ruoyi.yunxin.config;
|
||||
|
||||
import com.alibaba.ttl.threadpool.TtlExecutors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 线程变量定义
|
||||
* <p>created on 2023/3/3 11:16</p>
|
||||
* @author ZL
|
||||
*/
|
||||
public class YunxinExecutorConstant {
|
||||
|
||||
private final static int CPU_NUM = Runtime.getRuntime().availableProcessors();
|
||||
public static Executor COMMON_EXECUTOR;
|
||||
|
||||
static {
|
||||
ThreadPoolExecutor commonExecutor = new ThreadPoolExecutor(CPU_NUM,
|
||||
CPU_NUM << 2,
|
||||
5,
|
||||
TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<>(50),
|
||||
init("commonThreadPool-%d"),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
COMMON_EXECUTOR = TtlExecutors.getTtlExecutor(commonExecutor);
|
||||
}
|
||||
|
||||
private static ThreadFactory init(String nameFormat){
|
||||
return new ThreadFactoryBuilder().setNameFormat(nameFormat).build();
|
||||
}
|
||||
|
||||
private static ThreadPoolExecutor initExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, TimeUnit timeUnit,
|
||||
BlockingQueue<Runnable> workQueue, String nameFormat){
|
||||
return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue,
|
||||
init(nameFormat));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.yunxin.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "yunxin")
|
||||
public class YunxinProperties {
|
||||
private String baseUrl = "https://api.netease.im";
|
||||
private String appKey;
|
||||
private String appSecret;
|
||||
|
||||
private String defaultFromUid;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.ruoyi.yunxin.convertor;
|
||||
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import com.dtflys.forest.converter.ConvertOptions;
|
||||
import com.dtflys.forest.converter.ForestConverter;
|
||||
import com.dtflys.forest.converter.ForestEncoder;
|
||||
import com.dtflys.forest.converter.json.ForestJsonConverter;
|
||||
import com.dtflys.forest.http.ForestBody;
|
||||
import com.dtflys.forest.http.ForestRequest;
|
||||
import com.dtflys.forest.http.ForestRequestBody;
|
||||
import com.dtflys.forest.http.Lazy;
|
||||
import com.dtflys.forest.http.body.SupportFormUrlEncoded;
|
||||
import com.dtflys.forest.mapping.MappingParameter;
|
||||
import com.dtflys.forest.mapping.MappingTemplate;
|
||||
import com.dtflys.forest.utils.ForestDataType;
|
||||
import com.dtflys.forest.utils.ReflectUtils;
|
||||
import com.dtflys.forest.utils.RequestNameValue;
|
||||
import com.dtflys.forest.utils.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public class CustomFormConvertor implements ForestConverter<String>, ForestEncoder {
|
||||
|
||||
private final ForestConfiguration configuration;
|
||||
|
||||
public CustomFormConvertor(ForestConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T convertToJavaObject(String source, Type targetType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convertToJavaObject(byte[] source, Class<T> targetType, Charset charset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convertToJavaObject(byte[] source, Type targetType, Charset charset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForestDataType getDataType() {
|
||||
return ForestDataType.FORM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeToString(Object obj) {
|
||||
final ForestJsonConverter jsonConverter = configuration.getJsonConverter();
|
||||
final Map<String, Object> map = jsonConverter.convertObjectToMap(obj);
|
||||
final List<RequestNameValue> nameValueList = new LinkedList<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
final RequestNameValue nameValue = new RequestNameValue(entry.getKey(), MappingParameter.TARGET_BODY);
|
||||
nameValue.setValue(entry.getValue());
|
||||
nameValueList.add(nameValue);
|
||||
}
|
||||
final List<RequestNameValue> newNameValueList = processFromNameValueList(
|
||||
null, nameValueList, configuration, ConvertOptions.defaultOptions());
|
||||
return formUrlEncodedString(newNameValueList, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Form表单中的集合项
|
||||
* @param newNameValueList 键值对列表
|
||||
* @param configuration Forest配置
|
||||
* @param name 表单项目名
|
||||
* @param collection 集合对象
|
||||
* @param target 请求目标位置
|
||||
*/
|
||||
protected void processFormCollectionItem(List<RequestNameValue> newNameValueList, ForestConfiguration configuration, String name, Collection collection, int target) {
|
||||
int index = 0;
|
||||
for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
|
||||
final Object item = iterator.next();
|
||||
final String subName = name + "[" + index + "]";
|
||||
processFormItem(newNameValueList, configuration, subName, item, target);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Form表单中的数组项
|
||||
* @param newNameValueList 键值对列表
|
||||
* @param configuration Forest配置
|
||||
* @param name 表单项目名
|
||||
* @param array 数组
|
||||
* @param target 请求目标位置
|
||||
*/
|
||||
protected void processFormArrayItem(List<RequestNameValue> newNameValueList, ForestConfiguration configuration, String name, Object array, int target) {
|
||||
final int len = Array.getLength(array);
|
||||
for (int i = 0; i < len; i++) {
|
||||
final Object item = Array.get(array, i);
|
||||
final String subName = name + "[" + i + "]";
|
||||
processFormItem(newNameValueList, configuration, subName, item, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Form表单中的Map项
|
||||
* @param newNameValueList 键值对列表
|
||||
* @param configuration Forest配置
|
||||
* @param name 表单项目名
|
||||
* @param map Map对象
|
||||
* @param target 请求目标位置
|
||||
*/
|
||||
protected void processFormMapItem(List<RequestNameValue> newNameValueList, ForestConfiguration configuration, String name, Map map, int target) {
|
||||
for (Iterator<Map.Entry> iterator = map.entrySet().iterator(); iterator.hasNext(); ) {
|
||||
final Map.Entry entry = iterator.next();
|
||||
final Object mapKey = entry.getKey();
|
||||
final Object mapValue = entry.getValue();
|
||||
final String subName = name + "[" + mapKey + "]";
|
||||
processFormItem(newNameValueList, configuration, subName, mapValue, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理Form表单中的项
|
||||
* @param newNameValueList 键值对列表
|
||||
* @param configuration Forest配置
|
||||
* @param name 表单项目名
|
||||
* @param value 表单项目值
|
||||
* @param target 请求目标位置
|
||||
*/
|
||||
protected void processFormItem(List<RequestNameValue> newNameValueList, ForestConfiguration configuration, String name, Object value, int target) {
|
||||
if (StringUtils.isEmpty(name) && value == null) {
|
||||
return;
|
||||
}
|
||||
if (value != null) {
|
||||
final Class<?> itemClass = value.getClass();
|
||||
boolean needCollapse = false;
|
||||
if (value instanceof Collection) {
|
||||
final Collection<?> collection = (Collection<?>) value;
|
||||
if (collection.size() <= 8) {
|
||||
for (Object item : collection) {
|
||||
if (!ReflectUtils.isPrimaryType(item.getClass())) {
|
||||
needCollapse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (itemClass.isArray() && !ReflectUtils.isPrimaryArrayType(itemClass)) {
|
||||
needCollapse = true;
|
||||
}
|
||||
if (needCollapse) {
|
||||
if (value instanceof Collection) {
|
||||
processFormCollectionItem(newNameValueList, configuration, name, (Collection) value, target);
|
||||
} else if (itemClass.isArray()) {
|
||||
processFormArrayItem(newNameValueList, configuration, name, value, target);
|
||||
}
|
||||
} else if (ReflectUtils.isPrimaryType(itemClass)
|
||||
|| ReflectUtils.isPrimaryArrayType(itemClass)
|
||||
|| value instanceof Collection) {
|
||||
newNameValueList.add(new RequestNameValue(name, value, target));
|
||||
} else if (value instanceof Map) {
|
||||
processFormMapItem(newNameValueList, configuration, name, (Map) value, target);
|
||||
} else {
|
||||
Map<String, Object> itemAttrs = ReflectUtils.convertObjectToMap(value, configuration);
|
||||
for (Map.Entry<String, Object> entry : itemAttrs.entrySet()) {
|
||||
String subAttrName = entry.getKey();
|
||||
Object subAttrValue = entry.getValue();
|
||||
String subName = name + "." + subAttrName;
|
||||
processFormItem(newNameValueList, configuration, subName, subAttrValue, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 处理Form表单中的键值对列表
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param nameValueList 键值对列表
|
||||
* @param configuration Forest 配置对象
|
||||
* @param options 转换选项
|
||||
* @return 处理过的新键值对列表
|
||||
*/
|
||||
protected List<RequestNameValue> processFromNameValueList(
|
||||
final ForestRequest request,
|
||||
final List<RequestNameValue> nameValueList,
|
||||
final ForestConfiguration configuration,
|
||||
final ConvertOptions options) {
|
||||
final List<RequestNameValue> newNameValueList = new LinkedList<>();
|
||||
for (RequestNameValue nameValue : nameValueList) {
|
||||
final String name = nameValue.getName();
|
||||
if (options != null && options.shouldExclude(name)) {
|
||||
continue;
|
||||
}
|
||||
Object value = nameValue.getValue();
|
||||
if (Lazy.isEvaluatingLazyValue(value, request)) {
|
||||
continue;
|
||||
}
|
||||
if (options != null) {
|
||||
value = options.getValue(value, request);
|
||||
if (options.shouldIgnore(value)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
final int target = nameValue.getTarget();
|
||||
processFormItem(newNameValueList, configuration, name, value, target);
|
||||
}
|
||||
return newNameValueList;
|
||||
}
|
||||
|
||||
private String formUrlEncodedString(List<RequestNameValue> nameValueList, Charset charset) {
|
||||
final ForestJsonConverter jsonConverter = configuration.getJsonConverter();
|
||||
final StringBuilder strBuilder = new StringBuilder();
|
||||
for (int i = 0; i < nameValueList.size(); i++) {
|
||||
final RequestNameValue nameValue = nameValueList.get(i);
|
||||
if (!nameValue.isInBody()) {
|
||||
continue;
|
||||
}
|
||||
final String name = nameValue.getName();
|
||||
strBuilder.append(name);
|
||||
Object value = nameValue.getValue();
|
||||
if (value != null) {
|
||||
value = MappingTemplate.getFormValueString(jsonConverter, value);
|
||||
strBuilder.append("=").append(CustomURLEncoder.FORM_VALUE.encode(String.valueOf(value), charset));
|
||||
}
|
||||
if (i < nameValueList.size() - 1) {
|
||||
strBuilder.append("&");
|
||||
}
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeRequestBody(final ForestBody body, final Charset charset, final ConvertOptions options) {
|
||||
final List<RequestNameValue> nameValueList = new LinkedList<>();
|
||||
final Charset cs = charset != null ? charset : StandardCharsets.UTF_8;
|
||||
final ForestRequest request = body.getRequest();
|
||||
for (ForestRequestBody bodyItem : body) {
|
||||
if (bodyItem instanceof SupportFormUrlEncoded) {
|
||||
nameValueList.addAll(((SupportFormUrlEncoded) bodyItem).getNameValueList(request));
|
||||
}
|
||||
}
|
||||
final List<RequestNameValue> newNameValueList =
|
||||
processFromNameValueList(request, nameValueList, configuration, options);
|
||||
String strBody = formUrlEncodedString(newNameValueList, cs);
|
||||
byte[] bytes = strBody.getBytes(cs);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package com.ruoyi.yunxin.convertor;
|
||||
|
||||
import com.dtflys.forest.exceptions.ForestRuntimeException;
|
||||
import com.dtflys.forest.utils.StringUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class CustomURLEncoder {
|
||||
|
||||
/**
|
||||
* 空格字符
|
||||
*/
|
||||
private static final char SPACE = ' ';
|
||||
|
||||
/**
|
||||
* 十六进制字符的大写字符数组
|
||||
*/
|
||||
private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
/**
|
||||
* URI用户信息部分中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] USER_INFO_EXCLUDED_CHARACTERS = {'-', '.', '_', '+', '!', '(', ')', '*', ':', '=', '%'};
|
||||
|
||||
/**
|
||||
* URI路径中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] PATH_EXCLUDED_CHARACTERS = {'-', '.', '_', '+', '!', '(', ')', '[', ']', '*', '/', ':', '?', '=', '$', '@', '&', '%', '~'};
|
||||
|
||||
/**
|
||||
* 查询参数值中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] QUERY_VALUE_EXCLUDED_CHARACTERS = {'-', '.', '_', '+', '!', '(', ')', '[', ']', ',', '*', '/', ':', '?', '=', '%', '~'};
|
||||
|
||||
/**
|
||||
* (带不转义大括号的) 查询参数值中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] QUERY_VALUE_EXCLUDED_CHARACTERS_WITH_BRACE = {'-', '.', '_', '+', '!', '{', '}', '(', ')', '[', ']', ',', '*', '/', ':', '?', '=', '%', '~'};
|
||||
|
||||
|
||||
/**
|
||||
* 查询参数值中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] X_WWW_FORM_URLENCODED_VALUE_EXCLUDED_CHARACTERS = {'-', '.', '_', '!', '{', '}', '[', ']', ',', '"', '*', '/', ':', '?', '#', '='};
|
||||
|
||||
/**
|
||||
* 强制全编码中不会被编码的字符集
|
||||
*/
|
||||
private static final char[] ALL_EXCLUDED_CHARACTERS = {'*', '-', '.', '_'};
|
||||
|
||||
/**
|
||||
* 用于用户验证信息的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder USER_INFO = createUserInfoUrlEncoder();
|
||||
|
||||
/**
|
||||
* 用于URI路径部分的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder PATH = createPathUrlEncoder();
|
||||
|
||||
/**
|
||||
* 用于查询参数值部分的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder QUERY_VALUE = createQueryValueUrlEncoder();
|
||||
|
||||
/**
|
||||
* 用于 (带不转义大括号的) 查询参数值部分的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder QUERY_VALUE_WITH_BRACE = createQueryValueWithBraceUrlEncoder();
|
||||
|
||||
|
||||
/**
|
||||
* 用于表单参数值部分的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder FORM_VALUE = createXWwwFormUrlEncodedValueUrlEncoder();
|
||||
|
||||
|
||||
/**
|
||||
* 强制全编码的编码{@link CustomURLEncoder}
|
||||
*/
|
||||
public static final CustomURLEncoder ALL = createAllUrlEncoder();
|
||||
|
||||
|
||||
private static CustomURLEncoder createURLEncoder(final char[] excludedCharacters, final boolean encodeSpaceAsPlus) {
|
||||
final CustomURLEncoder encoder = new CustomURLEncoder();
|
||||
encoder.setEncodeSpaceAsPlus(encodeSpaceAsPlus);
|
||||
final int len = excludedCharacters.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
final char ch = excludedCharacters[i];
|
||||
encoder.excludeCharacter(ch);
|
||||
}
|
||||
return encoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用于用户验证信息编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createUserInfoUrlEncoder() {
|
||||
return createURLEncoder(USER_INFO_EXCLUDED_CHARACTERS, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建用于URI路径编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createPathUrlEncoder() {
|
||||
return createURLEncoder(PATH_EXCLUDED_CHARACTERS, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用于查询参数值编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createQueryValueUrlEncoder() {
|
||||
return createURLEncoder(QUERY_VALUE_EXCLUDED_CHARACTERS, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用于 (带不转义大括号的) 查询参数值编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createQueryValueWithBraceUrlEncoder() {
|
||||
return createURLEncoder(QUERY_VALUE_EXCLUDED_CHARACTERS_WITH_BRACE, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建用于表单参数值编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createXWwwFormUrlEncodedValueUrlEncoder() {
|
||||
return createURLEncoder(X_WWW_FORM_URLENCODED_VALUE_EXCLUDED_CHARACTERS, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建用于强制编码的{@link CustomURLEncoder}
|
||||
*
|
||||
* @return {@link CustomURLEncoder}实例
|
||||
*/
|
||||
public static CustomURLEncoder createAllUrlEncoder() {
|
||||
return createURLEncoder(ALL_EXCLUDED_CHARACTERS, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 安全编码字符集
|
||||
*/
|
||||
private final BitSet excludedCharacters;
|
||||
|
||||
/**
|
||||
* 是否将空格编码为+
|
||||
*/
|
||||
private boolean encodeSpaceAsPlus = false;
|
||||
|
||||
/**
|
||||
* {@link CustomURLEncoder}构造函数
|
||||
*/
|
||||
public CustomURLEncoder() {
|
||||
this(new BitSet(256));
|
||||
// 排除所有小写英文字母
|
||||
for (char i = 'a'; i <= 'z'; i++) {
|
||||
excludeCharacter(i);
|
||||
}
|
||||
// 排除所有大写英文字母
|
||||
for (char i = 'A'; i <= 'Z'; i++) {
|
||||
excludeCharacter(i);
|
||||
}
|
||||
// 排除所有数字
|
||||
for (char i = '0'; i <= '9'; i++) {
|
||||
excludeCharacter(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URLEncoder构造函数
|
||||
*
|
||||
* @param excludedCharacters 安全字符,安全字符不被编码
|
||||
*/
|
||||
private CustomURLEncoder(BitSet excludedCharacters) {
|
||||
this.excludedCharacters = excludedCharacters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除不被不被编码的字符
|
||||
*
|
||||
* @param c 字符
|
||||
*/
|
||||
public void excludeCharacter(char c) {
|
||||
excludedCharacters.set(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否将空格编码为+
|
||||
*
|
||||
* @param encodeSpaceAsPlus 是否将空格编码为+
|
||||
*/
|
||||
public void setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) {
|
||||
this.encodeSpaceAsPlus = encodeSpaceAsPlus;
|
||||
}
|
||||
|
||||
public String encode(final String path, final String charset) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final Charset cs = StringUtils.isEmpty(charset) ?
|
||||
StandardCharsets.UTF_8 : Charset.forName(charset);
|
||||
return encode(path, cs);
|
||||
} catch (Throwable th) {
|
||||
throw new ForestRuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isURLEncoded(final char[] charArray, final int index) {
|
||||
if (charArray[index] != '%') {
|
||||
return false;
|
||||
}
|
||||
final int len = charArray.length;
|
||||
if (index + 2 < len) {
|
||||
final char ch1 = charArray[index + 1];
|
||||
final char ch2 = charArray[index + 2];
|
||||
return Character.isDigit(ch1) && Character.isDigit(ch2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将URL中的字符串编码为%形式
|
||||
*
|
||||
* @param path 需要编码的字符串
|
||||
* @param charset 编码
|
||||
* @return 编码后的字符串
|
||||
*/
|
||||
public String encode(final String path, final Charset charset) {
|
||||
final StringBuilder builder = new StringBuilder(path.length());
|
||||
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
final OutputStreamWriter writer = new OutputStreamWriter(buf, charset);
|
||||
final char[] charArray = path.toCharArray();
|
||||
final int len = charArray.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
final char ch = charArray[i];
|
||||
if (isURLEncoded(charArray, i)) {
|
||||
builder.append(ch);
|
||||
} else if (excludedCharacters.get(ch)) {
|
||||
builder.append(ch);
|
||||
} else if (encodeSpaceAsPlus && ch == SPACE) {
|
||||
// 处理空格为加号+
|
||||
builder.append('+');
|
||||
} else {
|
||||
try {
|
||||
writer.write((char) ch);
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
buf.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
final byte[] ba = buf.toByteArray();
|
||||
for (byte toEncode : ba) {
|
||||
builder.append('%');
|
||||
final int high = (toEncode & 0xf0) >>> 4;//高位
|
||||
final int low = toEncode & 0x0f;//低位
|
||||
builder.append(HEX_DIGITS_UPPER[high]);
|
||||
builder.append(HEX_DIGITS_UPPER[low]);
|
||||
}
|
||||
buf.reset();
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.yunxin.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 0=文本消息,1=图片消息,2=语音消息,3=视频消息,4=发送地理位置消息,6=发送文件消息,10=发送提示消息,100=发送第三方自定义消息
|
||||
* <p>created on 2024/1/29 21:10</p>
|
||||
* @author 77
|
||||
*/
|
||||
@Getter
|
||||
public enum YxImTypeEnum {
|
||||
TXT(0,"文本消息"),
|
||||
IMAGE(1,"图片消息"),
|
||||
VOICE(2,"语音消息"),
|
||||
VIDEO(3,"视频消息"),
|
||||
GIS(4,"发送地理位置消息"),
|
||||
FILE(6,"发送文件消息"),
|
||||
NOTICE(10,"发送提示消息"),
|
||||
CUSTOM(100,"发送第三方自定义消息"),
|
||||
;
|
||||
private final Integer code;
|
||||
private final String text;
|
||||
|
||||
YxImTypeEnum(Integer code, String text) {
|
||||
this.code = code;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.ruoyi.yunxin.interceptor;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.exceptions.ForestRuntimeException;
|
||||
import com.dtflys.forest.http.ForestRequest;
|
||||
import com.dtflys.forest.http.ForestResponse;
|
||||
import com.dtflys.forest.interceptor.Interceptor;
|
||||
import com.ruoyi.yunxin.config.YunxinProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GlodonTokenInterceptor implements Interceptor {
|
||||
|
||||
@Autowired
|
||||
private YunxinProperties yunxinProperties;
|
||||
|
||||
@Override
|
||||
public boolean beforeExecute(ForestRequest request) {
|
||||
String nonce = UUID.fastUUID().toString();
|
||||
String curTime = DateUtil.currentSeconds() + "";
|
||||
request.addHeader("AppKey",yunxinProperties.getAppKey());
|
||||
request.addHeader("Nonce", nonce);
|
||||
request.addHeader("CurTime", curTime);
|
||||
request.addHeader("CheckSum", getCheckSum(yunxinProperties.getAppSecret(),nonce,curTime));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Object data, ForestRequest request, ForestResponse response) {
|
||||
log.info("onSuccess URI:{},QueryValues:{},耗时:{}ms,Param:{},RespStatus:{},Response:{}",
|
||||
request.getURI(),
|
||||
JSON.toJSONString(request.getQueryValues()),
|
||||
response.getTimeAsMillisecond(),
|
||||
JSONObject.toJSONString(request.getArguments()),
|
||||
response.getStatusCode(),
|
||||
response.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ForestRuntimeException ex, ForestRequest request, ForestResponse response) {
|
||||
log.info("onError URI:{},QueryValues:{},耗时:{}ms,Param:{},RespStatus:{},Response:{}",
|
||||
request.getURI(),
|
||||
JSON.toJSONString(request.getQueryValues()),
|
||||
response.getTimeAsMillisecond(),
|
||||
JSONObject.toJSONString(request.getArguments()),
|
||||
response.getStatusCode(),
|
||||
response.getContent());
|
||||
}
|
||||
|
||||
// 计算并获取CheckSum
|
||||
public static String getCheckSum(String appSecret, String nonce, String curTime) {
|
||||
return encode("sha1", appSecret + nonce + curTime);
|
||||
}
|
||||
|
||||
private static String encode(String algorithm, String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
MessageDigest messageDigest
|
||||
= MessageDigest.getInstance(algorithm);
|
||||
messageDigest.update(value.getBytes());
|
||||
return getFormattedText(messageDigest.digest());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFormattedText(byte[] bytes) {
|
||||
int len = bytes.length;
|
||||
StringBuilder buf = new StringBuilder(len * 2);
|
||||
for (int j = 0; j < len; j++) {
|
||||
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
|
||||
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.yunxin.manager;
|
||||
|
||||
import com.ruoyi.yunxin.config.YunxinProperties;
|
||||
import com.ruoyi.yunxin.util.CheckSumBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class YunxinManager {
|
||||
|
||||
@Autowired
|
||||
private YunxinProperties yunxinProperties;
|
||||
|
||||
public boolean checkNotify(String body,String curTime, String checkSum,String md5){
|
||||
try {
|
||||
String appSecret = yunxinProperties.getAppSecret();
|
||||
String verifyMD5 = CheckSumBuilder.getMD5(body);
|
||||
if(md5 == null || !md5.equals(verifyMD5)){
|
||||
log.error("云信回调校验异常,md5 不相等");
|
||||
return false;
|
||||
}
|
||||
String verifyChecksum = CheckSumBuilder.getCheckSum(appSecret, verifyMD5, curTime);
|
||||
if(checkSum == null || !checkSum.equals(verifyChecksum)){
|
||||
log.error("云信回调校验异常,checkSum 不相等");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}catch (Exception e){
|
||||
log.info("检查云信回调数据失败",e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BlockReq {
|
||||
private String accid;
|
||||
private boolean needkick = true;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreateUserReq {
|
||||
private String accid;
|
||||
private String token;
|
||||
private String name;
|
||||
|
||||
}
|
||||
27
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/req/Option.java
Normal file
27
ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/req/Option.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Option {
|
||||
/**
|
||||
* 该消息是否需要APNS推送或安卓系统通知栏推送
|
||||
*/
|
||||
private boolean push = true;
|
||||
/**
|
||||
* 该消息是否需要漫游(需要app开通漫游消息功能)
|
||||
*/
|
||||
private boolean roam = false;
|
||||
/**
|
||||
* 该消息是否存云端历史
|
||||
*/
|
||||
private boolean history = false;
|
||||
/**
|
||||
* 该消息是否需要发送方多端同步
|
||||
*/
|
||||
private boolean sendersync = false;
|
||||
/**
|
||||
* 该消息是否需要抄送第三方 (需要app开通消息抄送功能)
|
||||
*/
|
||||
private boolean route = false;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SendAttachMsgReq {
|
||||
private String from;
|
||||
private Integer msgType = 0;
|
||||
private String to;
|
||||
private String attach;
|
||||
private String option;
|
||||
private int save = 1;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SendBatchAttachMsgReq {
|
||||
private String fromAccid;
|
||||
private List<String> toAccids;
|
||||
private String attach;
|
||||
private int save = 1;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SendBatchMsgReq {
|
||||
private String fromAccid;
|
||||
private List<String> toAccids;
|
||||
private int type = 0;
|
||||
private String body;
|
||||
private String option;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SendMsgReq {
|
||||
private String from;
|
||||
private int ope = 0;
|
||||
private String to;
|
||||
private int type = 100;
|
||||
private String body;
|
||||
private String option;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UnblockReq {
|
||||
private String accid;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateTokenReq {
|
||||
private String accid;
|
||||
private String token;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateUinfoReq {
|
||||
private String accid;
|
||||
private String name;
|
||||
private String icon;
|
||||
private String birth;
|
||||
private String mobile;
|
||||
private String gender;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.yunxin.req.type;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class YxTextData {
|
||||
private String msg;
|
||||
|
||||
public YxTextData(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public YxTextData() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ruoyi.yunxin.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SendMsgResp {
|
||||
private Long msgid;
|
||||
private Long timetag;
|
||||
private Boolean antispam;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class YxCommonR {
|
||||
private Integer code;
|
||||
private String desc;
|
||||
|
||||
public boolean isSuccess(){
|
||||
return code != null && code == 200;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.yunxin.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class YxDataR<T> implements Serializable {
|
||||
private Integer code;
|
||||
private T data;
|
||||
private String desc;
|
||||
|
||||
public boolean isSuccess(){
|
||||
return code != null && code == 200;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class YxInfoR<T> {
|
||||
private Integer code;
|
||||
private T info;
|
||||
|
||||
public boolean isSuccess(){
|
||||
return code != null && code == 200;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.ruoyi.yunxin.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class CheckSumBuilder {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String appSecret = "77c09917d2de";
|
||||
String curTime = "1707115388876";
|
||||
String body = "{\"clientType\":\"IOS\",\"code\":\"200\",\"clientIp\":\"117.153.13.133\",\"accid\":\"33503\",\"sdkVersion\":\"91301\",\"eventType\":\"2\",\"deviceId\":\"2957635E-A852-4EBD-94C7-AA1FFA71551B\",\"timestamp\":\"1707115388795\"}";
|
||||
String verifyMD5 = CheckSumBuilder.getMD5(body);
|
||||
System.out.println(verifyMD5);
|
||||
String verifyChecksum = CheckSumBuilder.getCheckSum(appSecret, verifyMD5, curTime);
|
||||
System.out.println(verifyChecksum);
|
||||
}
|
||||
|
||||
|
||||
// 计算并获取CheckSum
|
||||
public static String getCheckSum(String appSecret, String bodyMd5, String curTime) {
|
||||
return encode("sha1", appSecret + bodyMd5 + curTime);
|
||||
}
|
||||
|
||||
// 计算并获取md5值
|
||||
public static String getMD5(String requestBody) {
|
||||
return encode("md5", requestBody);
|
||||
}
|
||||
|
||||
private static String encode(String algorithm, String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
|
||||
messageDigest.update(value.getBytes());
|
||||
return getFormattedText(messageDigest.digest());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getFormattedText(byte[] bytes) {
|
||||
int len = bytes.length;
|
||||
StringBuilder buf = new StringBuilder(len * 2);
|
||||
for (int j = 0; j < len; j++) {
|
||||
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
|
||||
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
|
||||
}
|
||||
return buf.toString();
|
||||
|
||||
}
|
||||
|
||||
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ruoyi.yunxin.util;
|
||||
|
||||
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class RestTemplateUtil {
|
||||
|
||||
public static RestTemplate restTemplate;
|
||||
|
||||
static {
|
||||
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||
requestFactory.setConnectTimeout(3000);
|
||||
requestFactory.setReadTimeout(3000);
|
||||
restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(requestFactory));
|
||||
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
|
||||
//添加转换器
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
if (messageConverter instanceof StringHttpMessageConverter) {
|
||||
StringHttpMessageConverter converter = (StringHttpMessageConverter) messageConverter;
|
||||
converter.setDefaultCharset(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.yunxin.util;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
|
||||
public class YunDateUtils {
|
||||
public static String localDateTimeToString(LocalDateTime localDateTime){
|
||||
return localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user