REQ-2010: 添加撤回功能
This commit is contained in:
parent
02ba772644
commit
cb1b72f4c1
@ -0,0 +1,25 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import feign.codec.Encoder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class FormConfig {
|
||||
|
||||
private final ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
@Bean
|
||||
public Encoder feignFormEncoder() {
|
||||
return new FormEncoder(new SpringEncoder(messageConverters));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public class FormEncoder extends SpringFormEncoder {
|
||||
|
||||
private final Encoder delegate;
|
||||
|
||||
public FormEncoder(Encoder delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
|
||||
if (object == null) {
|
||||
delegate.encode(null, bodyType, template);
|
||||
return;
|
||||
}
|
||||
FormRequest formRequest = AnnotationUtils.getAnnotation(object.getClass(), FormRequest.class);
|
||||
if (formRequest == null) {
|
||||
delegate.encode(object, bodyType, template);
|
||||
return;
|
||||
}
|
||||
template.header("Content-Type", "application/x-www-form-urlencoded");
|
||||
// 为了使用@JSONField
|
||||
JSONObject jsonReq = JSON.parseObject(JSON.toJSONString(object), JSONObject.class);
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
for (String key : jsonReq.keySet()) {
|
||||
params.put(key, jsonReq.getString(key));
|
||||
}
|
||||
super.encode(params, Encoder.MAP_STRING_WILDCARD, template);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface FormRequest {
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import cn.axzo.im.channel.netease.dto.QueryEventRequest;
|
||||
import cn.axzo.im.channel.netease.dto.QueryEventResponse;
|
||||
import cn.axzo.im.channel.netease.dto.QueryMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.QueryMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.RevokeMessageRequest;
|
||||
import lombok.Data;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import static cn.axzo.im.channel.netease.client.NimClient.URL;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@FeignClient(name = "NimClient", url = URL)
|
||||
public interface NimClient {
|
||||
|
||||
String URL = "https://api.netease.im/nimserver";
|
||||
|
||||
@PostMapping(value = "/msg/delMsgOneWay.action")
|
||||
CodeResponse revoke(RevokeMessageRequest request);
|
||||
|
||||
@PostMapping(value = "/history/queryUserEvents.action")
|
||||
QueryEventResponse queryEvents(QueryEventRequest request);
|
||||
|
||||
@PostMapping(value = "/history/querySessionMsg.action")
|
||||
QueryMessageResponse queryMessages(QueryMessageRequest request);
|
||||
|
||||
@Data
|
||||
class CodeResponse {
|
||||
private Integer code;
|
||||
private String desc;
|
||||
private int size;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import cn.axzo.im.channel.netease.AppKeyUtil;
|
||||
import cn.axzo.im.channel.netease.CheckSumUtil;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class NimRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
private final AppKeyUtil appKeyUtil;
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
String url = template.feignTarget().url();
|
||||
if (!url.startsWith(NimClient.URL))
|
||||
return;
|
||||
String nonce = UUID.randomUUID().toString();
|
||||
String curTime = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String checkSum = CheckSumUtil.getCheckSum(appKeyUtil.getAppSecret(), nonce, curTime);
|
||||
template.header("AppKey", appKeyUtil.getAppKey());
|
||||
template.header("Nonce", nonce);
|
||||
template.header("CurTime", curTime);
|
||||
template.header("CheckSum", checkSum);
|
||||
template.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.framework.domain.ServiceException;
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class QueryEventRequest {
|
||||
// 要查询用户的accid
|
||||
@JSONField(name = "accid")
|
||||
@NotBlank
|
||||
private String accountId;
|
||||
// 开始时间,毫秒级
|
||||
@JSONField(name = "begintime")
|
||||
@NotBlank
|
||||
private String beginTime;
|
||||
// 截止时间,毫秒级
|
||||
@JSONField(name = "endtime")
|
||||
@NotBlank
|
||||
private String endTime;
|
||||
// 本次查询的记录数量上限(最多100条),小于等于0,或者大于100,会提示参数错误
|
||||
private int limit;
|
||||
// 1按时间正序排列,2按时间降序排列。其它返回参数414错误。默认是按降序排列
|
||||
private int reverse = 2;
|
||||
|
||||
public void setBeginTime(String beginTime) {
|
||||
this.beginTime = parseDate(beginTime);
|
||||
}
|
||||
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = parseDate(endTime);
|
||||
}
|
||||
|
||||
private static String parseDate(String input) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
try {
|
||||
long time = sdf.parse(input).getTime();
|
||||
return String.valueOf(time);
|
||||
} catch (ParseException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class QueryEventResponse extends NimClient.CodeResponse {
|
||||
// 总共记录数
|
||||
private int size;
|
||||
private List<EventItem> events;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@SuppressWarnings("LombokSetterMayBeUsed")
|
||||
public static class EventItem {
|
||||
// //用户accid
|
||||
private String accid;
|
||||
// 发生时间,ms
|
||||
private Integer timestamp;
|
||||
// //2表示登录,3表示登出
|
||||
private int eventType;
|
||||
// 用户clientip
|
||||
private String clientIp;
|
||||
// sdk 版本
|
||||
private int sdkVersion;
|
||||
// //终端
|
||||
private String clientType;
|
||||
// 设备ID,可选字段
|
||||
private String deviceId;
|
||||
// 登录时设置的自定义tag,可选字段
|
||||
private String customTag;
|
||||
// 登录成功状态,200表示成功
|
||||
private int code;
|
||||
|
||||
public void setTimestamp(Integer timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setEventType(int eventType) {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.framework.domain.ServiceException;
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class QueryMessageRequest {
|
||||
// 发送者accid
|
||||
@NotBlank
|
||||
private String from;
|
||||
// 接收者accid
|
||||
@NotBlank
|
||||
private String to;
|
||||
// 开始时间,毫秒级
|
||||
@NotBlank
|
||||
@JSONField(name = "begintime")
|
||||
private String beginTime;
|
||||
// 截止时间,毫秒级
|
||||
@NotBlank
|
||||
@JSONField(name = "endtime")
|
||||
private String endTime;
|
||||
// 本次查询的消息条数上限(最多100条),小于等于0,或者大于100,会提示参数错误
|
||||
private int limit;
|
||||
// 1按时间正序排列,2按时间降序排列。其它返回参数414错误.默认是按降序排列,即时间戳最晚的消息排在最前面。
|
||||
private int reverse = 2;
|
||||
//查询指定的多个消息类型,类型之间用","分割,不设置该参数则查询全部类型消息格式示例: 0,1,2,3
|
||||
//类型支持:0:文本,1:图片,2:语音,3:视频,4:地理位置,5:通知,6:文件,10:提示,11:Robot,100:自定义
|
||||
private String type = "0,1,6,100";
|
||||
// 查询结果中是否需要包含无感知消息,true:包含,false:不包含,默认为 false
|
||||
private String includeNoSenseMsg = "true";
|
||||
// 结束查询的最后一条消息的 msgid(不包含在查询结果中),用于定位锚点
|
||||
private String excludeMsgid;
|
||||
|
||||
public void setBeginTime(String beginTime) {
|
||||
this.beginTime = parseDate(beginTime);
|
||||
}
|
||||
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = parseDate(endTime);
|
||||
}
|
||||
|
||||
private static String parseDate(String input) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
try {
|
||||
long time = sdf.parse(input).getTime();
|
||||
return String.valueOf(time);
|
||||
} catch (ParseException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class QueryMessageResponse extends NimClient.CodeResponse {
|
||||
// 消息集合
|
||||
private List<Map<String, ?>> msgs;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class RevokeMessageRequest {
|
||||
@JSONField(name = "deleteMsgid")
|
||||
private String messageId;
|
||||
// 消息发送者的云信 IM 账号(accid)
|
||||
private String from;
|
||||
// 消息接收方。如果待撤回消息为单聊消息,则需传入消息接收者的云信 IM 账号(accid)。如果是群消息,则需传入对应群的ID (tid)
|
||||
private String to;
|
||||
// 待撤回消息的类型,只能传入 13 或 14。13 表示点对点消息撤回,14 表示群消息撤回,传入其他值均判定为参数错误
|
||||
private int type = 13;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package cn.axzo.im.controller;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import cn.axzo.im.channel.netease.dto.QueryEventRequest;
|
||||
import cn.axzo.im.channel.netease.dto.QueryMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.RevokeMessageRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class PrivateController {
|
||||
|
||||
private final NimClient nimClient;
|
||||
|
||||
@PostMapping("/private/revoke")
|
||||
public Object revoke(@Valid @RequestBody RevokeMessageRequest request) {
|
||||
return nimClient.revoke(request);
|
||||
}
|
||||
|
||||
@PostMapping("/private/queryEvents")
|
||||
public Object queryEvents(@Valid @RequestBody QueryEventRequest request) {
|
||||
return nimClient.queryEvents(request);
|
||||
}
|
||||
|
||||
@PostMapping("/private/queryMessages")
|
||||
public Object queryMessages(@Valid @RequestBody QueryMessageRequest request) {
|
||||
return nimClient.queryMessages(request);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
package cn.axzo.im.utils;
|
||||
|
||||
import cn.axzo.basics.common.exception.ServiceException;
|
||||
import cn.axzo.basics.common.util.AssertUtil;
|
||||
import cn.axzo.framework.domain.web.result.ApiListResult;
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
public class BizAssertions {
|
||||
|
||||
/**
|
||||
* 断言为NULL
|
||||
*/
|
||||
public static void assertNull(Object actual, String message, Object... args) {
|
||||
AssertUtil.isNull(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言不为NULL
|
||||
*/
|
||||
public static void assertNotNull(Object actual, String message, Object... args) {
|
||||
AssertUtil.notNull(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言集合不为空
|
||||
*/
|
||||
public static void assertNotEmpty(Collection<?> actual, String message, Object... args) {
|
||||
AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言数组不为空
|
||||
*/
|
||||
public static <T> void assertNotEmpty(T[] actual, String message, Object... args) {
|
||||
AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言集合为空
|
||||
*/
|
||||
public static void assertEmpty(Collection<?> actual, String message, Object... args) {
|
||||
AssertUtil.isEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言值为真
|
||||
*/
|
||||
public static void assertTrue(boolean actual, String message, Object... args) {
|
||||
AssertUtil.isTrue(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言值不为真
|
||||
*/
|
||||
public static void assertFalse(boolean actual, String message, Object... args) {
|
||||
AssertUtil.isFalse(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言值2个值是否equals
|
||||
*/
|
||||
public static void assertEquals(Object expected, Object actual, String message, Object... args) {
|
||||
if (!Objects.equals(expected, actual)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言值2个值是否不equals
|
||||
*/
|
||||
public static void assertNotEquals(Object expected, Object actual, String message, Object... args) {
|
||||
if (Objects.equals(expected, actual)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(CommonResponse<T> response) {
|
||||
return assertResponse(response, "error resp={}", JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(CommonResponse<T> response, String message, Object... args) {
|
||||
if (response.getCode() != HttpStatus.HTTP_OK) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(ApiResult<T> response) {
|
||||
return assertResponse(response, "error resp={}", JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(ApiResult<T> response, String message, Object... args) {
|
||||
if (!response.isSuccess()) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
|
||||
public static <T> List<T> assertResponse(ApiListResult<T> response) {
|
||||
return assertResponse(response, "error resp={}", JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
public static <T> List<T> assertResponse(ApiListResult<T> response, String message, Object... args) {
|
||||
if (!response.isSuccess()) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
|
||||
}
|
||||
@ -50,7 +50,7 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: ${NACOS_HOST:test-nacos.axzo.cn}:${NACOS_PORT:80}
|
||||
server-addr: ${NACOS_HOST:https://test-nacos.axzo.cn}:${NACOS_PORT:443}
|
||||
file-extension: yaml
|
||||
namespace: ${NACOS_NAMESPACE_ID:f3c0f0d2-bac4-4498-bee7-9c3636b3afdf}
|
||||
---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user