Merge branch 'feature/REQ-3345'
# Conflicts: # im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupChangeOwnerEventHandler.java
This commit is contained in:
commit
389047223d
@ -1,4 +1,4 @@
|
||||
package cn.axzo.im.event.inner;
|
||||
package cn.axzo.im.center.api.enums;
|
||||
|
||||
import cn.axzo.framework.rocketmq.Event;
|
||||
import lombok.Getter;
|
||||
@ -9,11 +9,15 @@ import lombok.Getter;
|
||||
* @Created by lilong
|
||||
*/
|
||||
@Getter
|
||||
public enum EventTypeEnum {
|
||||
public enum MqEventType {
|
||||
|
||||
MESSAGE_HISTORY_CREATED("message-history", "message-history-created", "发送记录创建"),
|
||||
MESSAGE_HISTORY_UPDATED("message-history", "message-history-updated", "发送记录修改"),
|
||||
MESSAGE_CHAT_GROUP_CREATE("chat-group", "chat-group-create", "群聊创建"),
|
||||
MESSAGE_CHAT_GROUP_CREATE("chat-group", "chat-group-create", "群聊创建[项目]"),
|
||||
GROUP_CREATED("group", "group-created", "创建群聊"),
|
||||
GROUP_DISMISSED("group", "group-dismissed", "群解散"),
|
||||
GROUP_ADD_MEMBERS("group", "group-add-members", "添加群聊成员"),
|
||||
GROUP_REMOVE_MEMBERS("group", "group-remove-members", "移除群聊成员"),
|
||||
UPDATE_AVATAR("profile", "update-avatar", "头像更新"),
|
||||
NODE_USER_CREATE("node-user", "node-user-create", "节点用户创建"),
|
||||
NODE_USER_UPDATE("node-user", "node-user-update", "节点用户修改"),
|
||||
@ -23,7 +27,7 @@ public enum EventTypeEnum {
|
||||
SAAS_ROLE_USER_RELATION_UPSERT("saas-role-user-relation", "saas-role-user-relation-upsert", "更新用户角色信息"),
|
||||
;
|
||||
|
||||
EventTypeEnum(String model, String name, String desc) {
|
||||
MqEventType(String model, String name, String desc) {
|
||||
this.eventCode = Event.EventCode.builder()
|
||||
.module(model)
|
||||
.name(name)
|
||||
@ -33,8 +37,8 @@ public enum EventTypeEnum {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
private String model;
|
||||
private String name;
|
||||
private String desc;
|
||||
private Event.EventCode eventCode;
|
||||
private final String model;
|
||||
private final String name;
|
||||
private final String desc;
|
||||
private final Event.EventCode eventCode;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package cn.axzo.im.center.api.feign;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.im.center.api.vo.group.GroupInfo;
|
||||
import cn.axzo.im.center.api.vo.req.GroupAddMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupCreateRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupDismissRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupFindInfoRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupGetInfoRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupGetMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupGetOwnerRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupRemoveMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupAddMembersResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupGetInfoResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupGetMembersResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupGetOwnerResponse;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@FeignClient(name = "im-center", url = "${axzo.service.im-center:http://im-center:8080}")
|
||||
public interface GroupApi {
|
||||
|
||||
/**
|
||||
* 创建群
|
||||
*/
|
||||
@PostMapping("/api/im/group/createGroup")
|
||||
ApiResult<GroupCreateResponse> createGroup(@RequestBody @Validated GroupCreateRequest request);
|
||||
|
||||
/**
|
||||
* 解散群
|
||||
*/
|
||||
@PostMapping("/api/im/group/dismissGroup")
|
||||
ApiResult<Void> dismissGroup(@RequestBody @Validated GroupDismissRequest request);
|
||||
|
||||
/**
|
||||
* 添加群成员
|
||||
*/
|
||||
@PostMapping("/api/im/group/addMembers")
|
||||
ApiResult<GroupAddMembersResponse> addMembers(@RequestBody @Validated GroupAddMembersRequest request);
|
||||
|
||||
/**
|
||||
* 移除群成员
|
||||
*/
|
||||
@PostMapping("/api/im/group/removeMembers")
|
||||
ApiResult<Void> removeMembers(@RequestBody @Validated GroupRemoveMembersRequest request);
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
*/
|
||||
@PostMapping("/api/im/group/getMembers")
|
||||
ApiResult<GroupGetMembersResponse> getMembers(@RequestBody @Validated GroupGetMembersRequest request);
|
||||
|
||||
/**
|
||||
* 获取群主信息
|
||||
*/
|
||||
@PostMapping("/api/im/group/getOwner")
|
||||
ApiResult<GroupGetOwnerResponse> getOwner(@RequestBody @Validated GroupGetOwnerRequest request);
|
||||
|
||||
/**
|
||||
* 获取群信息
|
||||
*/
|
||||
@PostMapping("/api/im/group/getGroupInfo")
|
||||
ApiResult<GroupGetInfoResponse> getGroupInfo(@RequestBody @Validated GroupGetInfoRequest request);
|
||||
|
||||
/**
|
||||
* 查询群信息
|
||||
*/
|
||||
@PostMapping("/api/im/group/findGroupInfo")
|
||||
ApiResult<GroupGetInfoResponse> findGroupInfo(@RequestBody @Validated GroupFindInfoRequest request);
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package cn.axzo.im.center.api.feign;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiPageResult;
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.im.center.api.vo.req.GroupCreateRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupMessagePageQueryRequest;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupMessagePageQueryResponse;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@FeignClient(name = "im-center", url = "${axzo.service.im-center:http://im-center:8080}")
|
||||
public interface GroupMessageApi {
|
||||
|
||||
/**
|
||||
* 查询群聊历史消息
|
||||
*/
|
||||
@PostMapping("/api/im/group/message/pageQuery")
|
||||
ApiPageResult<GroupMessagePageQueryResponse> pageQuery(
|
||||
@RequestBody @Validated GroupMessagePageQueryRequest request);
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo;
|
||||
import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest;
|
||||
import cn.axzo.im.center.api.vo.req.MessageInfo;
|
||||
import cn.axzo.im.center.api.vo.req.SendChatMessageRequest;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
|
||||
import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest;
|
||||
@ -64,6 +65,10 @@ public interface MessageApi {
|
||||
ApiResult<MessageTaskResp> sendTemplateMessageAsync(
|
||||
@RequestBody @Validated SendTemplateMessageParam sendMessageParam);
|
||||
|
||||
@PostMapping("/api/im/template-message/async/send/chatMessage")
|
||||
ApiResult<Long> sendChatMessage(
|
||||
@RequestBody @Validated SendChatMessageRequest request);
|
||||
|
||||
/**
|
||||
* 更新消息
|
||||
*/
|
||||
|
||||
@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class SendPriority {
|
||||
|
||||
public static final SendPriority CHAT_MESSAGE = create(900);
|
||||
public static final SendPriority TEMPLATE_MESSAGE = create(1000);
|
||||
public static final SendPriority SYSTEM_CUSTOM_MESSAGE = create(5000);
|
||||
public static final SendPriority UPDATE_MESSAGE = create(5500);
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
package cn.axzo.im.center.api.vo;
|
||||
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
@ -17,10 +20,11 @@ import javax.validation.constraints.NotNull;
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
// IMPORTANT: 不要删除这个注解, 避免@Data被@Setter, @Getter取代
|
||||
@EqualsAndHashCode
|
||||
public class PersonAccountAttribute {
|
||||
|
||||
private static final PersonAccountAttribute ROBOT =
|
||||
new PersonAccountAttribute("0", 0L, 0L, AppTypeEnum.NONE);
|
||||
|
||||
/**
|
||||
* 接收消息的personId
|
||||
*/
|
||||
@ -28,24 +32,75 @@ public class PersonAccountAttribute {
|
||||
private String personId;
|
||||
|
||||
/**
|
||||
* appType = AppTypeEnum.CMP时,因为网易云信无法对同一个账号做企业隔离,只能一个企业一个账号,
|
||||
* 所以需要根据organizationalUnitId获取账号
|
||||
* 单位id, 管理端人员必填
|
||||
*/
|
||||
private Long ouId;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
* 项目id, 选填
|
||||
*/
|
||||
private Long workspaceId;
|
||||
|
||||
/**
|
||||
* 发送消息到App端
|
||||
* 工人端、企业端、服务器
|
||||
* CM、CMP、SYSTEM
|
||||
*
|
||||
* @See cn.axzo.im.center.common.enums.AppTypeEnum
|
||||
* 发送消息到App端. CM: 工人端, CMP: 企业端
|
||||
*/
|
||||
@NotNull(message = "appType不能为空")
|
||||
private AppTypeEnum appType;
|
||||
|
||||
}
|
||||
public static PersonAccountAttribute robot() {
|
||||
return ROBOT;
|
||||
}
|
||||
|
||||
public static PersonAccountAttribute cmp(Long personId, Long ouId) {
|
||||
return PersonAccountAttribute.builder()
|
||||
.personId(String.valueOf(personId))
|
||||
.ouId(ouId)
|
||||
.appType(AppTypeEnum.CMP)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static PersonAccountAttribute cm(Long personId) {
|
||||
return PersonAccountAttribute.builder()
|
||||
.personId(String.valueOf(personId))
|
||||
.appType(AppTypeEnum.CM)
|
||||
.build();
|
||||
}
|
||||
|
||||
public Long personIdAsLong() {
|
||||
if (NumberUtils.isDigits(personId))
|
||||
return Long.valueOf(personId);
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public Long ouIdOrDefault() {
|
||||
return getOuIdOrDefault(ouId);
|
||||
}
|
||||
|
||||
public static Long getOuIdOrDefault(Long ouId) {
|
||||
return ouId == null ? 0L : ouId;
|
||||
}
|
||||
|
||||
@JsonIgnore @JSONField(serialize = false)
|
||||
public boolean isRobot() {
|
||||
return this == ROBOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof PersonAccountAttribute)) return false;
|
||||
PersonAccountAttribute that = (PersonAccountAttribute) o;
|
||||
return Objects.equals(personId, that.personId)
|
||||
&& Objects.equals(ouIdOrDefault(), that.ouIdOrDefault())
|
||||
&& appType == that.appType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(personId, ouIdOrDefault(), appType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[personId:%s,ouId:%s,appType:%s]", personId, ouId, appType);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package cn.axzo.im.center.api.vo.group;
|
||||
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupInfo {
|
||||
/**
|
||||
* 群id
|
||||
*/
|
||||
private Long tid;
|
||||
/**
|
||||
* 群名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 群业务id
|
||||
*/
|
||||
private String bizCode;
|
||||
/**
|
||||
* 群类型. VISA: 变洽签, WORKSPACE: 项目群, WORKSPACE_OU: 项目单位群, WORKSPACE_TEAM: 项目班组群
|
||||
*/
|
||||
private GroupType type;
|
||||
/**
|
||||
* 群聊业务扩展信息
|
||||
*/
|
||||
private Map<String, Object> bizGroupInfo;
|
||||
/**
|
||||
* 群头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 群成员数量
|
||||
*/
|
||||
private Long memberCount;
|
||||
/**
|
||||
* 群成员上限
|
||||
*/
|
||||
private Long memberLimit;
|
||||
/**
|
||||
* 群主账号
|
||||
*/
|
||||
private String ownerAccount;
|
||||
/**
|
||||
* 群主personId
|
||||
*/
|
||||
private Long ownerPersonId;
|
||||
/**
|
||||
* 群创建人ouId
|
||||
*/
|
||||
private Long createPersonId;
|
||||
/**
|
||||
* 是否解散
|
||||
*/
|
||||
private YesOrNo isDismissed;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createAt;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.axzo.im.center.api.vo.group;
|
||||
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.GroupMemberType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupMemberInfo {
|
||||
/**
|
||||
* 群成员personId
|
||||
*/
|
||||
private Long personId;
|
||||
/**
|
||||
* 群成员ouId
|
||||
*/
|
||||
private Long personOuId;
|
||||
/**
|
||||
* 群成员端类型
|
||||
*/
|
||||
private AppTypeEnum appType;
|
||||
/**
|
||||
* 群成员类型. MEMBER: 普通成员, OWNER: 群主
|
||||
*/
|
||||
private GroupMemberType memberType;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.im.center.api.vo.mq;
|
||||
|
||||
import cn.axzo.im.center.api.vo.group.GroupInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupChangedMessage extends MqMessage {
|
||||
|
||||
/**
|
||||
* 群信息
|
||||
*/
|
||||
private GroupInfo group;
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package cn.axzo.im.center.api.vo.mq;
|
||||
|
||||
import cn.axzo.im.center.api.vo.group.GroupInfo;
|
||||
import cn.axzo.im.center.api.vo.group.GroupMemberInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GroupMembersChangeMessage extends MqMessage {
|
||||
|
||||
/**
|
||||
* 群信息
|
||||
*/
|
||||
private GroupInfo group;
|
||||
|
||||
/**
|
||||
* 群成员信息
|
||||
*/
|
||||
private GroupMemberInfo member;
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.axzo.im.center.api.vo.mq;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public abstract class MqMessage implements Serializable {
|
||||
|
||||
/**
|
||||
* 消息唯一id
|
||||
*/
|
||||
private String messageId = UUID.randomUUID().toString();
|
||||
|
||||
/**
|
||||
* 消息发送时间
|
||||
*/
|
||||
private Date messageSendTime = new Date();
|
||||
|
||||
/**
|
||||
* 消息发送时间, 字符串格式
|
||||
*/
|
||||
private String messageSendTimeStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(messageSendTime);
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupAddMembersRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
/**
|
||||
* 邀请者
|
||||
*/
|
||||
@NotNull(message = "inviter不能为空")
|
||||
private String inviter;
|
||||
|
||||
/**
|
||||
* 群成员, 不包含群主. members数量不能超过199
|
||||
*/
|
||||
@NotEmpty(message = "群成员不能为空")
|
||||
private Set<PersonAccountAttribute> members;
|
||||
|
||||
public void addMember(PersonAccountAttribute member) {
|
||||
if (members == null)
|
||||
members = new HashSet<>();
|
||||
members.add(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GroupCreateRequest {
|
||||
|
||||
/**
|
||||
* 群类型. groupType和bizCode一起做幂等控制
|
||||
*/
|
||||
@NotNull(message = "群类型不能为空")
|
||||
private GroupType groupType;
|
||||
|
||||
/**
|
||||
* 业务编码. groupType和bizCode一起做幂等控制
|
||||
*/
|
||||
@NotBlank(message = "业务码不能为空")
|
||||
private String bizCode;
|
||||
|
||||
/**
|
||||
* 群名称
|
||||
*/
|
||||
@NotBlank(message = "群名称不能为空")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 群主账号
|
||||
*/
|
||||
@NotNull(message = "群主账号不能为空")
|
||||
private PersonAccountAttribute owner;
|
||||
|
||||
/**
|
||||
* 群成员, 不包含群主. members数量不能超过199
|
||||
*/
|
||||
@NotEmpty(message = "群成员不能为空")
|
||||
private Set<PersonAccountAttribute> members;
|
||||
|
||||
/**
|
||||
* 群成员上限, 不传默认为 499
|
||||
*/
|
||||
private Long memberLimit;
|
||||
|
||||
/**
|
||||
* 群头像,最大长度 1024 位字符
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 群聊业务扩展信息, 透传到群属性中. 不能传太多属性或较长字段,云信有长度限制
|
||||
*/
|
||||
private Map<String, Object> bizGroupInfo;
|
||||
|
||||
public Map<String, Object> bizGroupInfoOrEmpty() {
|
||||
return bizGroupInfo == null ? Collections.emptyMap() : bizGroupInfo;
|
||||
}
|
||||
|
||||
@JsonIgnore @JSONField(serialize = false, deserialize = false)
|
||||
public Set<PersonAccountAttribute> getPeople() {
|
||||
Set<PersonAccountAttribute> ownerAndMembers = new HashSet<>(members);
|
||||
ownerAndMembers.add(owner);
|
||||
return ownerAndMembers;
|
||||
}
|
||||
|
||||
public void addMembers(Set<PersonAccountAttribute> members) {
|
||||
if (this.members == null)
|
||||
this.members = new HashSet<>();
|
||||
this.members.addAll(members);
|
||||
}
|
||||
|
||||
public void addMember(PersonAccountAttribute member) {
|
||||
if (members == null)
|
||||
members = new HashSet<>();
|
||||
members.add(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupDismissRequest {
|
||||
|
||||
@NotNull(message = "群id不能为空")
|
||||
private Long tid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GroupFindInfoRequest {
|
||||
/**
|
||||
* 群类型
|
||||
*/
|
||||
@NotNull(message = "群类型不能为空")
|
||||
private GroupType groupType;
|
||||
|
||||
/**
|
||||
* 业务码
|
||||
*/
|
||||
@NotBlank(message = "业务码不能为空")
|
||||
private String bizCode;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetInfoRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetMembersRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetOwnerRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.basics.common.page.PageRequest;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupMessagePageQueryRequest extends PageRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupRemoveMembersRequest {
|
||||
|
||||
@NotNull(message = "群ID不能为空")
|
||||
private Long tid;
|
||||
|
||||
/**
|
||||
* 一次性最多传200个
|
||||
*/
|
||||
@NotEmpty(message = "群成员不能为空")
|
||||
private Set<PersonAccountAttribute> members;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.NimMessageType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class SendChatMessageRequest extends SendMessageRequest {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@NotNull(message = "消息类型不能为空")
|
||||
private NimMessageType messageType;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@NotEmpty(message = "消息体不能为空")
|
||||
private Map<String, Object> messageBody;
|
||||
|
||||
/**
|
||||
* 尝试同步发送
|
||||
* trySyncSend=true, 接收人只有一个的情况下就会异步发送
|
||||
*/
|
||||
private boolean trySyncSend;
|
||||
|
||||
/**
|
||||
* 发送文本消息
|
||||
*
|
||||
* @param text 消息内容
|
||||
*/
|
||||
public void setAsTextMessage(String text) {
|
||||
messageType = NimMessageType.TEXT;
|
||||
addToMessageBody("msg", text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送图片消息
|
||||
*
|
||||
* @param name 图片名称
|
||||
* @param md5 图片文件 md5,按照字节流加密
|
||||
* @param url url
|
||||
* @param ext 图片后缀
|
||||
* @param width 宽,单位为像素
|
||||
* @param height 高,单位为像素
|
||||
* @param size 图片文件大小,单位为字节(Byte)
|
||||
*/
|
||||
public void setAsImageMessage(String name,
|
||||
String md5,
|
||||
String url,
|
||||
String ext,
|
||||
int width,
|
||||
int height,
|
||||
int size) {
|
||||
messageType = NimMessageType.IMAGE;
|
||||
addToMessageBody("name", name);
|
||||
addToMessageBody("md5", md5);
|
||||
addToMessageBody("url", url);
|
||||
addToMessageBody("ext", ext);
|
||||
addToMessageBody("w", width);
|
||||
addToMessageBody("h", height);
|
||||
addToMessageBody("size", size);
|
||||
}
|
||||
|
||||
private void addToMessageBody(String key, Object value) {
|
||||
if (messageBody == null)
|
||||
messageBody = new java.util.HashMap<>();
|
||||
messageBody.put(key, value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class SendMessageRequest {
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
@NotBlank(message = "业务id不能为空")
|
||||
private String bizId;
|
||||
|
||||
/**
|
||||
* 发送人
|
||||
*/
|
||||
@NotNull(message = "发送人不能为空")
|
||||
private PersonAccountAttribute sender;
|
||||
|
||||
/**
|
||||
* 消息接收用户信息
|
||||
*/
|
||||
@Valid
|
||||
private Set<PersonAccountAttribute> receivePersons;
|
||||
|
||||
/**
|
||||
* 消息接收IM账号或群id
|
||||
*/
|
||||
private Set<String> imReceiveAccounts;
|
||||
|
||||
|
||||
public Long determineSenderPersonId() {
|
||||
if (sender != null)
|
||||
return Long.parseLong(sender.getPersonId());
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public boolean isSendByRobot() {
|
||||
return sender.getAppType() == null;
|
||||
}
|
||||
|
||||
public Set<PersonAccountAttribute> receivePersonsOrEmpty() {
|
||||
if (receivePersons == null)
|
||||
return Collections.emptySet();
|
||||
return new HashSet<>(receivePersons);
|
||||
}
|
||||
|
||||
public Set<String> imReceiveAccountsOrEmpty() {
|
||||
return imReceiveAccounts == null ? Collections.emptySet() : imReceiveAccounts;
|
||||
}
|
||||
|
||||
public Long determineSenderOuId() {
|
||||
if (sender != null)
|
||||
return sender.ouIdOrDefault();
|
||||
return 0L;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,40 +1,17 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.TemplatedMsgType;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SendTemplateMessageParam {
|
||||
|
||||
/**
|
||||
* 发送人
|
||||
*/
|
||||
@NotNull(message = "发送人不能为空")
|
||||
private PersonAccountAttribute sender;
|
||||
|
||||
/**
|
||||
* 消息接收用户信息
|
||||
*/
|
||||
@NotEmpty(message = "消息接收用户信息不能为空")
|
||||
@Valid
|
||||
private List<PersonAccountAttribute> receivePersons;
|
||||
@Setter
|
||||
@Getter
|
||||
public class SendTemplateMessageParam extends SendMessageRequest {
|
||||
|
||||
/**
|
||||
* 消息标题
|
||||
@ -63,11 +40,6 @@ public class SendTemplateMessageParam {
|
||||
*/
|
||||
private JSONObject ext;
|
||||
|
||||
/**
|
||||
* 业务的唯一ID,用于查询发送消息的记录和结果,不验证唯一
|
||||
*/
|
||||
private String bizId;
|
||||
|
||||
private Integer sendPriority;
|
||||
|
||||
private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE;
|
||||
@ -77,34 +49,14 @@ public class SendTemplateMessageParam {
|
||||
*/
|
||||
private boolean isUpdatable;
|
||||
|
||||
public boolean isSendByRobot() {
|
||||
return sender.getAppType() == null;
|
||||
}
|
||||
|
||||
private PushContent pushContent;
|
||||
|
||||
private List<ExcludePushPayload> excludePushPayloads;
|
||||
|
||||
public Long determineSenderPersonId() {
|
||||
if (sender != null)
|
||||
return Long.parseLong(sender.getPersonId());
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public Long determineSenderOuId() {
|
||||
if (sender != null)
|
||||
return sender.getOuId();
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public boolean isUpdatable() {
|
||||
return isUpdatable;
|
||||
}
|
||||
|
||||
public Set<PersonAccountAttribute> uniqueReceivePersons() {
|
||||
return new HashSet<>(receivePersons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupAddMembersResponse {
|
||||
|
||||
/**
|
||||
* 未找到IM账号的列表
|
||||
*/
|
||||
private Set<PersonAccountAttribute> accountsNotFound = new HashSet<>();
|
||||
|
||||
private JSONObject faccid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GroupCreateResponse {
|
||||
|
||||
/**
|
||||
* 群id
|
||||
*/
|
||||
private Long tid;
|
||||
|
||||
/**
|
||||
* 未找到IM账号的列表
|
||||
*/
|
||||
private Set<PersonAccountAttribute> accountsNotFound;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.api.vo.group.GroupInfo;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetInfoResponse {
|
||||
private GroupInfo group;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.api.vo.group.GroupMemberInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetMembersResponse {
|
||||
/**
|
||||
* 群成员信息列表
|
||||
*/
|
||||
private List<GroupMemberInfo> members;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.api.vo.group.GroupMemberInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupGetOwnerResponse {
|
||||
|
||||
/**
|
||||
* 群主信息
|
||||
*/
|
||||
private GroupMemberInfo owner;
|
||||
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package cn.axzo.im.center.api.vo.resp;
|
||||
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.NimFromClientType;
|
||||
import cn.axzo.im.center.common.enums.NimMessageType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GroupMessagePageQueryResponse {
|
||||
/**
|
||||
* 服务器消息id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 群id
|
||||
*/
|
||||
private Long tid;
|
||||
/**
|
||||
* 发送者IM账号
|
||||
*/
|
||||
private String fromAccount;
|
||||
/**
|
||||
* 发送者自然人ID
|
||||
*/
|
||||
private Long fromPersonId;
|
||||
/**
|
||||
* 发送者OU ID
|
||||
*/
|
||||
private Long fromPersonOuId;
|
||||
/**
|
||||
* 发送者应用类型
|
||||
*/
|
||||
private AppTypeEnum fromPersonAppType;
|
||||
/**
|
||||
* 是否来自机器人
|
||||
*/
|
||||
private YesOrNo isFromRobot;
|
||||
/**
|
||||
* 云信消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
/**
|
||||
* 消息类型. TEXT: 文本, IMAGE: 图片, SPEECH: 语音, VIDEO: 视频, POSITION: 地理位置, FILE: 文件, CUSTOM: 自定义消息
|
||||
*/
|
||||
private NimMessageType messageType;
|
||||
/**
|
||||
* 发送者的客户端类型. ANDROID, IOS, PC, WEB, REST, MAC
|
||||
*/
|
||||
private NimFromClientType fromClientType;
|
||||
/**
|
||||
* 发送时间
|
||||
*/
|
||||
private Long sendTime;
|
||||
/**
|
||||
* 消息体. 具体格式根据messageType来定, 字段参考云信文档的body部分: https://doc.yunxin.163.com/messaging/server-apis/DE0MTk0OTY?platform=server#%E5%8E%86%E5%8F%B2%E6%B6%88%E6%81%AF%E6%9F%A5%E8%AF%A2%E8%BF%94%E5%9B%9E%E7%9A%84%E6%B6%88%E6%81%AF%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E
|
||||
*/
|
||||
private JSONObject body;
|
||||
|
||||
/**
|
||||
* 单位名称
|
||||
*/
|
||||
private String unitName;
|
||||
|
||||
/**
|
||||
* 发送者名称
|
||||
*/
|
||||
private String fromPersonName;
|
||||
|
||||
/**
|
||||
* 发送者头像
|
||||
*/
|
||||
private String fromPersonAvatar;
|
||||
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
private String messageIdClient;
|
||||
}
|
||||
@ -11,5 +11,6 @@ import lombok.Setter;
|
||||
@Getter
|
||||
public class UpdatableMessageSendResult {
|
||||
private String bizMessageId;
|
||||
private PersonAccountAttribute account;
|
||||
private PersonAccountAttribute person;
|
||||
private String imAccount;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.axzo.im.center.common.enums;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -26,9 +27,14 @@ public enum AppTypeEnum implements CodeDefinition<String> {
|
||||
/**
|
||||
* 服务器 机器人
|
||||
*/
|
||||
SYSTEM("system", "服务器");
|
||||
SYSTEM("system", "服务器"),
|
||||
|
||||
/**
|
||||
* 没有端信息
|
||||
*/
|
||||
NONE("none", "没有端信息");
|
||||
|
||||
@EnumValue
|
||||
private final String code;
|
||||
|
||||
private final String message;
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
package cn.axzo.im.center.common.enums;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public enum GroupMemberType implements CodeDefinition<String> {
|
||||
|
||||
OWNER,
|
||||
ADMIN,
|
||||
MEMBER;
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package cn.axzo.im.center.common.enums;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum GroupType implements CodeDefinition<String> {
|
||||
NONE("未指定"),
|
||||
VISA("变洽签"),
|
||||
WORKSPACE("项目群"),
|
||||
WORKSPACE_OU("项目单位群"),
|
||||
WORKSPACE_TEAM("项目班组群"),
|
||||
;
|
||||
|
||||
private final String description;
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return name();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package cn.axzo.im.center.common.enums;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public enum NimFromClientType implements CodeDefinition<Integer> {
|
||||
ANDROID(1),
|
||||
IOS(2),
|
||||
PC(4),
|
||||
WEB(16),
|
||||
REST(32),
|
||||
MAC(64)
|
||||
;
|
||||
|
||||
private final Integer nimCode;
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return nimCode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package cn.axzo.im.center.common.enums;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum NimMessageType implements CodeDefinition<Integer> {
|
||||
TEXT("文本", 0),
|
||||
IMAGE("图片", 1),
|
||||
SPEECH("语音", 2),
|
||||
VIDEO("视频", 3),
|
||||
POSITION("地理位置", 4),
|
||||
FILE("文件", 6),
|
||||
CUSTOM("自定义", 100),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
private final int nimCode;
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return nimCode;
|
||||
}
|
||||
}
|
||||
@ -20,4 +20,11 @@ public enum YesOrNo implements CodeDefinition<String> {
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
public static YesOrNo valueOf(boolean value) {
|
||||
return value ? YES : NO;
|
||||
}
|
||||
|
||||
public boolean booleanValue() {
|
||||
return this == YES;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import cn.axzo.im.channel.netease.dto.RegisterResponse;
|
||||
import cn.axzo.im.channel.netease.dto.RegisterUpdateRequest;
|
||||
import cn.axzo.im.channel.netease.dto.UserAddChatGroupRequest;
|
||||
import cn.axzo.im.channel.netease.dto.UserAddChatGroupResponse;
|
||||
import cn.axzo.im.group.support.GroupRateLimiter;
|
||||
import cn.axzo.im.utils.ImProperties;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
@ -133,6 +134,9 @@ public class NimChannelService implements IMChannelProvider {
|
||||
@Resource
|
||||
private ImProperties props;
|
||||
|
||||
@Resource
|
||||
private GroupRateLimiter rateLimiter;
|
||||
|
||||
@Override
|
||||
public String getProviderAppKey() {
|
||||
return appKeyUtil.getAppKey();
|
||||
@ -213,8 +217,6 @@ public class NimChannelService implements IMChannelProvider {
|
||||
if (messageInfo == null) {
|
||||
throw new ServiceException("发送单聊消息请求,请求参数不能为空!");
|
||||
}
|
||||
//目前支持的默认值 0、单聊消息 100、自定义消息
|
||||
messageInfo.setOpe(MSG_OPE);
|
||||
HashMap<String, Object> paramMap = Maps.newHashMap();
|
||||
paramMap.put("from", messageInfo.getFrom());
|
||||
paramMap.put("body", messageInfo.getBody());
|
||||
@ -397,6 +399,7 @@ public class NimChannelService implements IMChannelProvider {
|
||||
Map<String, String> authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
|
||||
log.warn("chatGroupCreate-请求网易云信,URL:{},Header:{},请求参数:{}", CHAT_GROUP_CREATE,
|
||||
JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
|
||||
rateLimiter.requireCreateGroup();
|
||||
HttpResponse response = HttpRequest.post(CHAT_GROUP_CREATE).addHeaders(authHeaderMap)
|
||||
.form(paramMap).timeout(5000).execute();
|
||||
String result = response.body();
|
||||
@ -454,6 +457,7 @@ public class NimChannelService implements IMChannelProvider {
|
||||
Map<String, String> authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
|
||||
log.warn("userAddChatGroup-请求网易云信,URL:{},Header:{},请求参数:{}", USER_ADD_CHAT_GROUP,
|
||||
JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
|
||||
rateLimiter.requireAddMember();
|
||||
HttpResponse response = HttpRequest.post(USER_ADD_CHAT_GROUP).addHeaders(authHeaderMap)
|
||||
.form(paramMap).timeout(5000).execute();
|
||||
String result = response.body();
|
||||
@ -486,6 +490,7 @@ public class NimChannelService implements IMChannelProvider {
|
||||
Map<String, String> authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
|
||||
log.warn("kickChatGroup-请求网易云信,URL:{},Header:{},请求参数:{}", KICK_CHAT_GROUP,
|
||||
JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
|
||||
rateLimiter.requireRemoveMember();
|
||||
HttpResponse response = HttpRequest.post(KICK_CHAT_GROUP).addHeaders(authHeaderMap)
|
||||
.form(paramMap).timeout(5000).execute();
|
||||
String result = response.body();
|
||||
@ -542,6 +547,7 @@ public class NimChannelService implements IMChannelProvider {
|
||||
Map<String, String> authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
|
||||
log.warn("chatGroupQuery-请求网易云信,URL:{},Header:{},请求参数:{}", CHAT_GROUP_QUERY,
|
||||
JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
|
||||
rateLimiter.requireGetGroupInfo();
|
||||
HttpResponse response = HttpRequest.post(CHAT_GROUP_QUERY).addHeaders(authHeaderMap)
|
||||
.form(paramMap).timeout(5000).execute();
|
||||
String result = response.body();
|
||||
@ -582,6 +588,7 @@ public class NimChannelService implements IMChannelProvider {
|
||||
Map<String, String> authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
|
||||
log.warn("changeOwner-请求网易云信,URL:{},Header:{},请求参数:{}", CHANGE_OWNER,
|
||||
JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
|
||||
rateLimiter.requireChangeOwner();
|
||||
HttpResponse response = HttpRequest.post(CHANGE_OWNER).addHeaders(authHeaderMap)
|
||||
.form(paramMap).timeout(5000).execute();
|
||||
String result = response.body();
|
||||
|
||||
@ -1,24 +1,32 @@
|
||||
package cn.axzo.im.channel.netease.client;
|
||||
|
||||
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.DismissGroupRequest;
|
||||
import cn.axzo.im.channel.netease.dto.DismissGroupResponse;
|
||||
import cn.axzo.im.channel.netease.dto.GetAccountInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.GetAccountInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.GetGroupInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.GetGroupInfoResponse;
|
||||
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.RefreshTokenRequest;
|
||||
import cn.axzo.im.channel.netease.dto.RefreshTokenResponse;
|
||||
import cn.axzo.im.channel.netease.dto.RevokeMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.SendCustomMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.SendCustomMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupAddMembersRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupAddMembersResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupCreateRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupCreateResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupDismissRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupDismissResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetMessagesRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetMessagesResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupRemoveMembersRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupRemoveMembersResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryEventRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryEventResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimRefreshTokenRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimRefreshTokenResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimSendCustomMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimSendCustomMessageResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoResponse;
|
||||
import lombok.Data;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -37,37 +45,49 @@ public interface NimClient {
|
||||
String URL = "https://api.netease.im/nimserver";
|
||||
|
||||
@PostMapping(value = "/msg/delMsgOneWay.action")
|
||||
CodeResponse revoke(RevokeMessageRequest request);
|
||||
NimCodeResponse revoke(NimRevokeMessageRequest request);
|
||||
|
||||
@PostMapping(value = "/history/queryUserEvents.action")
|
||||
QueryEventResponse queryEvents(QueryEventRequest request);
|
||||
NimQueryEventResponse queryEvents(NimQueryEventRequest request);
|
||||
|
||||
@PostMapping(value = "/history/querySessionMsg.action")
|
||||
QueryMessageResponse queryMessages(QueryMessageRequest request);
|
||||
NimQueryMessageResponse queryMessages(NimQueryMessageRequest request);
|
||||
|
||||
@PostMapping(value = "/user/refreshToken.action")
|
||||
RefreshTokenResponse refreshToken(RefreshTokenRequest request);
|
||||
NimRefreshTokenResponse refreshToken(NimRefreshTokenRequest request);
|
||||
|
||||
@PostMapping(value = "/msg/sendAttachMsg.action")
|
||||
SendCustomMessageResponse sendCustomMessage(SendCustomMessageRequest request);
|
||||
NimSendCustomMessageResponse sendCustomMessage(NimSendCustomMessageRequest request);
|
||||
|
||||
@PostMapping(value = "/msg/sendBatchAttachMsg.action")
|
||||
BatchSendCustomMessageResponse batchSendCustomMessage(BatchSendCustomMessageRequest request);
|
||||
NimBatchSendCustomMessageResponse batchSendCustomMessage(NimBatchSendCustomMessageRequest request);
|
||||
|
||||
@PostMapping(value = "/user/getUinfos.action")
|
||||
GetAccountInfoResponse getAccountInfo(GetAccountInfoRequest request);
|
||||
NimGetAccountInfoResponse getAccountInfo(NimGetAccountInfoRequest request);
|
||||
|
||||
@PostMapping(value = "/user/updateUinfo.action")
|
||||
UpdateAccountInfoResponse updateAccountInfo(UpdateAccountInfoRequest request);
|
||||
NimUpdateAccountInfoResponse updateAccountInfo(NimUpdateAccountInfoRequest request);
|
||||
|
||||
@PostMapping(value = "/team/remove.action")
|
||||
DismissGroupResponse dismissGroup(DismissGroupRequest request);
|
||||
NimGroupDismissResponse dismissGroup(NimGroupDismissRequest request);
|
||||
|
||||
@PostMapping(value = "/team/queryDetail.action")
|
||||
GetGroupInfoResponse getGroupInfo(GetGroupInfoRequest request);
|
||||
NimGroupGetInfoResponse getGroupInfo(NimGroupGetInfoRequest request);
|
||||
|
||||
@PostMapping(value = "/team/create.action")
|
||||
NimGroupCreateResponse createGroup(NimGroupCreateRequest request);
|
||||
|
||||
@PostMapping(value = "/team/add.action")
|
||||
NimGroupAddMembersResponse addGroupMembers(NimGroupAddMembersRequest request);
|
||||
|
||||
@PostMapping(value = "/team/kick.action")
|
||||
NimGroupRemoveMembersResponse removeGroupMembers(NimGroupRemoveMembersRequest request);
|
||||
|
||||
@PostMapping(value = "/history/queryTeamMsg.action")
|
||||
NimGroupGetMessagesResponse getGroupMessages(NimGroupGetMessagesRequest request);
|
||||
|
||||
@Data
|
||||
class CodeResponse {
|
||||
class NimCodeResponse {
|
||||
private Integer code;
|
||||
private String desc;
|
||||
private int size;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class DismissGroupResponse extends NimClient.CodeResponse {
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GetGroupInfoResponse extends NimClient.CodeResponse {
|
||||
|
||||
private Object tinfo;
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GroupMemberRequest {
|
||||
|
||||
private String members;
|
||||
|
||||
public void addMembers(Collection<String> members) {
|
||||
if (StringUtils.isBlank(this.members))
|
||||
this.members = "[]";
|
||||
this.members = JSON.parseArray(this.members)
|
||||
.fluentAddAll(members)
|
||||
.toJSONString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.group.controller.GroupMessageController;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Data;
|
||||
@ -38,7 +39,9 @@ public class MessageBody {
|
||||
private String msgContent;
|
||||
|
||||
/**
|
||||
* 消息通知结构体
|
||||
* @see GroupMessageController 有字符串引用
|
||||
*
|
||||
* 消息通知结构体, 名称不能随便调整
|
||||
*/
|
||||
private String msgBody;
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class BatchSendCustomMessageRequest implements CustomPushRequest {
|
||||
public class NimBatchSendCustomMessageRequest implements CustomPushRequest {
|
||||
@JSONField(name = "fromAccid")
|
||||
private String fromAccount;
|
||||
// ["aaa","bbb"]
|
||||
@ -13,7 +13,7 @@ import java.util.Set;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class BatchSendCustomMessageResponse extends NimClient.CodeResponse {
|
||||
public class NimBatchSendCustomMessageResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private String desc;
|
||||
private Set<String> unregister = Collections.emptySet();
|
||||
@ -3,10 +3,8 @@ package cn.axzo.im.channel.netease.dto;
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -15,7 +13,7 @@ import java.util.List;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class GetAccountInfoRequest {
|
||||
public class NimGetAccountInfoRequest {
|
||||
|
||||
/**
|
||||
* 用户帐号 ID 列表,例如:["id1", "id2", "id3"]。格式错误会返回 414 参数错误。
|
||||
@ -15,7 +15,7 @@ import java.util.Optional;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class GetAccountInfoResponse extends NimClient.CodeResponse {
|
||||
public class NimGetAccountInfoResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private List<AccountInfo> uinfos;
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@FormRequest
|
||||
public class NimGroupAddMembersRequest extends GroupMemberRequest {
|
||||
|
||||
private Long tid;
|
||||
private String owner;
|
||||
private String msg;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupAddMembersResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private JSONObject faccid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter @Getter
|
||||
@FormRequest
|
||||
public class NimGroupCreateRequest extends GroupMemberRequest {
|
||||
|
||||
/**
|
||||
* 群名称,最大长度 64 位字符
|
||||
*/
|
||||
@JSONField(name = "tname")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 群主账号,accid,最大长度 32 位字符
|
||||
*/
|
||||
private String owner;
|
||||
|
||||
/**
|
||||
* 群描述,最大长度 512 位字符
|
||||
*/
|
||||
@JSONField(name = "intro")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 邀请发送的文字,最大长度 150 位字符
|
||||
*/
|
||||
@JSONField(name = "msg")
|
||||
private String introduceMessage;
|
||||
|
||||
/**
|
||||
* 创建群时,若 members 不为空,那么邀请其入群是否需要同意
|
||||
* 0: 不需要被邀请人同意加入群(默认)
|
||||
* 1: 需要被邀请人同意才可以加入群
|
||||
* 只有当 beinvitemode = 0 时,magree 才能设为 1,即 时,magree =1 才生效。
|
||||
*/
|
||||
@JSONField(name = "magree")
|
||||
private Integer memberAgree;
|
||||
|
||||
/**
|
||||
* 群创建完成后,通过 SDK 侧操作申请入群的验证方式
|
||||
* 0: 不用验证
|
||||
* 1: 需要群主或管理员的验证
|
||||
* 2: 不允许任何人加入
|
||||
*/
|
||||
@JSONField(name = "joinmode")
|
||||
private Integer joinMode = 0;
|
||||
|
||||
/**
|
||||
* 自定义高级群扩展属性,第三方可以跟据此属性自定义扩展自己的群属性,建议为 JSON,最大长度 1024 位字符
|
||||
*/
|
||||
private String custom;
|
||||
|
||||
/**
|
||||
* 群头像,最大长度 1024 位字符
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
/**
|
||||
* 群创建完成后,邀请入群时是否需要被邀请人的同意
|
||||
* 0: 需要同意(默认)
|
||||
* 1: 不需要同意
|
||||
*/
|
||||
@JSONField(name = "beinvitemode")
|
||||
private Integer beInviteMode = 1;
|
||||
|
||||
/**
|
||||
* 邀请权限,即谁可以邀请他人入群
|
||||
* 0: 群主和管理员(默认)
|
||||
* 1: 所有人
|
||||
*/
|
||||
@JSONField(name = "invitemode")
|
||||
private Integer inviteMode = 1;
|
||||
|
||||
/**
|
||||
* 自定义扩展字段,最大长度 512 位字符
|
||||
*/
|
||||
private String attach;
|
||||
|
||||
public void addAttachment(String key, Object value) {
|
||||
if (StringUtils.isBlank(this.attach))
|
||||
this.attach = "{}";
|
||||
this.attach = JSON.parseObject(this.attach)
|
||||
.fluentPut(key, value)
|
||||
.toJSONString();
|
||||
}
|
||||
|
||||
public void addCustom(String key, Object value) {
|
||||
if (StringUtils.isBlank(this.custom))
|
||||
this.custom = "{}";
|
||||
this.custom = JSON.parseObject(this.custom)
|
||||
.fluentPut(key, value)
|
||||
.toJSONString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupCreateResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
/**
|
||||
* 群id
|
||||
*/
|
||||
private Long tid;
|
||||
|
||||
/**
|
||||
* 入群失败的账号(accid)列表
|
||||
* 如果创建时邀请的成员中存在加群数量超过限制的情况,会返回入群失败的 accid 以及附言(msg)
|
||||
*/
|
||||
private List<String> faccid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -10,9 +10,9 @@ import javax.validation.constraints.NotBlank;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class DismissGroupRequest {
|
||||
public class NimGroupDismissRequest {
|
||||
@NotBlank(message = "群id不能为空")
|
||||
private String tid;
|
||||
private Long tid;
|
||||
@NotBlank(message = "群主id不能为空")
|
||||
private String owner;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupDismissResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
public boolean isNotGroupOwnerError() {
|
||||
return getCode() == 403;
|
||||
}
|
||||
|
||||
public boolean isGroupNotFoundError() {
|
||||
return getCode() == 803;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
@ -10,9 +11,13 @@ import javax.validation.constraints.NotBlank;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class GetGroupInfoRequest {
|
||||
public class NimGroupGetInfoRequest {
|
||||
|
||||
@NotBlank(message = "群id不能为空")
|
||||
private String tid;
|
||||
private Long tid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupGetInfoResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private NimGroupInfo tinfo;
|
||||
|
||||
public boolean isGroupNotFoundError() {
|
||||
return getCode() == 414;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@FormRequest
|
||||
public class NimGroupGetMessagesRequest {
|
||||
private Long tid;
|
||||
private String accid;
|
||||
private Long begintime;
|
||||
private Long endtime;
|
||||
private Long limit;
|
||||
// 1 按时间正序排列,2 按时间降序排列。其它返回参数 414 错误。默认是按降序排列,即时间戳最晚的消息排在最前面。
|
||||
private Integer reverse;
|
||||
// 查询指定的多个消息类型,类型之间用","分割,不设置该参数则查询全部类型消息格式示例:0,1,2,3
|
||||
//类型支持:0:文本,1:图片,2:语音,3:视频,4:地理位置,5:通知,6:文件,10:提示,11:Robot,100:自定义
|
||||
private String type;
|
||||
private Boolean checkTeamValid;
|
||||
private Boolean includeNoSenseMsg;
|
||||
// 结束查询的最后一条消息的 msgid(不包含在查询结果中),用于定位锚点
|
||||
private Long excludeMsgid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupGetMessagesResponse extends NimClient.NimCodeResponse {
|
||||
private List<NimGroupMessage> msgs;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class NimGroupMessage {
|
||||
private String from;
|
||||
private String msgid;
|
||||
private Long sendtime;
|
||||
private Integer type;
|
||||
private Integer fromclienttype;
|
||||
private String msgidclient;
|
||||
private JSONObject body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupInfo {
|
||||
|
||||
private NimGroupMemberInfo owner;
|
||||
private Set<NimGroupMemberInfo> admins;
|
||||
private Set<NimGroupMemberInfo> members;
|
||||
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public Set<NimGroupMemberInfo> getPeople() {
|
||||
HashSet<NimGroupMemberInfo> people = new HashSet<>();
|
||||
people.add(owner);
|
||||
if (admins != null)
|
||||
people.addAll(admins);
|
||||
if (this.members != null)
|
||||
people.addAll(this.members);
|
||||
return people;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupMemberInfo {
|
||||
private String nick;
|
||||
private Date createtime;
|
||||
private Date updatetime;
|
||||
private String accid;
|
||||
private boolean mute;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof NimGroupMemberInfo)) return false;
|
||||
NimGroupMemberInfo that = (NimGroupMemberInfo) o;
|
||||
return Objects.equals(accid, that.accid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(accid);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.FormRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@FormRequest
|
||||
public class NimGroupRemoveMembersRequest extends GroupMemberRequest {
|
||||
private Long tid;
|
||||
private String owner;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class NimGroupRemoveMembersResponse extends NimClient.NimCodeResponse {
|
||||
private JSONObject faccid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ import java.text.SimpleDateFormat;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class QueryEventRequest {
|
||||
public class NimQueryEventRequest {
|
||||
// 要查询用户的accid
|
||||
@JSONField(name = "accid")
|
||||
@NotBlank
|
||||
@ -11,7 +11,7 @@ import java.util.List;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class QueryEventResponse extends NimClient.CodeResponse {
|
||||
public class NimQueryEventResponse extends NimClient.NimCodeResponse {
|
||||
// 总共记录数
|
||||
private int size;
|
||||
private List<EventItem> events;
|
||||
@ -14,7 +14,7 @@ import java.text.SimpleDateFormat;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class QueryMessageRequest {
|
||||
public class NimQueryMessageRequest {
|
||||
// 发送者accid
|
||||
@NotBlank
|
||||
private String from;
|
||||
@ -1,7 +1,6 @@
|
||||
package cn.axzo.im.channel.netease.dto;
|
||||
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@ -13,7 +12,7 @@ import java.util.Map;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class QueryMessageResponse extends NimClient.CodeResponse {
|
||||
public class NimQueryMessageResponse extends NimClient.NimCodeResponse {
|
||||
// 消息集合
|
||||
private List<Map<String, ?>> msgs;
|
||||
}
|
||||
@ -11,7 +11,7 @@ import javax.validation.constraints.NotBlank;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class RefreshTokenRequest {
|
||||
public class NimRefreshTokenRequest {
|
||||
@NotBlank
|
||||
@JSONField(name = "accid")
|
||||
private String accid;
|
||||
@ -10,7 +10,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class RefreshTokenResponse extends NimClient.CodeResponse {
|
||||
public class NimRefreshTokenResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private Info info;
|
||||
|
||||
@ -9,7 +9,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class RevokeMessageRequest {
|
||||
public class NimRevokeMessageRequest {
|
||||
@JSONField(name = "deleteMsgid")
|
||||
private String messageId;
|
||||
// 消息发送者的云信 IM 账号(accid)
|
||||
@ -9,7 +9,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class SendCustomMessageRequest implements CustomPushRequest {
|
||||
public class NimSendCustomMessageRequest implements CustomPushRequest {
|
||||
@JSONField(name = "from")
|
||||
private String fromAccount;
|
||||
@JSONField(name = "to")
|
||||
@ -10,7 +10,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class SendCustomMessageResponse extends NimClient.CodeResponse {
|
||||
public class NimSendCustomMessageResponse extends NimClient.NimCodeResponse {
|
||||
|
||||
private String desc;
|
||||
|
||||
@ -11,7 +11,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@FormRequest
|
||||
public class UpdateAccountInfoRequest {
|
||||
public class NimUpdateAccountInfoRequest {
|
||||
|
||||
@JSONField(name = "accid")
|
||||
private String imAccountId;
|
||||
@ -10,7 +10,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class UpdateAccountInfoResponse extends NimClient.CodeResponse {
|
||||
public class NimUpdateAccountInfoResponse extends NimClient.NimCodeResponse {
|
||||
private NimAccountInfo info;
|
||||
|
||||
@Override
|
||||
@ -31,6 +31,7 @@ public class MqProducer {
|
||||
}
|
||||
//生产消息
|
||||
eventProducer.send(event);
|
||||
log.info("send mq:{}", JSON.toJSONString(event));
|
||||
}
|
||||
|
||||
public void sendBatch(List<Event> events){
|
||||
|
||||
@ -13,6 +13,7 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo;
|
||||
import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest;
|
||||
import cn.axzo.im.center.api.vo.req.MessageInfo;
|
||||
import cn.axzo.im.center.api.vo.req.SendChatMessageRequest;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
|
||||
import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest;
|
||||
@ -27,19 +28,23 @@ import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult;
|
||||
import cn.axzo.im.center.api.vo.resp.UserAccountResp;
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.entity.AccountRegister;
|
||||
import cn.axzo.im.entity.MessageHistory;
|
||||
import cn.axzo.im.entity.MessageTask;
|
||||
import cn.axzo.im.enums.MessageHistoryStatus;
|
||||
import cn.axzo.im.enums.MessageTaskStatus;
|
||||
import cn.axzo.im.send.handler.CommonSendOneHandler;
|
||||
import cn.axzo.im.service.AccountRegisterService;
|
||||
import cn.axzo.im.service.AccountService;
|
||||
import cn.axzo.im.service.CustomMessageService;
|
||||
import cn.axzo.im.service.MessageHistoryService;
|
||||
import cn.axzo.im.service.MessageTaskService;
|
||||
import cn.axzo.im.service.RobotMsgTemplateService;
|
||||
import cn.axzo.im.service.impl.MessageHistoryServiceImpl;
|
||||
import cn.axzo.im.updatable.UpdatableMessageManager;
|
||||
import cn.axzo.im.updatable.UpdatableMessageQueryService;
|
||||
import cn.axzo.im.updatable.UpdateSupport;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import cn.axzo.pokonyan.exception.Aassert;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.common.collect.Sets;
|
||||
@ -53,10 +58,12 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -68,7 +75,7 @@ import static cn.axzo.im.config.BizResultCode.SEND_IM_ACCOUNT_NOT_FOUND;
|
||||
import static cn.axzo.im.config.BizResultCode.SEND_PERSSON_ERROR;
|
||||
|
||||
/**
|
||||
* IM消息派发相关
|
||||
* IM消息派发相关
|
||||
*
|
||||
* @author zuoqinbo
|
||||
* @version V1.0
|
||||
@ -89,15 +96,17 @@ public class MessageController implements MessageApi {
|
||||
@Autowired
|
||||
private AccountRegisterService accountRegisterService;
|
||||
@Autowired
|
||||
private MessageHistoryService messageHistoryService;
|
||||
private MessageHistoryServiceImpl messageHistoryService;
|
||||
@Autowired
|
||||
private CustomMessageService customMessageService;
|
||||
@Autowired
|
||||
private UpdatableMessageManager updatableMessageManager;
|
||||
@Autowired
|
||||
private UpdateSupport updateSupport;
|
||||
@Autowired
|
||||
private UpdatableMessageQueryService updatableMessageQueryService;
|
||||
@Autowired
|
||||
private TransactionTemplate transactionTemplate;
|
||||
@Autowired
|
||||
private CommonSendOneHandler commonSendOneHandler;
|
||||
|
||||
|
||||
@Override
|
||||
@ -112,7 +121,7 @@ public class MessageController implements MessageApi {
|
||||
return ApiResult.ok(messageRespList);
|
||||
}
|
||||
|
||||
@ExceptionHandler({ RequestNotPermitted.class })
|
||||
@ExceptionHandler({RequestNotPermitted.class})
|
||||
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
|
||||
public ApiResult<String> handleRequestNotPermitted() {
|
||||
return ApiResult.err("服务器资源繁忙,请求被拒绝!");
|
||||
@ -122,6 +131,7 @@ public class MessageController implements MessageApi {
|
||||
/**
|
||||
* 发送消息时只是存储在messageTask中,通过xxlJob或者mq异步去处理
|
||||
* 因为:1、为了提高接口响应性能。2、第三方接口有限流控制,防止被限流后阻塞业务
|
||||
*
|
||||
* @param sendMessageParam 发送消息请求参数
|
||||
* @return
|
||||
*/
|
||||
@ -184,6 +194,7 @@ public class MessageController implements MessageApi {
|
||||
if (sender.getAppType() != null) {
|
||||
sendImAccount = accountService.registerAccountIfAbsent(
|
||||
sender.getPersonId(), sender.getOuId(), sender.getAppType());
|
||||
BizAssertions.assertNotNull(sendImAccount, "创建账号失败");
|
||||
} else {
|
||||
sendImAccount = check(request);
|
||||
}
|
||||
@ -198,8 +209,17 @@ public class MessageController implements MessageApi {
|
||||
.senderPersonId(request.determineSenderPersonId())
|
||||
.build();
|
||||
Date now = new Date();
|
||||
List<MessageTask.ReceivePerson> receivePersons = JSONArray.parseArray(
|
||||
JSONObject.toJSONString(request.uniqueReceivePersons()), MessageTask.ReceivePerson.class);
|
||||
List<MessageTask.ReceivePerson> requestReceivePersons = JSONArray.parseArray(
|
||||
JSONObject.toJSONString(request.receivePersonsOrEmpty()), MessageTask.ReceivePerson.class);
|
||||
List<MessageTask.ReceivePerson> receivePersons = new ArrayList<>(requestReceivePersons);
|
||||
if (CollectionUtils.isNotEmpty(request.getImReceiveAccounts())) {
|
||||
for (String account : request.getImReceiveAccounts()) {
|
||||
ensureImAccountNotBlank(account);
|
||||
receivePersons.add(MessageTask.ReceivePerson.builder()
|
||||
.imAccount(account)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
MessageTask messageTask = messageTaskService.create(MessageTask.builder()
|
||||
.bizId(request.getBizId())
|
||||
.sendImAccount(sendImAccount)
|
||||
@ -217,13 +237,72 @@ public class MessageController implements MessageApi {
|
||||
.build());
|
||||
List<UpdatableMessageSendResult> updatableMessageSendResults = Collections.emptyList();
|
||||
if (request.isUpdatable()) {
|
||||
updatableMessageSendResults = updatableMessageManager.createUpdatableMessage(messageTask, request, receivePersons);
|
||||
updatableMessageSendResults = updatableMessageManager.createUpdatableMessage(
|
||||
messageTask, request, requestReceivePersons, request.imReceiveAccountsOrEmpty());
|
||||
}
|
||||
MessageTaskResp messageTaskResp = toMessageTaskResp(messageTask);
|
||||
messageTaskResp.setUpdatableMessageSendResults(updatableMessageSendResults);
|
||||
return ApiResult.ok(messageTaskResp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Long> sendChatMessage(SendChatMessageRequest request) {
|
||||
log.info("sendChatMessage, request={}", request);
|
||||
PersonAccountAttribute sender = request.getSender();
|
||||
BizAssertions.assertNotNull(sender.getAppType(), "发送人appType不能为空");
|
||||
String sendImAccount = accountService.registerAccountIfAbsent(
|
||||
sender.getPersonId(), sender.getOuId(), sender.getAppType());
|
||||
BizAssertions.assertNotNull(sendImAccount, "创建账号失败");
|
||||
Date now = new Date();
|
||||
List<MessageTask.ReceivePerson> requestReceivePersons = JSONArray.parseArray(
|
||||
JSONObject.toJSONString(request.receivePersonsOrEmpty()), MessageTask.ReceivePerson.class);
|
||||
List<MessageTask.ReceivePerson> receivePersons = new ArrayList<>(requestReceivePersons);
|
||||
if (CollectionUtils.isNotEmpty(request.getImReceiveAccounts())) {
|
||||
for (String account : request.getImReceiveAccounts()) {
|
||||
ensureImAccountNotBlank(account);
|
||||
receivePersons.add(MessageTask.ReceivePerson.builder()
|
||||
.imAccount(account)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
int receiverSize = request.receivePersonsOrEmpty().size() + request.imReceiveAccountsOrEmpty().size();
|
||||
BizAssertions.assertTrue(receiverSize >= 1, "接收人不能为空");
|
||||
boolean syncSend = request.isTrySyncSend() && receiverSize == 1;
|
||||
MessageTask.BizData bizData = MessageTask.BizData.builder()
|
||||
.messageBody(JSON.toJSONString(request.getMessageBody()))
|
||||
.isSenderRobot(false)
|
||||
.syncSend(syncSend)
|
||||
.senderPersonId(request.determineSenderPersonId())
|
||||
.nimMessageType(request.getMessageType())
|
||||
.build();
|
||||
Long taskId = transactionTemplate.execute(unused -> {
|
||||
MessageTask task = messageTaskService.create(MessageTask.builder()
|
||||
.bizId(request.getBizId())
|
||||
.sendImAccount(sendImAccount)
|
||||
.receivePersons(receivePersons)
|
||||
.status(MessageTaskStatus.PENDING)
|
||||
.bizData(bizData)
|
||||
.planStartTime(now)
|
||||
.createAt(now)
|
||||
.sendPriority(SendPriority.CHAT_MESSAGE.getPriority())
|
||||
.apiChannel(ApiChannel.COMMON_MESSAGE)
|
||||
.build());
|
||||
if (syncSend) {
|
||||
task = messageTaskService.getById(task.getId());
|
||||
List<Long> historyIds = messageTaskService.createMessageHistory(task);
|
||||
MessageHistory history = messageHistoryService.getById(historyIds.get(0));
|
||||
commonSendOneHandler.updateSyncSendState(history, commonSendOneHandler.send(history));
|
||||
}
|
||||
return task.getId();
|
||||
});
|
||||
return ApiResult.ok(taskId);
|
||||
}
|
||||
|
||||
private void ensureImAccountNotBlank(String imAccount) {
|
||||
BizAssertions.assertTrue(StringUtils.isNotBlank(imAccount), "接收人IM账号不能为空");
|
||||
BizAssertions.assertFalse("null".equalsIgnoreCase(imAccount), "接收人IM账号不能为空");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<MessageUpdateResponse> updateMessage(UpdateMessageRequest request) {
|
||||
log.info("updateMessage, request={}", request);
|
||||
|
||||
@ -1,12 +1,21 @@
|
||||
package cn.axzo.im.controller;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.GroupAddMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupCreateRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupDismissRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupRemoveMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import cn.axzo.im.channel.netease.dto.DismissGroupRequest;
|
||||
import cn.axzo.im.channel.netease.dto.GetGroupInfoRequest;
|
||||
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 cn.axzo.im.channel.netease.dto.NimGroupDismissRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryEventRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimQueryMessageRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
|
||||
import cn.axzo.im.dao.repository.GroupDao;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import cn.axzo.im.group.GroupManager;
|
||||
import cn.axzo.im.group.GroupMemberSyncer;
|
||||
import cn.axzo.im.job.CreateMessageHistoryJob;
|
||||
import cn.axzo.im.job.ExpungeImTaskJob;
|
||||
import cn.axzo.im.job.RevokeAllMessagesJob;
|
||||
@ -34,19 +43,22 @@ public class PrivateController {
|
||||
private final CreateMessageHistoryJob createMessageHistoryJob;
|
||||
private final MessageController messageController;
|
||||
private final ExpungeImTaskJob expungeImTaskJob;
|
||||
private final GroupManager groupManager;
|
||||
private final GroupDao groupDao;
|
||||
private final GroupMemberSyncer groupMemberSyncer;
|
||||
|
||||
@PostMapping("/private/revoke")
|
||||
public Object revoke(@Valid @RequestBody RevokeMessageRequest request) {
|
||||
public Object revoke(@Valid @RequestBody NimRevokeMessageRequest request) {
|
||||
return nimClient.revoke(request);
|
||||
}
|
||||
|
||||
@PostMapping("/private/queryEvents")
|
||||
public Object queryEvents(@Valid @RequestBody QueryEventRequest request) {
|
||||
public Object queryEvents(@Valid @RequestBody NimQueryEventRequest request) {
|
||||
return nimClient.queryEvents(request);
|
||||
}
|
||||
|
||||
@PostMapping("/private/queryMessages")
|
||||
public Object queryMessages(@Valid @RequestBody QueryMessageRequest request) {
|
||||
public Object queryMessages(@Valid @RequestBody NimQueryMessageRequest request) {
|
||||
return nimClient.queryMessages(request);
|
||||
}
|
||||
|
||||
@ -76,14 +88,51 @@ public class PrivateController {
|
||||
return CommonResponse.success(count);
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/createGroup")
|
||||
public Object createGroup(@Valid @RequestBody GroupCreateRequest request) {
|
||||
return CommonResponse.success(groupManager.createGroup(request));
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/dismissGroup")
|
||||
public Object dismissGroup(@Valid @RequestBody DismissGroupRequest request) {
|
||||
return CommonResponse.success(nimClient.dismissGroup(request));
|
||||
public Object dismissGroup(@Valid @RequestBody GroupDismissRequest request) {
|
||||
groupManager.dismissGroup(request);
|
||||
return CommonResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/dismissGroupDirectly")
|
||||
public Object dismissGroupDirectly(@Valid @RequestBody GroupDismissRequest request) {
|
||||
NimGroupGetInfoRequest req1 = new NimGroupGetInfoRequest();
|
||||
req1.setTid(request.getTid());
|
||||
NimGroupGetInfoResponse res1 = nimClient.getGroupInfo(req1);
|
||||
NimGroupDismissRequest req2 = new NimGroupDismissRequest();
|
||||
req2.setOwner(res1.getTinfo().getOwner().getAccid());
|
||||
req2.setTid(request.getTid());
|
||||
return CommonResponse.success(nimClient.dismissGroup(req2));
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/addMembers")
|
||||
public Object addMembers(@Valid @RequestBody GroupAddMembersRequest request) {
|
||||
groupManager.addMembers(request);
|
||||
return CommonResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/removeMembers")
|
||||
public Object removeMembers(@Valid @RequestBody GroupRemoveMembersRequest request) {
|
||||
groupManager.removeMembers(request);
|
||||
return CommonResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/getGroupInfo")
|
||||
public Object getGroupInfo(@Valid @RequestBody GetGroupInfoRequest request) {
|
||||
public Object getGroupInfo(@Valid @RequestBody NimGroupGetInfoRequest request) {
|
||||
return CommonResponse.success(nimClient.getGroupInfo(request));
|
||||
}
|
||||
|
||||
@PostMapping("/private/group/syncGroupMembers")
|
||||
public Object syncGroupMembers(@RequestParam("tid") Long tid) {
|
||||
Group group = groupDao.findByTid(tid, false).orElse(null);
|
||||
if (group == null) return "group not found";
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
return CommonResponse.success("done");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package cn.axzo.im.dao.mapper;
|
||||
|
||||
import cn.axzo.im.entity.GroupLog;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface GroupLogMapper extends BaseMapper<GroupLog> {
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package cn.axzo.im.dao.mapper;
|
||||
|
||||
import cn.axzo.im.entity.Group;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface GroupMapper extends BaseMapper<Group> {
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package cn.axzo.im.dao.mapper;
|
||||
|
||||
import cn.axzo.im.entity.GroupMember;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface GroupMapperMapper extends BaseMapper<GroupMember> {
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package cn.axzo.im.dao.mapper;
|
||||
|
||||
import cn.axzo.im.entity.GroupMessage;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface GroupMessageMapper extends BaseMapper<GroupMessage> {
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package cn.axzo.im.dao.repository;
|
||||
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import cn.axzo.im.dao.mapper.GroupMapper;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Repository("groupDao")
|
||||
public class GroupDao extends ServiceImpl<GroupMapper, Group> {
|
||||
|
||||
public void updateMembersCount(Long tid, Integer count) {
|
||||
lambdaUpdate()
|
||||
.eq(Group::getTid, tid)
|
||||
.set(Group::getMemberCount, count)
|
||||
.update();
|
||||
}
|
||||
|
||||
public void updateTid(Long id, Long tid) {
|
||||
lambdaUpdate()
|
||||
.eq(Group::getId, id)
|
||||
.set(Group::getTid, tid)
|
||||
.update();
|
||||
}
|
||||
|
||||
public Optional<Group> findByTid(Long tid, boolean forUpdate) {
|
||||
Group group = lambdaQuery()
|
||||
.eq(Group::getTid, tid)
|
||||
.last(forUpdate, "FOR UPDATE")
|
||||
.one();
|
||||
return Optional.ofNullable(group);
|
||||
}
|
||||
|
||||
public void setDismissed(Long tid) {
|
||||
lambdaUpdate()
|
||||
.eq(Group::getTid, tid)
|
||||
.set(Group::getIsDismissed, YesOrNo.YES)
|
||||
.set(Group::getDismissedAt, new Date())
|
||||
.update();
|
||||
}
|
||||
|
||||
public Optional<Group> findByBizCode(String bizCode, GroupType groupType, boolean forUpdate) {
|
||||
return lambdaQuery()
|
||||
.eq(Group::getBizCode, bizCode)
|
||||
.eq(Group::getType, groupType)
|
||||
.eq(Group::getIsDismissed, YesOrNo.NO)
|
||||
.last(forUpdate, "FOR UPDATE")
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.axzo.im.dao.repository;
|
||||
|
||||
import cn.axzo.im.dao.mapper.GroupLogMapper;
|
||||
import cn.axzo.im.entity.GroupLog;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Repository("groupLogDao")
|
||||
public class GroupLogDao extends ServiceImpl<GroupLogMapper, GroupLog> {
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package cn.axzo.im.dao.repository;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.GroupMemberType;
|
||||
import cn.axzo.im.dao.mapper.GroupMapperMapper;
|
||||
import cn.axzo.im.entity.GroupMember;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Repository("groupMemberDao")
|
||||
public class GroupMemberDao extends ServiceImpl<GroupMapperMapper, GroupMember> {
|
||||
|
||||
public Optional<GroupMember> findByAccount(Long tid, String imAccount) {
|
||||
return lambdaQuery()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.eq(GroupMember::getImAccount, imAccount)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
public void deleteAccounts(Long tid) {
|
||||
lambdaUpdate()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.remove();
|
||||
}
|
||||
|
||||
public Set<PersonAccountAttribute> getAsPersons(Long tid) {
|
||||
return getByTid(tid).stream()
|
||||
.map(GroupMember::asPerson)
|
||||
.collect(toSet());
|
||||
}
|
||||
|
||||
public List<GroupMember> getByTid(Long tid) {
|
||||
return lambdaQuery()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.list();
|
||||
}
|
||||
|
||||
public void deleteByPersons(Long tid, Collection<PersonAccountAttribute> persons) {
|
||||
if (CollectionUtils.isEmpty(persons))
|
||||
return;
|
||||
lambdaUpdate()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.nested(wrapper -> {
|
||||
for (PersonAccountAttribute person : persons) {
|
||||
wrapper.or()
|
||||
.eq(GroupMember::getPersonId, Long.parseLong(person.getPersonId()))
|
||||
.eq(GroupMember::getPersonOuId, person.ouIdOrDefault())
|
||||
.eq(GroupMember::getAppType, person.getAppType());
|
||||
}
|
||||
})
|
||||
.remove();
|
||||
}
|
||||
|
||||
public Optional<GroupMember> findByPerson(Long tid, PersonAccountAttribute person) {
|
||||
List<GroupMember> members = getByPersons(tid, Collections.singletonList(person));
|
||||
return CollectionUtils.isEmpty(members) ? Optional.empty() : Optional.of(members.get(0));
|
||||
}
|
||||
|
||||
public List<GroupMember> getByPersons(
|
||||
Long tid, Collection<PersonAccountAttribute> persons) {
|
||||
if (CollectionUtils.isEmpty(persons))
|
||||
return Collections.emptyList();
|
||||
return lambdaQuery()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.nested(wrapper -> {
|
||||
for (PersonAccountAttribute person : persons) {
|
||||
wrapper.or()
|
||||
.eq(GroupMember::getPersonId, Long.parseLong(person.getPersonId()))
|
||||
.eq(GroupMember::getPersonOuId, person.ouIdOrDefault())
|
||||
.eq(GroupMember::getAppType, person.getAppType());
|
||||
}
|
||||
})
|
||||
.list();
|
||||
}
|
||||
|
||||
public GroupMember getOwner(Long tid) {
|
||||
return lambdaQuery()
|
||||
.eq(GroupMember::getTid, tid)
|
||||
.eq(GroupMember::getMemberType, GroupMemberType.OWNER)
|
||||
.one();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.axzo.im.dao.repository;
|
||||
|
||||
import cn.axzo.im.dao.mapper.GroupMessageMapper;
|
||||
import cn.axzo.im.entity.GroupMessage;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Repository("groupMessageDao")
|
||||
public class GroupMessageDao extends ServiceImpl<GroupMessageMapper, GroupMessage> {
|
||||
|
||||
public List<GroupMessage> reloadByNimId(Collection<GroupMessage> messages) {
|
||||
if (CollectionUtils.isEmpty(messages))
|
||||
return Collections.emptyList();
|
||||
return lambdaQuery()
|
||||
.nested(wrapper -> {
|
||||
for (GroupMessage message : messages) {
|
||||
wrapper.or().nested(nested -> nested
|
||||
.eq(GroupMessage::getTid, message.getTid())
|
||||
.eq(GroupMessage::getMessageId, message.getMessageId()));
|
||||
}
|
||||
})
|
||||
.list();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.axzo.im.entity;
|
||||
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@ -100,4 +101,10 @@ public class AccountRegister implements Serializable {
|
||||
|
||||
@TableField
|
||||
private Date updateAt;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
66
im-center-server/src/main/java/cn/axzo/im/entity/Group.java
Normal file
66
im-center-server/src/main/java/cn/axzo/im/entity/Group.java
Normal file
@ -0,0 +1,66 @@
|
||||
package cn.axzo.im.entity;
|
||||
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@TableName(value = "im_group", autoResultMap = true)
|
||||
public class Group {
|
||||
|
||||
public static final String CUSTOM_GROUP_TYPE = "groupType";
|
||||
public static final String CUSTOM_BIZ_CODE = "bizCode";
|
||||
public static final String CUSTOM_BIZ_GROUP_INFO = "bizGroupInfo";
|
||||
|
||||
private Long id;
|
||||
private Long tid;
|
||||
private String name;
|
||||
private String bizCode;
|
||||
private GroupType type;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private Map<String, Object> bizGroupInfo;
|
||||
private String avatar;
|
||||
private Long memberCount;
|
||||
private Long memberLimit;
|
||||
private String ownerAccount;
|
||||
private Long ownerPersonId;
|
||||
private Long createPersonId;
|
||||
private Date dismissedAt;
|
||||
private Long isDelete;
|
||||
private YesOrNo isDismissed;
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private RecordExt recordExt;
|
||||
|
||||
public boolean addMoreMembers(int memberCount) {
|
||||
return memberLimit <= 0 || memberCount <= memberLimit;
|
||||
}
|
||||
|
||||
public boolean isDismissed() {
|
||||
return getIsDismissed() == YesOrNo.YES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class RecordExt {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package cn.axzo.im.entity;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@TableName(value = "im_group_log", autoResultMap = true)
|
||||
public class GroupLog {
|
||||
private Long id;
|
||||
private String context;
|
||||
private Long tid;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private JSONObject content;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private Object request;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private RecordExt recordExt;
|
||||
private Long isDelete;
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class RecordExt {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package cn.axzo.im.entity;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.GroupMemberType;
|
||||
import cn.axzo.im.center.common.enums.GroupType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@TableName(value = "im_group_member", autoResultMap = true)
|
||||
public class GroupMember {
|
||||
private Long id;
|
||||
private Long tid;
|
||||
private GroupType groupType;
|
||||
private String imAccount;
|
||||
private Long personId;
|
||||
private Long personOuId;
|
||||
private AppTypeEnum appType;
|
||||
private GroupMemberType memberType;
|
||||
private YesOrNo isRobot;
|
||||
private Long isDelete;
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
|
||||
public PersonAccountAttribute asPerson() {
|
||||
PersonAccountAttribute person = new PersonAccountAttribute();
|
||||
person.setPersonId(personId + "");
|
||||
person.setOuId(personOuId);
|
||||
person.setAppType(appType);
|
||||
return person;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package cn.axzo.im.entity;
|
||||
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.NimFromClientType;
|
||||
import cn.axzo.im.center.common.enums.NimMessageType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@TableName(value = "im_group_message", autoResultMap = true)
|
||||
public class GroupMessage {
|
||||
private Long id;
|
||||
private Long tid;
|
||||
private String fromAccount;
|
||||
private Long fromPersonId;
|
||||
private Long fromPersonOuId;
|
||||
private AppTypeEnum fromPersonAppType;
|
||||
private YesOrNo isFromRobot;
|
||||
private String messageId;
|
||||
private NimMessageType messageType;
|
||||
private NimFromClientType fromClientType;
|
||||
private String messageIdClient;
|
||||
private Long sendTime;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private JSONObject body;
|
||||
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||
private RecordExt recordExt;
|
||||
private Long isDelete;
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
|
||||
public RecordExt getOrCreateRecordExt() {
|
||||
if (recordExt == null)
|
||||
recordExt = new RecordExt();
|
||||
return recordExt;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class RecordExt {
|
||||
}
|
||||
}
|
||||
@ -35,4 +35,7 @@ public class HistoryRecordExt {
|
||||
private Long updateRetryCount;
|
||||
private Map<String, String> initMessageExt;
|
||||
private Long workspaceId;
|
||||
|
||||
private Integer nimMessageType;
|
||||
private boolean syncSend;
|
||||
}
|
||||
@ -14,6 +14,7 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
@ -160,6 +161,8 @@ public class MessageHistory implements Serializable, NimMessageHistory {
|
||||
public Optional<String> determineBatchNo() {
|
||||
if (isUpdatableMessage())
|
||||
return Optional.empty();
|
||||
if (isSendToGroup())
|
||||
return Optional.empty();
|
||||
String batchNo = this.batchNo;
|
||||
// 兼容在途数据
|
||||
if (StringUtils.isBlank(batchNo) && imMessageTaskId != null)
|
||||
@ -168,4 +171,9 @@ public class MessageHistory implements Serializable, NimMessageHistory {
|
||||
batchNo = null;
|
||||
return Optional.ofNullable(batchNo);
|
||||
}
|
||||
|
||||
public boolean isSendToGroup() {
|
||||
return AppTypeEnum.NONE.is(getAppType())
|
||||
&& NumberUtils.isDigits(getToAccount());
|
||||
}
|
||||
}
|
||||
@ -6,9 +6,11 @@ import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.BizTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.NimMessageType;
|
||||
import cn.axzo.im.center.common.enums.TemplatedMsgType;
|
||||
import cn.axzo.im.config.BaseListTypeHandler;
|
||||
import cn.axzo.im.enums.MessageTaskStatus;
|
||||
import cn.axzo.im.utils.UUIDUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@ -121,6 +123,8 @@ public class MessageTask {
|
||||
@AllArgsConstructor
|
||||
public static class BizData {
|
||||
|
||||
private final String requestNo = UUIDUtil.uuidString();
|
||||
|
||||
private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE;
|
||||
|
||||
private Boolean isOpMessage = false;
|
||||
@ -132,6 +136,11 @@ public class MessageTask {
|
||||
*/
|
||||
private String msgTemplateContent;
|
||||
|
||||
/**
|
||||
* 直接发送的消息内容
|
||||
*/
|
||||
private String messageBody;
|
||||
|
||||
/**
|
||||
* 网易云信-自定义消息使用
|
||||
*/
|
||||
@ -164,6 +173,10 @@ public class MessageTask {
|
||||
|
||||
private Boolean isSenderRobot;
|
||||
|
||||
private NimMessageType nimMessageType;
|
||||
|
||||
private boolean syncSend;
|
||||
|
||||
public boolean determineIsSenderRobot() {
|
||||
return isSenderRobot != null && isSenderRobot;
|
||||
}
|
||||
|
||||
@ -113,6 +113,7 @@ public class UpdatableMessage implements MessageUpdateInfo {
|
||||
@Setter
|
||||
public static class RecordExt {
|
||||
private Long receiverWorkspaceId;
|
||||
private boolean isSendByImAccountDirectly;
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,6 +9,12 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service("dingDingMsgApiGateway")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@ -16,10 +22,22 @@ public class DingDingMsgApiGateway {
|
||||
|
||||
private final DingDingMsgApi dingDingMsgApi;
|
||||
|
||||
private final ExecutorService executor = new ThreadPoolExecutor(2, 2,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<>(10));
|
||||
|
||||
public void sendMsg(DingTalkMsgTypeEnum msgType, String title, String text, String dingDingScene) {
|
||||
try {
|
||||
executor.execute(() -> sendMsgImpl(msgType, title, text, dingDingScene));
|
||||
} catch (Exception e) {
|
||||
log.warn("DingDingMsgApiGateway-sendMsg Exception, msgType:{}, title:{}, text:{}, dingDingScene:{}", msgType, title, text, dingDingScene, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
public void sendMsg(DingTalkMsgTypeEnum msgType, String title, String text, String dingDingScene) {
|
||||
private void sendMsgImpl(DingTalkMsgTypeEnum msgType, String title, String text, String dingDingScene) {
|
||||
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
|
||||
req.setMsgType(msgType);
|
||||
req.setDingDingJson(new SampleMarkdown(title, text).toJson());
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.im.gateway;
|
||||
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import cn.axzo.maokai.api.client.OrganizationalUnitApi;
|
||||
import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery;
|
||||
import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OrgGateway {
|
||||
|
||||
private final OrganizationalUnitApi organizationalUnitApi;
|
||||
|
||||
public List<OrganizationalUnitVO> getUnits(List<Long> ouIdList) {
|
||||
if (ouIdList.isEmpty())
|
||||
return Collections.emptyList();
|
||||
OrganizationalUnitQuery req = new OrganizationalUnitQuery();
|
||||
req.setPageSize((long) ouIdList.size());
|
||||
req.setUnitIds(Lists.newArrayList(Sets.newHashSet(ouIdList)));
|
||||
return BizAssertions.assertResponse(organizationalUnitApi.list(req));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package cn.axzo.im.gateway.domain;
|
||||
|
||||
import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class OrgUnits {
|
||||
|
||||
private final Collection<OrganizationalUnitVO> units;
|
||||
|
||||
public static OrgUnits wrap(Collection<OrganizationalUnitVO> units) {
|
||||
return new OrgUnits(units);
|
||||
}
|
||||
|
||||
public Optional<OrganizationalUnitVO> findUnit(Long ouId) {
|
||||
return units.stream().filter(unit -> unit.getId().equals(ouId)).findFirst();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package cn.axzo.im.gateway.domain;
|
||||
|
||||
import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class PersonProfiles {
|
||||
|
||||
private final List<PersonProfileDto> profiles;
|
||||
|
||||
public static PersonProfiles wrap(List<PersonProfileDto> profiles) {
|
||||
return new PersonProfiles(profiles);
|
||||
}
|
||||
|
||||
public Optional<PersonProfileDto> findByUserId(Long personId) {
|
||||
return profiles.stream()
|
||||
.filter(p -> p.getId().equals(personId))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package cn.axzo.im.group;
|
||||
|
||||
import cn.axzo.basics.common.BeanMapper;
|
||||
import cn.axzo.framework.rocketmq.Event;
|
||||
import cn.axzo.im.center.api.enums.MqEventType;
|
||||
import cn.axzo.im.center.api.vo.group.GroupInfo;
|
||||
import cn.axzo.im.center.api.vo.group.GroupMemberInfo;
|
||||
import cn.axzo.im.center.api.vo.mq.GroupChangedMessage;
|
||||
import cn.axzo.im.center.api.vo.mq.GroupMembersChangeMessage;
|
||||
import cn.axzo.im.config.MqProducer;
|
||||
import cn.axzo.im.dao.repository.GroupDao;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import cn.axzo.im.entity.GroupMember;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
class GroupBroadcaster {
|
||||
|
||||
private final MqProducer mqProducer;
|
||||
private final GroupDao groupDao;
|
||||
|
||||
void fireGroupChanged(Group group, MqEventType eventType) {
|
||||
Group effectiveGroup = groupDao.getById(group.getId());
|
||||
GroupChangedMessage message = new GroupChangedMessage();
|
||||
message.setGroup(BeanMapper.copyBean(effectiveGroup, GroupInfo.class));
|
||||
Event event = Event.builder()
|
||||
.targetId(effectiveGroup.getTid() + "")
|
||||
.targetType(eventType.getModel())
|
||||
.eventCode(eventType.getEventCode())
|
||||
.shardingKey(effectiveGroup.getTid() + "")
|
||||
.data(message)
|
||||
.build();
|
||||
mqProducer.send(event);
|
||||
}
|
||||
|
||||
void fireMembersChanged(Group group,
|
||||
Collection<GroupMember> members,
|
||||
MqEventType eventType) {
|
||||
if (members.isEmpty()) return;
|
||||
Group effectiveGroup = groupDao.getById(group.getId());
|
||||
for (GroupMember account : members) {
|
||||
GroupMembersChangeMessage message = new GroupMembersChangeMessage();
|
||||
message.setGroup(BeanMapper.copyBean(effectiveGroup, GroupInfo.class));
|
||||
message.setMember(BeanMapper.copyBean(account, GroupMemberInfo.class));
|
||||
Event event = Event.builder()
|
||||
.targetId(effectiveGroup.getTid() + "")
|
||||
.targetType(eventType.getModel())
|
||||
.eventCode(eventType.getEventCode())
|
||||
.shardingKey(effectiveGroup.getTid() + "")
|
||||
.data(message)
|
||||
.build();
|
||||
mqProducer.send(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
package cn.axzo.im.group;
|
||||
|
||||
import cn.axzo.im.center.api.enums.MqEventType;
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.api.vo.req.GroupAddMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupCreateRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupDismissRequest;
|
||||
import cn.axzo.im.center.api.vo.req.GroupRemoveMembersRequest;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupAddMembersResponse;
|
||||
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupAddMembersRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupAddMembersResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupCreateRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupCreateResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupDismissRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupDismissResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupRemoveMembersRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupRemoveMembersResponse;
|
||||
import cn.axzo.im.dao.repository.GroupDao;
|
||||
import cn.axzo.im.dao.repository.GroupMemberDao;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import cn.axzo.im.entity.GroupMember;
|
||||
import cn.axzo.im.group.support.GroupRateLimiter;
|
||||
import cn.axzo.im.service.AccountService;
|
||||
import cn.axzo.im.service.domain.ImAccounts;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import cn.axzo.im.utils.Notification;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GroupManager {
|
||||
|
||||
private final NimClient nimClient;
|
||||
private final GroupDao groupDao;
|
||||
private final GroupMemberDao groupMemberDao;
|
||||
private final GroupSupport groupSupport;
|
||||
private final AccountService accountService;
|
||||
private final GroupBroadcaster groupBroadcaster;
|
||||
private final GroupMemberSyncer groupMemberSyncer;
|
||||
private final GroupRateLimiter rateLimiter;
|
||||
private final Notification notification;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
|
||||
public GroupCreateResponse createGroup(GroupCreateRequest request) {
|
||||
BizAssertions.assertTrue(request.getPeople().size() > 1, "群成员数量(含群主)不能少于2");
|
||||
groupSupport.log(0L, "create-group:preparing", request);
|
||||
// DON'T delete this line
|
||||
accountService.maybeCreateImAccounts(request.getPeople());
|
||||
return transactionTemplate.execute(unused -> {
|
||||
Group savedGroup = groupDao
|
||||
.findByBizCode(request.getBizCode(), request.getGroupType(), true)
|
||||
.orElse(null);
|
||||
BizAssertions.assertTrue(savedGroup == null, String.format("群已经存在: %s", request.getName()));
|
||||
ImAccounts imAccounts = accountService.getAccountsByPersons(request.getPeople());
|
||||
String owner = imAccounts.findAccount(request.getOwner()).orElse(null);
|
||||
BizAssertions.assertNotNull(owner, "群主没有IM账号, 无法创建群. {}", request.getOwner());
|
||||
Group group = groupSupport.buildNewGroup(request, imAccounts);
|
||||
BizAssertions.assertTrue(group.addMoreMembers(
|
||||
request.getPeople().size()), "无法创建群, 群成员数量超过上限" + group.getMemberLimit());
|
||||
groupDao.save(group);
|
||||
NimGroupCreateRequest nimRequest = groupSupport
|
||||
.buildNimCreateGroupRequest(request, imAccounts);
|
||||
rateLimiter.requireCreateGroup();
|
||||
NimGroupCreateResponse nimResponse = nimClient.createGroup(nimRequest);
|
||||
log.info("创建群, request={}, response={}", nimRequest, nimResponse);
|
||||
BizAssertions.assertTrue(nimResponse.isSuccess(), "创建群失败: {}", nimResponse.getDesc());
|
||||
groupDao.updateTid(group.getId(), nimResponse.getTid());
|
||||
group = groupDao.getById(group.getId());
|
||||
groupSupport.log(group.getTid(), "create-group", request);
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
// 同步完成员后再发消息, 因为接收方可能会查询群成员
|
||||
groupBroadcaster.fireGroupChanged(group, MqEventType.GROUP_CREATED);
|
||||
groupBroadcaster.fireMembersChanged(group,
|
||||
groupMemberDao.getByTid(nimResponse.getTid()),
|
||||
MqEventType.GROUP_ADD_MEMBERS);
|
||||
GroupCreateResponse response = new GroupCreateResponse();
|
||||
response.setTid(nimResponse.getTid());
|
||||
response.setAccountsNotFound(getAccountNotFoundPersons(
|
||||
"创建群", imAccounts, group, request.getMembers()));
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void dismissGroup(GroupDismissRequest request) {
|
||||
Group group = getGroupForUpdateOrThrow(request.getTid());
|
||||
groupSupport.log(group.getTid(), "dismiss-group", request);
|
||||
if (group.isDismissed()) return;
|
||||
NimGroupDismissRequest nimRequest = groupSupport
|
||||
.buildNimDismissGroupRequest(group.getOwnerAccount(), group);
|
||||
rateLimiter.requireDismissGroup();
|
||||
NimGroupDismissResponse nimResponse = nimClient.dismissGroup(nimRequest);
|
||||
log.info("解散群, request={}, response={}", nimRequest, nimResponse);
|
||||
if (!nimResponse.isGroupNotFoundError())
|
||||
BizAssertions.assertTrue(nimResponse.isSuccess(), "解散群失败: {}", nimResponse.getDesc());
|
||||
groupDao.setDismissed(group.getTid());
|
||||
group = groupDao.getById(group.getId());
|
||||
groupBroadcaster.fireGroupChanged(group, MqEventType.GROUP_DISMISSED);
|
||||
}
|
||||
|
||||
public GroupAddMembersResponse addMembers(GroupAddMembersRequest request) {
|
||||
// DON'T delete this line
|
||||
accountService.maybeCreateImAccounts(request.getMembers());
|
||||
return transactionTemplate.execute(unused -> {
|
||||
Group group = getGroupForUpdateOrThrow(request.getTid());
|
||||
groupSupport.log(group.getTid(), "add-members", request);
|
||||
BizAssertions.assertFalse(group.isDismissed(), "群已经解散");
|
||||
// sync members 1
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
// prepare add members
|
||||
Set<PersonAccountAttribute> prePersons = groupMemberDao.getAsPersons(group.getTid());
|
||||
Set<PersonAccountAttribute> addPersons = request.getMembers().stream()
|
||||
.filter(member -> !prePersons.contains(member))
|
||||
.collect(toSet());
|
||||
if (addPersons.isEmpty())
|
||||
return new GroupAddMembersResponse();
|
||||
BizAssertions.assertTrue(group.addMoreMembers(prePersons.size() + addPersons.size()),
|
||||
"群聊人数上限{}人, 请删除部分已选人员", group.getMemberLimit());
|
||||
ImAccounts imAccounts = accountService.getAccountsByPersons(addPersons);
|
||||
if (imAccounts.isAccountEmpty()) {
|
||||
notification.send("添加群成员[{},{}], 有效群成员IM账号列表为空. 请求成员信息: {}",
|
||||
group.getTid(), group.getName(), JSON.toJSONString(request.getMembers()));
|
||||
return new GroupAddMembersResponse();
|
||||
}
|
||||
GroupMember inviter = groupMemberDao
|
||||
.findByAccount(group.getTid(), request.getInviter())
|
||||
.orElse(null);
|
||||
BizAssertions.assertNotNull(inviter, "邀请者不在群中");
|
||||
//noinspection DataFlowIssue
|
||||
NimGroupAddMembersRequest nimRequest = groupSupport
|
||||
.buildAddMembersRequest(group, inviter.getImAccount(), imAccounts);
|
||||
// add members
|
||||
rateLimiter.requireAddMember();
|
||||
NimGroupAddMembersResponse nimResponse = nimClient.addGroupMembers(nimRequest);
|
||||
log.info("添加群成员, request={}, response={}", nimRequest, nimResponse);
|
||||
BizAssertions.assertTrue(nimResponse.isSuccess(), "添加群成员失败: {}", nimResponse.getDesc());
|
||||
// sync members 2
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
groupBroadcaster.fireMembersChanged(group,
|
||||
groupMemberDao.getByPersons(group.getTid(), addPersons),
|
||||
MqEventType.GROUP_ADD_MEMBERS);
|
||||
GroupAddMembersResponse response = new GroupAddMembersResponse();
|
||||
response.setAccountsNotFound(getAccountNotFoundPersons(
|
||||
"添加群成员", imAccounts, group, addPersons));
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeMembers(GroupRemoveMembersRequest request) {
|
||||
Group group = getGroupForUpdateOrThrow(request.getTid());
|
||||
BizAssertions.assertFalse(group.isDismissed(), "群已经解散");
|
||||
groupSupport.log(group.getTid(), "remove-members", request);
|
||||
ImAccounts imAccounts = accountService.getAccountsByPersons(request.getMembers());
|
||||
if (imAccounts.isAccountEmpty()) {
|
||||
notification.send("移除群成员[{},{}], 有效群成员IM账号列表为空. 请求成员信息: {}",
|
||||
group.getTid(), group.getName(), JSON.toJSONString(request.getMembers()));
|
||||
return;
|
||||
}
|
||||
// sync members 1
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
Set<PersonAccountAttribute> groupPersons = groupMemberDao
|
||||
.getAsPersons(group.getTid());
|
||||
Set<PersonAccountAttribute> removePersons = request.getMembers().stream()
|
||||
.filter(groupPersons::contains)
|
||||
.collect(toSet());
|
||||
if (CollectionUtils.isEmpty(removePersons)) {
|
||||
log.info("移除群成员, 成员不在群中, request={}", request);
|
||||
return;
|
||||
}
|
||||
BizAssertions.assertTrue(
|
||||
groupPersons.size() - removePersons.size() >= 1, "无法移除, 群聊人数不能少于1人");
|
||||
PersonAccountAttribute owner = groupMemberDao.getOwner(request.getTid()).asPerson();
|
||||
BizAssertions.assertFalse(removePersons.contains(owner), "不能移除群主");
|
||||
NimGroupRemoveMembersRequest nimRequest = groupSupport
|
||||
.buildRemoveMembersRequest(group, group.getOwnerAccount(), imAccounts);
|
||||
rateLimiter.requireRemoveMember();
|
||||
NimGroupRemoveMembersResponse nimResponse = nimClient.removeGroupMembers(nimRequest);
|
||||
log.info("移除群成员, request={}, response={}", nimRequest, nimResponse);
|
||||
BizAssertions.assertTrue(nimResponse.isSuccess(), "移除群成员失败: {}", nimResponse.getDesc());
|
||||
// 同步前进行查询, 不然查询不到了
|
||||
List<GroupMember> removeMembers = groupMemberDao
|
||||
.getByPersons(group.getTid(), removePersons);
|
||||
// sync members 2
|
||||
groupMemberSyncer.syncMembers(group);
|
||||
groupBroadcaster.fireMembersChanged(group,
|
||||
removeMembers, MqEventType.GROUP_REMOVE_MEMBERS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Group getGroupForUpdateOrThrow(Long tid) {
|
||||
Group group = groupDao.findByTid(tid, true).orElse(null);
|
||||
BizAssertions.assertNotNull(group, "群不存在: {}", tid);
|
||||
return group;
|
||||
}
|
||||
|
||||
public Set<PersonAccountAttribute> getAccountNotFoundPersons(
|
||||
String operation, ImAccounts accounts, Group group,
|
||||
Set<PersonAccountAttribute> persons) {
|
||||
if (org.apache.commons.collections.CollectionUtils.isEmpty(persons))
|
||||
return Collections.emptySet();
|
||||
Set<PersonAccountAttribute> notFound = persons.stream()
|
||||
.filter(person -> !accounts.findAccount(person).isPresent())
|
||||
.collect(toSet());
|
||||
if (!notFound.isEmpty())
|
||||
this.notification.send("{}[{},{}], IM账号不存在列表: {}", operation,
|
||||
group.getTid(), group.getName(),
|
||||
cn.axzo.framework.jackson.utility.JSON.toJSONString(notFound));
|
||||
return notFound;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package cn.axzo.im.group;
|
||||
|
||||
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
|
||||
import cn.axzo.im.center.common.enums.GroupMemberType;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import cn.axzo.im.channel.netease.client.NimClient;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoResponse;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupInfo;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupMemberInfo;
|
||||
import cn.axzo.im.dao.repository.GroupDao;
|
||||
import cn.axzo.im.dao.repository.GroupMemberDao;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import cn.axzo.im.entity.GroupMember;
|
||||
import cn.axzo.im.group.support.GroupRateLimiter;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import cn.axzo.im.utils.ImAccountParser;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.axzo.im.center.api.vo.PersonAccountAttribute.robot;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GroupMemberSyncer {
|
||||
|
||||
private final GroupMemberDao groupMemberDao;
|
||||
private final GroupDao groupDao;
|
||||
private final NimClient nimClient;
|
||||
private final GroupRateLimiter rateLimiter;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
|
||||
public void syncMembers(Group group) {
|
||||
NimGroupInfo groupInfo = fetchGroupInfo(group).orElse(null);
|
||||
if (groupInfo == null) return;
|
||||
transactionTemplate.executeWithoutResult(unused -> {
|
||||
List<GroupMember> newMembers = parseGroupMembers(group, groupInfo);
|
||||
List<GroupMember> oldMembers = groupMemberDao.getByTid(group.getTid());
|
||||
Set<PersonAccountAttribute> newPersons = newMembers.stream().map(GroupMember::asPerson).collect(toSet());
|
||||
Set<PersonAccountAttribute> oldPersons = oldMembers.stream().map(GroupMember::asPerson).collect(toSet());
|
||||
|
||||
Sets.SetView<PersonAccountAttribute> removedPersons = Sets.difference(oldPersons, newPersons);
|
||||
groupMemberDao.deleteByPersons(group.getTid(), removedPersons);
|
||||
Sets.SetView<PersonAccountAttribute> addedPersons = Sets.difference(newPersons, oldPersons);
|
||||
List<GroupMember> addMembers = newMembers.stream()
|
||||
.filter(newMember -> addedPersons.contains(newMember.asPerson()))
|
||||
.collect(toList());
|
||||
if (!addMembers.isEmpty())
|
||||
groupMemberDao.saveBatch(addMembers);
|
||||
groupDao.updateMembersCount(group.getTid(), newMembers.size());
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<NimGroupInfo> fetchGroupInfo(Group group) {
|
||||
NimGroupGetInfoRequest nimRequest = new NimGroupGetInfoRequest();
|
||||
nimRequest.setTid(group.getTid());
|
||||
long start = System.currentTimeMillis();
|
||||
rateLimiter.requireGetGroupInfo();
|
||||
NimGroupGetInfoResponse nimResponse = nimClient.getGroupInfo(nimRequest);
|
||||
log.info("获取群信息, request={}, response={}, timeUsed={}",
|
||||
nimRequest, nimResponse, System.currentTimeMillis() - start);
|
||||
if (nimResponse.isGroupNotFoundError())
|
||||
return Optional.empty();
|
||||
BizAssertions.assertTrue(nimResponse.isSuccess(),
|
||||
"获取群信息失败: {}", nimResponse.getDesc());
|
||||
return Optional.of(nimResponse.getTinfo());
|
||||
}
|
||||
|
||||
private List<GroupMember> parseGroupMembers(Group group, NimGroupInfo groupInfo) {
|
||||
ArrayList<GroupMember> members = new ArrayList<>();
|
||||
Set<NimGroupMemberInfo> admins = groupInfo.getAdmins();
|
||||
if (admins == null)
|
||||
admins = Collections.emptySet();
|
||||
for (NimGroupMemberInfo member : groupInfo.getPeople()) {
|
||||
PersonAccountAttribute person = ImAccountParser
|
||||
.parsePerson(member.getAccid())
|
||||
.orElse(robot());
|
||||
GroupMember account = new GroupMember();
|
||||
members.add(account);
|
||||
account.setTid(group.getTid());
|
||||
account.setImAccount(member.getAccid());
|
||||
account.setGroupType(group.getType());
|
||||
account.setMemberType(
|
||||
groupInfo.getOwner().equals(member)
|
||||
? GroupMemberType.OWNER
|
||||
: (admins.contains(member) ? GroupMemberType.ADMIN : GroupMemberType.MEMBER));
|
||||
account.setPersonId(person.personIdAsLong());
|
||||
account.setPersonOuId(person.ouIdOrDefault());
|
||||
account.setAppType(person.getAppType());
|
||||
account.setIsRobot(person.isRobot() ? YesOrNo.YES : YesOrNo.NO);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package cn.axzo.im.group;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.GroupCreateRequest;
|
||||
import cn.axzo.im.center.common.enums.YesOrNo;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupAddMembersRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupCreateRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupDismissRequest;
|
||||
import cn.axzo.im.channel.netease.dto.NimGroupRemoveMembersRequest;
|
||||
import cn.axzo.im.dao.repository.GroupLogDao;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import cn.axzo.im.entity.GroupLog;
|
||||
import cn.axzo.im.group.support.GroupProps;
|
||||
import cn.axzo.im.service.domain.ImAccounts;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GroupSupport {
|
||||
|
||||
private static final String INTRODUCE_MESSAGE = "邀请您加入群聊";
|
||||
|
||||
private final GroupProps groupProps;
|
||||
private final GroupLogDao groupLogDao;
|
||||
|
||||
Group buildNewGroup(GroupCreateRequest request, ImAccounts imAccounts) {
|
||||
String owner = imAccounts.findAccount(request.getOwner()).orElse(null);
|
||||
BizAssertions.assertNotNull(owner, "群主没有IM账号, 无法创建群. {}", request.getOwner());
|
||||
Group group = new Group();
|
||||
group.setName(request.getName());
|
||||
group.setBizCode(request.getBizCode());
|
||||
group.setType(request.getGroupType());
|
||||
group.setBizGroupInfo(request.bizGroupInfoOrEmpty());
|
||||
group.setAvatar(request.getAvatar());
|
||||
group.setMemberCount((long) request.getPeople().size());
|
||||
Long memberLimit = request.getMemberLimit();
|
||||
if (memberLimit == null)
|
||||
memberLimit = groupProps.getDefaultMemberLimit();
|
||||
group.setMemberLimit(memberLimit);
|
||||
group.setOwnerAccount(owner);
|
||||
group.setOwnerPersonId(request.getOwner().personIdAsLong());
|
||||
group.setCreatePersonId(request.getOwner().personIdAsLong());
|
||||
group.setIsDismissed(YesOrNo.NO);
|
||||
return group;
|
||||
}
|
||||
|
||||
NimGroupCreateRequest buildNimCreateGroupRequest(
|
||||
GroupCreateRequest request, ImAccounts imAccounts) {
|
||||
String owner = imAccounts.findAccount(request.getOwner()).orElse(null);
|
||||
BizAssertions.assertNotNull(owner, "群主没有IM账号, 无法创建群. {}", request.getOwner());
|
||||
BizAssertions.assertTrue(imAccounts.getAccountSize() > 1, "无法创建群. 存在的群成员IM账号数量需要大于等于2");
|
||||
|
||||
NimGroupCreateRequest nimRequest = new NimGroupCreateRequest();
|
||||
nimRequest.setName(request.getName());
|
||||
nimRequest.setOwner(owner);
|
||||
nimRequest.addMembers(imAccounts.filterAccounts(account -> !account.equals(owner)));
|
||||
nimRequest.setIntroduceMessage(INTRODUCE_MESSAGE);
|
||||
nimRequest.setIcon(request.getAvatar());
|
||||
nimRequest.addCustom(Group.CUSTOM_GROUP_TYPE, request.getGroupType());
|
||||
nimRequest.addCustom(Group.CUSTOM_BIZ_CODE, request.getBizCode());
|
||||
nimRequest.addCustom(Group.CUSTOM_BIZ_GROUP_INFO, request.bizGroupInfoOrEmpty());
|
||||
return nimRequest;
|
||||
}
|
||||
|
||||
NimGroupDismissRequest buildNimDismissGroupRequest(String owner, Group group) {
|
||||
NimGroupDismissRequest nimRequest = new NimGroupDismissRequest();
|
||||
nimRequest.setTid(group.getTid());
|
||||
nimRequest.setOwner(owner);
|
||||
return nimRequest;
|
||||
}
|
||||
|
||||
NimGroupAddMembersRequest buildAddMembersRequest(
|
||||
Group group, String inviter, ImAccounts accounts) {
|
||||
BizAssertions.assertNotEmpty(accounts.getAccounts(), "有效群成员IM账号列表为空");
|
||||
NimGroupAddMembersRequest nimRequest = new NimGroupAddMembersRequest();
|
||||
nimRequest.setTid(group.getTid());
|
||||
nimRequest.setOwner(inviter);
|
||||
nimRequest.setMsg(INTRODUCE_MESSAGE);
|
||||
nimRequest.addMembers(accounts.getAccounts());
|
||||
return nimRequest;
|
||||
}
|
||||
|
||||
NimGroupRemoveMembersRequest buildRemoveMembersRequest(
|
||||
Group group, String owner, ImAccounts accounts) {
|
||||
BizAssertions.assertNotEmpty(accounts.getAccounts(), "有效群成员IM账号列表为空");
|
||||
NimGroupRemoveMembersRequest nimRequest = new NimGroupRemoveMembersRequest();
|
||||
nimRequest.setTid(group.getTid());
|
||||
nimRequest.setOwner(owner);
|
||||
nimRequest.addMembers(accounts.getAccounts());
|
||||
return nimRequest;
|
||||
}
|
||||
|
||||
public void log(Long tid, String context, Object request) {
|
||||
GroupLog log = new GroupLog();
|
||||
log.setTid(tid == null ? 0 : tid);
|
||||
log.setContext(context);
|
||||
log.setRequest(request);
|
||||
groupLogDao.save(log);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package cn.axzo.im.group;
|
||||
|
||||
import cn.axzo.im.dao.mapper.ChatGroupMapper;
|
||||
import cn.axzo.im.dao.repository.GroupDao;
|
||||
import cn.axzo.im.entity.ChatGroup;
|
||||
import cn.axzo.im.entity.Group;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.axzo.im.utils.Queries.query;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LegacyGroupSupport {
|
||||
|
||||
private final GroupDao groupDao;
|
||||
private final ChatGroupMapper chatGroupMapper;
|
||||
|
||||
public Set<String> getMergedAccounts(Set<String> groupIds) {
|
||||
Set<String> accounts = Sets.newHashSet(getGroupAccounts(groupIds));
|
||||
accounts.addAll(getGroupCatAccounts(groupIds));
|
||||
return accounts;
|
||||
}
|
||||
|
||||
private List<String> getGroupAccounts(Set<String> groupIds) {
|
||||
if (CollectionUtils.isEmpty(groupIds))
|
||||
return Collections.emptyList();
|
||||
if (parseLongs(groupIds).isEmpty())
|
||||
return Collections.emptyList();
|
||||
return groupDao.lambdaQuery()
|
||||
.in(Group::getTid, parseLongs(groupIds))
|
||||
.list().stream()
|
||||
.map(Group::getTid)
|
||||
.map(String::valueOf)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private List<String> getGroupCatAccounts(Set<String> groupIds) {
|
||||
if (CollectionUtils.isEmpty(groupIds))
|
||||
return Collections.emptyList();
|
||||
return chatGroupMapper.selectList(
|
||||
query(ChatGroup.class)
|
||||
.in(ChatGroup::getTid, groupIds)).stream()
|
||||
.map(ChatGroup::getTid)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private List<Long> parseLongs(Collection<String> groupIds) {
|
||||
return groupIds.stream()
|
||||
.filter(NumberUtils::isDigits)
|
||||
.map(Long::parseLong)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user