REQ-3345: backup

This commit is contained in:
yanglin 2025-01-20 19:25:16 +08:00
parent a0c3d14d1f
commit 9e63599dac
72 changed files with 1777 additions and 157 deletions

View File

@ -0,0 +1,31 @@
package cn.axzo.im.center.api.feign;
import cn.axzo.framework.domain.web.result.ApiResult;
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.resp.GroupAddMembersResponse;
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
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 {
String INTRODUCE_MESSAGE = "邀请您加入群聊";
@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);
}

View File

@ -4,11 +4,12 @@ import cn.axzo.im.center.common.enums.AppTypeEnum;
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,8 +18,6 @@ import javax.validation.constraints.NotNull;
@Builder
@AllArgsConstructor
@NoArgsConstructor
// IMPORTANT: 不要删除这个注解, 避免@Data被@Setter, @Getter取代
@EqualsAndHashCode
public class PersonAccountAttribute {
/**
@ -48,4 +47,45 @@ public class PersonAccountAttribute {
@NotNull(message = "appType不能为空")
private AppTypeEnum appType;
}
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 ouId == null ? 0L : ouId;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof PersonAccountAttribute)) return false;
PersonAccountAttribute that = (PersonAccountAttribute) o;
return Objects.equals(personId, that.personId) && Objects.equals(ouId, that.ouId) && appType == that.appType;
}
@Override
public int hashCode() {
return Objects.hash(personId, ouId, appType);
}
@Override
public String toString() {
return String.format("[personId:%s,ouId:%s,appType:%s]", personId, ouId, appType);
}
}

View File

@ -0,0 +1,57 @@
package cn.axzo.im.center.api.vo.mq;
import cn.axzo.im.center.common.enums.AppTypeEnum;
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;
/**
* @author yanglin
*/
@Setter @Getter
public class GroupMembersChangeMessage extends MqMessage {
/**
* 群信息
*/
private GroupInfo groupInfo;
/**
* 账号信息
*/
private AccountInfo account;
@Setter @Getter
public static class GroupInfo {
private Long id;
private Long tid;
private String name;
private String bizCode;
private GroupType type;
private String avatar;
private Long memberCount;
private Long memberLimit;
private String ownerAccount;
private Long ownerPersonId;
private Long createPersonId;
private YesOrNo isDismissed;
private Date createAt;
private Date updateAt;
}
@Setter @Getter
public static class AccountInfo {
private Long id;
private Long tid;
private String imAccount;
private Long personId;
private Long personOuId;
private AppTypeEnum appType;
private YesOrNo isRobot;
private Date createAt;
private Date updateAt;
}
}

View File

@ -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);
}

View File

@ -0,0 +1,48 @@
package cn.axzo.im.center.api.vo.req;
import cn.axzo.im.center.api.feign.GroupApi;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
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;
/**
* 邀请发送的文字最大长度 150 位字符
*/
@NotBlank(message = "邀请发送的文字不能为空")
private String introduceMessage = GroupApi.INTRODUCE_MESSAGE;
/**
* 群成员, 不包含群主. 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);
}
}

View File

@ -0,0 +1,92 @@
package cn.axzo.im.center.api.vo.req;
import cn.axzo.im.center.api.feign.GroupApi;
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 lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
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;
/**
* 群成员上限
*/
private Long memberLimit;
/**
* 邀请发送的文字最大长度 150 位字符
*/
@NotBlank(message = "邀请发送的文字不能为空")
private String introduceMessage = GroupApi.INTRODUCE_MESSAGE;
/**
* 群头像最大长度 1024 位字符
*/
private String avatar;
@JSONField(serialize = false, deserialize = false)
public Set<PersonAccountAttribute> getOwnerAndMembers() {
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);
}
}

View File

@ -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;
}

View File

@ -0,0 +1,29 @@
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.Set;
/**
* @author yanglin
*/
@Setter
@Getter
public class GroupAddMembersResponse {
/**
* 未找到IM账号的列表
*/
private Set<PersonAccountAttribute> accountsNotFound;
private JSONObject faccid;
@Override
public String toString() {
return JSON.toJSONString(this);
}
}

View File

@ -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);
}
}

View File

@ -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;
@ -28,7 +29,7 @@ public enum AppTypeEnum implements CodeDefinition<String> {
*/
SYSTEM("system", "服务器");
@EnumValue
private final String code;
private final String message;

View File

@ -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();
}
}

View File

@ -0,0 +1,23 @@
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<String> {
ANDROID,
IOS,
PC,
WEB,
REST,
MAC
;
@Override
public String getCode() {
return name();
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.im.center.common.enums;
import cn.axzo.basics.common.constant.enums.CodeDefinition;
import lombok.RequiredArgsConstructor;
/**
* @author yanglin
*/
@RequiredArgsConstructor
public enum NimMessageType implements CodeDefinition<String> {
TEXT("文本"),
IMAGE("图片"),
SPEECH("语音"),
VIDEO("视频"),
POSITION("地理位置"),
FILE("文件"),
CUSTOM("第三方自定义消息"),
GROUP_NOTIFICATION("群通知"),
CHAT_GROUP_NOTIFICATION("聊天室通知"),
;
private final String description;
@Override
public String getCode() {
return name();
}
}

View File

@ -1,24 +1,28 @@
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.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 +41,43 @@ 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 addMembers(NimGroupAddMembersRequest request);
@Data
class CodeResponse {
class NimCodeResponse {
private Integer code;
private String desc;
private int size;

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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"]

View File

@ -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();

View File

@ -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 参数错误

View File

@ -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;

View File

@ -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.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
/**
* @author yanglin
*/
@Data
@FormRequest
public class NimGroupAddMembersRequest {
private Long tid;
private String owner;
private String members;
private String msg;
public void addMembers(Collection<String> members) {
if (StringUtils.isBlank(this.members))
this.members = "[]";
this.members = JSON.parseArray(this.members)
.fluentAddAll(members)
.toJSONString();
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,116 @@
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.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
/**
* @author yanglin
*/
@Data
@FormRequest
public class NimGroupCreateRequest {
/**
* 群名称最大长度 64 位字符
*/
@JSONField(name = "tname")
private String name;
/**
* 群主账号accid最大长度 32 位字符
*/
private String owner;
/**
* 邀请的群成员列表\["aaa","bbb"\](JSONArray 对应的 accid如果解析出错会报 414)
* members owner 总和上限为 200members 中无需再加 owner 自己的账号
*/
private String members;
/**
* 群描述最大长度 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 addMembers(Collection<String> members) {
if (StringUtils.isBlank(this.members))
this.members = "[]";
this.members = JSON.parseArray(this.members)
.fluentAddAll(members)
.toJSONString();
}
public void addAttachment(String key, Object value) {
if (StringUtils.isBlank(this.attach))
this.attach = "{}";
this.attach = JSON.parseObject(this.attach)
.fluentPut(key, value)
.toJSONString();
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,31 @@
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> members;
@JSONField(serialize = false, deserialize = false)
public Set<NimGroupMemberInfo> getOwnerAndMembers() {
HashSet<NimGroupMemberInfo> ownerAndMembers = new HashSet<>(members);
ownerAndMembers.add(owner);
return ownerAndMembers;
}
public int memberCount() {
return getOwnerAndMembers().size();
}
}

View File

@ -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);
}
}

View File

@ -14,7 +14,7 @@ import java.text.SimpleDateFormat;
*/
@Data
@FormRequest
public class QueryEventRequest {
public class NimQueryEventRequest {
// 要查询用户的accid
@JSONField(name = "accid")
@NotBlank

View File

@ -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;

View File

@ -14,7 +14,7 @@ import java.text.SimpleDateFormat;
*/
@Data
@FormRequest
public class QueryMessageRequest {
public class NimQueryMessageRequest {
// 发送者accid
@NotBlank
private String from;

View File

@ -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;
}

View File

@ -11,7 +11,7 @@ import javax.validation.constraints.NotBlank;
*/
@Data
@FormRequest
public class RefreshTokenRequest {
public class NimRefreshTokenRequest {
@NotBlank
@JSONField(name = "accid")
private String accid;

View File

@ -10,7 +10,7 @@ import lombok.Setter;
*/
@Setter
@Getter
public class RefreshTokenResponse extends NimClient.CodeResponse {
public class NimRefreshTokenResponse extends NimClient.NimCodeResponse {
private Info info;

View File

@ -9,7 +9,7 @@ import lombok.Data;
*/
@Data
@FormRequest
public class RevokeMessageRequest {
public class NimRevokeMessageRequest {
@JSONField(name = "deleteMsgid")
private String messageId;
// 消息发送者的云信 IM 账号accid

View File

@ -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")

View File

@ -10,7 +10,7 @@ import lombok.Setter;
*/
@Setter
@Getter
public class SendCustomMessageResponse extends NimClient.CodeResponse {
public class NimSendCustomMessageResponse extends NimClient.NimCodeResponse {
private String desc;

View File

@ -11,7 +11,7 @@ import lombok.Data;
*/
@Data
@FormRequest
public class UpdateAccountInfoRequest {
public class NimUpdateAccountInfoRequest {
@JSONField(name = "accid")
private String imAccountId;

View File

@ -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

View File

@ -2,11 +2,11 @@ package cn.axzo.im.controller;
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.NimQueryEventRequest;
import cn.axzo.im.channel.netease.dto.NimQueryMessageRequest;
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
import cn.axzo.im.job.CreateMessageHistoryJob;
import cn.axzo.im.job.ExpungeImTaskJob;
import cn.axzo.im.job.RevokeAllMessagesJob;
@ -36,17 +36,17 @@ public class PrivateController {
private final ExpungeImTaskJob expungeImTaskJob;
@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);
}
@ -77,12 +77,12 @@ public class PrivateController {
}
@PostMapping("/private/group/dismissGroup")
public Object dismissGroup(@Valid @RequestBody DismissGroupRequest request) {
public Object dismissGroup(@Valid @RequestBody NimGroupDismissRequest request) {
return CommonResponse.success(nimClient.dismissGroup(request));
}
@PostMapping("/private/group/getGroupInfo")
public Object getGroupInfo(@Valid @RequestBody GetGroupInfoRequest request) {
public Object getGroupInfo(@Valid @RequestBody NimGroupGetInfoRequest request) {
return CommonResponse.success(nimClient.getGroupInfo(request));
}

View File

@ -0,0 +1,10 @@
package cn.axzo.im.dao.mapper;
import cn.axzo.im.entity.GroupAccount;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author yanglin
*/
public interface GroupAccountMapper extends BaseMapper<GroupAccount> {
}

View File

@ -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> {
}

View File

@ -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> {
}

View File

@ -0,0 +1,28 @@
package cn.axzo.im.dao.repository;
import cn.axzo.im.dao.mapper.GroupAccountMapper;
import cn.axzo.im.entity.GroupAccount;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author yanglin
*/
@Repository("groupAccountDao")
public class GroupAccountDao extends ServiceImpl<GroupAccountMapper, GroupAccount> {
public List<GroupAccount> getByTid(Long tid) {
return lambdaQuery()
.eq(GroupAccount::getTid, tid)
.list();
}
public void deleteAccounts(Long tid) {
lambdaUpdate()
.eq(GroupAccount::getTid, tid)
.remove();
}
}

View File

@ -0,0 +1,55 @@
package cn.axzo.im.dao.repository;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
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.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 updateOwner(Long tid, String imAccount, PersonAccountAttribute person) {
lambdaUpdate()
.eq(Group::getTid, tid)
.set(Group::getOwnerAccount, imAccount)
.set(Group::getOwnerPersonId, person.getPersonId())
.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)
.update();
}
}

View File

@ -0,0 +1,13 @@
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.springframework.stereotype.Repository;
/**
* @author yanglin
*/
@Repository("groupMessageDao")
public class GroupMessageDao extends ServiceImpl<GroupMessageMapper, GroupMessage> {
}

View File

@ -0,0 +1,55 @@
package cn.axzo.im.entity;
import cn.axzo.im.center.common.enums.GroupType;
import cn.axzo.im.center.common.enums.YesOrNo;
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", autoResultMap = true)
public class Group {
public static final String ATTACHMENT_GROUP_TYPE = "groupType";
private Long id;
private Long tid;
private String name;
private String bizCode;
private GroupType type;
private String avatar;
private Long memberCount;
private Long memberLimit;
private String ownerAccount;
private Long ownerPersonId;
private Long createPersonId;
private Long isDelete;
private YesOrNo isDismissed;
private Date createAt;
private Date updateAt;
@TableField(typeHandler = FastjsonTypeHandler.class)
private RecordExt recordExt;
public boolean isMemberLimitReached(int memberCount) {
if (memberLimit <= 0) return false;
return memberCount > memberLimit;
}
public boolean isDismissed() {
return getIsDismissed() == YesOrNo.YES;
}
@Setter
@Getter
public static class RecordExt {
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.im.entity;
import cn.axzo.im.center.common.enums.AppTypeEnum;
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_account", autoResultMap = true)
public class GroupAccount {
private Long id;
private Long tid;
private String imAccount;
private Long personId;
private Long personOuId;
private AppTypeEnum appType;
private YesOrNo isRobot;
private Long isDelete;
private Date createAt;
private Date updateAt;
}

View File

@ -0,0 +1,46 @@
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 com.dingtalk.api.request.OapiAtsResumeAddRequest;
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 NimMessageType messageType;
private NimFromClientType fromClientType;
private Date sendTime;
@TableField(typeHandler = FastjsonTypeHandler.class)
private JSONObject body;
@TableField(typeHandler = FastjsonTypeHandler.class)
private RecordExt recordExt;
private Long isDelete;
private Date createAt;
private Date updateAt;
@Setter
@Getter
public static class RecordExt {
}
}

View File

@ -13,7 +13,9 @@ public enum EventTypeEnum {
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_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", "节点用户修改"),

View File

@ -0,0 +1,54 @@
package cn.axzo.im.group;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.framework.rocketmq.Event;
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.GroupAccount;
import cn.axzo.im.event.inner.EventTypeEnum;
import cn.axzo.im.group.domain.GroupMembersDiff;
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 fireMembersAdded(Group group, GroupMembersDiff diff) {
diff.diff();
fireMembersChanged(group, diff.getAdded(), EventTypeEnum.GROUP_ADD_MEMBERS);
}
void fireMembersRemoved(Group group, GroupMembersDiff diff) {
diff.diff();
fireMembersChanged(group, diff.getRemoved(), EventTypeEnum.GROUP_REMOVE_MEMBERS);
}
private void fireMembersChanged(Group group, Collection<GroupAccount> accounts, EventTypeEnum eventType) {
if (accounts.isEmpty()) return;
Group effectiveGroup = groupDao.getById(group.getId());
for (GroupAccount account : accounts) {
GroupMembersChangeMessage message = new GroupMembersChangeMessage();
message.setGroupInfo(BeanMapper.copyBean(effectiveGroup, GroupMembersChangeMessage.GroupInfo.class));
message.setAccount(BeanMapper.copyBean(account, GroupMembersChangeMessage.AccountInfo.class));
Event event = Event.builder()
.targetId(account.getImAccount())
.targetType(eventType.getModel())
.eventCode(eventType.getEventCode())
.data(message)
.build();
mqProducer.send(event);
}
}
}

View File

@ -0,0 +1,40 @@
package cn.axzo.im.group;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.feign.GroupApi;
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.resp.GroupAddMembersResponse;
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;
/**
* @author yanglin
*/
@Slf4j
@RestController
@RequiredArgsConstructor
public class GroupController implements GroupApi {
private final GroupManager groupManager;
@Override
public ApiResult<GroupCreateResponse> createGroup(GroupCreateRequest request) {
return ApiResult.ok(groupManager.createGroup(request));
}
@Override
public ApiResult<Void> dismissGroup(GroupDismissRequest request) {
groupManager.dismissGroup(request);
return ApiResult.ok();
}
@Override
public ApiResult<GroupAddMembersResponse> addMembers(GroupAddMembersRequest request) {
return ApiResult.ok(groupManager.addMembers(request));
}
}

View File

@ -0,0 +1,158 @@
package cn.axzo.im.group;
import cn.axzo.basics.common.exception.ServiceException;
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.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.NimGroupGetInfoRequest;
import cn.axzo.im.channel.netease.dto.NimGroupGetInfoResponse;
import cn.axzo.im.channel.netease.dto.NimGroupInfo;
import cn.axzo.im.dao.repository.GroupAccountDao;
import cn.axzo.im.dao.repository.GroupDao;
import cn.axzo.im.entity.Group;
import cn.axzo.im.entity.GroupAccount;
import cn.axzo.im.group.domain.GroupMembersDiff;
import cn.axzo.im.service.AccountService;
import cn.axzo.im.service.domain.PersonImAccounts;
import cn.axzo.im.utils.BizAssertions;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* @author yanglin
*/
@Slf4j
@Component
@RequiredArgsConstructor
class GroupManager {
private final NimClient nimClient;
private final GroupDao groupDao;
private final GroupAccountDao groupAccountDao;
private final GroupSupport groupSupport;
private final AccountService accountService;
private final GroupBroadcaster groupBroadcaster;
@Transactional
public GroupCreateResponse createGroup(GroupCreateRequest request) {
PersonImAccounts accounts = accountService.findPersonAccounts(request.getOwnerAndMembers());
Group group = groupSupport.buildNewGroup(request, accounts);
try {
groupDao.save(group);
} catch (DuplicateKeyException e) {
log.warn("重复创建群, request={}", request, e);
throw new ServiceException(403, String.format("群已经存在: %s", request.getName()));
}
NimGroupCreateRequest nimRequest = groupSupport.buildNimCreateGroupRequest(request, group, accounts);
NimGroupCreateResponse nimResponse = nimClient.createGroup(nimRequest);
log.info("创建群, request={}, response={}", nimRequest, nimResponse);
BizAssertions.assertTrue(nimResponse.isSuccess(), "创建群失败: {}", nimResponse.getDesc());
groupDao.updateTid(group.getId(), nimResponse.getTid());
GroupMembersDiff memberDiff = createMembersDiff(nimResponse.getTid());
syncGroupAccounts(group);
groupBroadcaster.fireMembersAdded(group, memberDiff);
GroupCreateResponse response = new GroupCreateResponse();
response.setTid(nimResponse.getTid());
Set<PersonAccountAttribute> notFound = accounts.getAccountNotFoundPersons(request.getMembers());
if (!notFound.isEmpty())
groupSupport.log("创建群[TID:{}], IM账号不存在列表: {}", group.getTid(), JSON.toJSONString(notFound));
response.setAccountsNotFound(notFound);
return response;
}
@Transactional
public void dismissGroup(GroupDismissRequest request) {
Group group = findGroupForUpdateOrThrow(request.getTid());
if (group.isDismissed()) return;
NimGroupDismissRequest nimRequest = groupSupport
.buildNimDismissGroupRequest(group.getOwnerAccount(), group);
NimGroupDismissResponse nimResponse = nimClient.dismissGroup(nimRequest);
log.info("解散群, request={}, response={}", nimRequest, nimResponse);
if (!nimResponse.isGroupNotFoundError())
BizAssertions.assertTrue(nimResponse.isSuccess(), "解散群失败: {}", nimResponse.getDesc());
groupDao.setDismissed(group.getTid());
}
@Transactional
public GroupAddMembersResponse addMembers(GroupAddMembersRequest request) {
Group group = findGroupForUpdateOrThrow(request.getTid());
BizAssertions.assertFalse(group.isDismissed(), "群已经解散: {}", group.getTid());
NimGroupInfo groupInfo = fetchGroupInfo(group).orElse(null);
BizAssertions.assertNotNull(groupInfo, "[NIM] 群不存在: {}", group.getTid());
PersonImAccounts accounts = accountService.findPersonAccounts(request.getMembers());
NimGroupAddMembersRequest nimRequest = groupSupport
.buildAddMembersRequest(request, group, groupInfo.getOwner().getAccid(), accounts);
GroupMembersDiff memberDiff = createMembersDiff(group.getTid());
NimGroupAddMembersResponse nimResponse = nimClient.addMembers(nimRequest);
log.info("添加群成员, request={}, response={}", nimRequest, nimResponse);
BizAssertions.assertTrue(nimResponse.isSuccess(), "添加群成员失败: {}", nimResponse.getDesc());
if (MapUtil.isNotEmpty(nimResponse.getFaccid()))
groupSupport.log("添加群成员[TID:{}], 错误信息: {}",
group.getTid(), JSON.toJSONString(nimResponse.getFaccid()));
syncGroupAccounts(group);
groupBroadcaster.fireMembersAdded(group, memberDiff);
GroupAddMembersResponse response = new GroupAddMembersResponse();
Set<PersonAccountAttribute> notFound = accounts.getAccountNotFoundPersons(request.getMembers());
if (!notFound.isEmpty())
groupSupport.log("创建群[TID:{}], IM账号不存在列表: {}", group.getTid(), JSON.toJSONString(notFound));
response.setAccountsNotFound(notFound);
return response;
}
private void syncGroupAccounts(Group group) {
NimGroupInfo groupInfo = fetchGroupInfo(group).orElse(null);
if (groupInfo == null) return;
groupAccountDao.deleteAccounts(group.getTid());
List<GroupAccount> accounts = groupSupport
.parseGroupAccounts(group, groupInfo.getOwnerAndMembers());
if (CollectionUtils.isNotEmpty(accounts))
groupAccountDao.saveBatch(accounts);
groupDao.updateMembersCount(group.getTid(), accounts.size());
}
@NotNull
private Group findGroupForUpdateOrThrow(Long tid) {
Group group = groupDao.findByTid(tid, true).orElse(null);
BizAssertions.assertNotNull(group, "群不存在: {}", tid);
return group;
}
private Optional<NimGroupInfo> fetchGroupInfo(Group group) {
NimGroupGetInfoRequest nimRequest = new NimGroupGetInfoRequest();
nimRequest.setTid(group.getTid());
long start = System.currentTimeMillis();
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 GroupMembersDiff createMembersDiff(Long tid) {
return new GroupMembersDiff(() -> groupAccountDao.getByTid(tid));
}
}

View File

@ -0,0 +1,119 @@
package cn.axzo.im.group;
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.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.NimGroupMemberInfo;
import cn.axzo.im.entity.Group;
import cn.axzo.im.entity.GroupAccount;
import cn.axzo.im.service.ChatGroupService;
import cn.axzo.im.service.domain.PersonImAccounts;
import cn.axzo.im.utils.BizAssertions;
import cn.axzo.im.utils.ImAccountParser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author yanglin
*/
@Slf4j
@Component
@RequiredArgsConstructor
class GroupSupport {
private final ChatGroupService chatGroupService;
Group buildNewGroup(GroupCreateRequest request, PersonImAccounts accounts) {
String owner = accounts.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.setAvatar(request.getAvatar());
group.setMemberCount((long) request.getOwnerAndMembers().size());
Long memberLimit = request.getMemberLimit();
group.setMemberLimit(memberLimit == null ? 0L : memberLimit);
group.setOwnerAccount(owner);
group.setOwnerPersonId(request.getOwner().personIdAsLong());
group.setCreatePersonId(request.getOwner().personIdAsLong());
group.setIsDismissed(YesOrNo.NO);
return group;
}
NimGroupCreateRequest buildNimCreateGroupRequest(
GroupCreateRequest request, Group group, PersonImAccounts accounts) {
BizAssertions.assertFalse(group.isMemberLimitReached(
accounts.getAccountSize()), "无法创建群, 群成员数量超过上限");
String owner = accounts.findAccount(request.getOwner()).orElse(null);
BizAssertions.assertNotNull(owner, "群主没有IM账号, 无法创建群. {}", request.getOwner());
BizAssertions.assertTrue(accounts.getAccountSize() > 1, "无法创建群. 存在的群成员IM账号数量需要大于等于2");
NimGroupCreateRequest nimRequest = new NimGroupCreateRequest();
nimRequest.setName(request.getName());
nimRequest.setOwner(owner);
nimRequest.addMembers(accounts.filterAccounts(account -> !account.equals(owner)));
nimRequest.setIntroduceMessage(request.getIntroduceMessage());
nimRequest.setIcon(request.getAvatar());
nimRequest.addAttachment(Group.ATTACHMENT_GROUP_TYPE, request.getGroupType());
return nimRequest;
}
NimGroupDismissRequest buildNimDismissGroupRequest(String owner, Group group) {
NimGroupDismissRequest nimRequest = new NimGroupDismissRequest();
nimRequest.setTid(group.getTid());
nimRequest.setOwner(owner);
return nimRequest;
}
NimGroupAddMembersRequest buildAddMembersRequest(
GroupAddMembersRequest request, Group group, String owner, PersonImAccounts accounts) {
NimGroupAddMembersRequest nimRequest = new NimGroupAddMembersRequest();
nimRequest.setTid(group.getTid());
nimRequest.setOwner(owner);
nimRequest.setMsg(request.getIntroduceMessage());
nimRequest.addMembers(accounts.getAccounts());
return nimRequest;
}
void log(String message, Object... args) {
try {
chatGroupService.sendDingRobot(MessageFormatter.arrayFormat(message, args).getMessage());
} catch (Exception e) {
log.warn("发送钉钉机器人消息失败", e);
}
}
List<GroupAccount> parseGroupAccounts(Group group, Set<NimGroupMemberInfo> members) {
ArrayList<GroupAccount> accounts = new ArrayList<>();
for (NimGroupMemberInfo member : members) {
PersonAccountAttribute person = ImAccountParser.parsePerson(member.getAccid()).orElse(null);
GroupAccount account = new GroupAccount();
accounts.add(account);
account.setTid(group.getTid());
account.setImAccount(member.getAccid());
if (person == null) {
account.setPersonId(0L);
account.setPersonOuId(0L);
account.setAppType(null);
account.setIsRobot(YesOrNo.YES);
} else {
account.setPersonId(Long.valueOf(person.getPersonId()));
account.setPersonOuId(person.ouIdOrDefault());
account.setAppType(person.getAppType());
account.setIsRobot(YesOrNo.NO);
}
}
return accounts;
}
}

View File

@ -0,0 +1,57 @@
package cn.axzo.im.group.domain;
import cn.axzo.im.entity.GroupAccount;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* @author yanglin
*/
@SuppressWarnings("DuplicatedCode")
public class GroupMembersDiff {
private final Supplier<List<GroupAccount>> accountsProvider;
private final Map<String, GroupAccount> before;
private Map<String, GroupAccount> after;
public GroupMembersDiff(Supplier<List<GroupAccount>> accountsProvider) {
this.accountsProvider = accountsProvider;
this.before = reload();
}
public void diff() {
if (after == null)
after = reload();
}
public Collection<GroupAccount> getAdded() {
HashMap<String, GroupAccount> accounts = new HashMap<>();
for (GroupAccount account : after.values()) {
String imAccount = account.getImAccount();
if (!before.containsKey(imAccount) && !accounts.containsKey(imAccount))
accounts.put(imAccount, account);
}
return accounts.values();
}
public Collection<GroupAccount> getRemoved() {
HashMap<String, GroupAccount> accounts = new HashMap<>();
for (GroupAccount account : before.values()) {
String imAccount = account.getImAccount();
if (!after.containsKey(imAccount) && !accounts.containsKey(imAccount))
accounts.put(imAccount, account);
}
return accounts.values();
}
private Map<String, GroupAccount> reload() {
return accountsProvider.get().stream()
.collect(Collectors.toMap(GroupAccount::getImAccount, account -> account));
}
}

View File

@ -7,7 +7,7 @@ import cn.axzo.basics.profiles.dto.mq.ProfileMQBaseEntity;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.im.channel.netease.dto.GetAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoResponse;
import cn.axzo.im.entity.AccountRegister;
import cn.axzo.im.entity.bo.AccountQueryParam;
import cn.axzo.im.event.inner.EventTypeEnum;
@ -83,7 +83,7 @@ public class PersonProfileAvatarUpdateEventHandler implements EventHandler, Init
//3 同步网易云信账号
PersonProfileDto personProfileDto = personProfileDtoCommonResponse.getData();
for (AccountRegister accountRegister : accountRegisters) {
GetAccountInfoResponse.AccountInfo accountInfo = accountService.fetchImAccountInfoByImAccount(accountRegister.getImAccount());
NimGetAccountInfoResponse.AccountInfo accountInfo = accountService.fetchImAccountInfoByImAccount(accountRegister.getImAccount());
accountService.syncImAccount(accountRegister.getAccountId(), accountRegister.getImAccount(), accountInfo.getOrCreateExtObject(),personProfileDto.getAvatarUrl(), personProfileDto.getRealName());
}
}

View File

@ -3,7 +3,7 @@ package cn.axzo.im.job;
import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
import cn.axzo.basics.common.page.PageRequest;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.RevokeMessageRequest;
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
import cn.axzo.im.dao.mapper.MessageHistoryMapper;
import cn.axzo.im.entity.MessageHistory;
import cn.axzo.im.utils.Queries;
@ -99,11 +99,11 @@ public class RevokeAllMessagesJob extends IJobHandler {
private void revokeImpl(List<MessageHistory> messages) {
for (MessageHistory message : messages) {
RevokeMessageRequest request = new RevokeMessageRequest();
NimRevokeMessageRequest request = new NimRevokeMessageRequest();
request.setMessageId(message.getMessageId());
request.setFrom(message.getFromAccount());
request.setTo(message.getToAccount());
NimClient.CodeResponse resp = nimClient.revoke(request);
NimClient.NimCodeResponse resp = nimClient.revoke(request);
log.info("revokeImpl, req={}, resp={}", JSON.toJSONString(request), JSON.toJSONString(resp));
}
}

View File

@ -6,9 +6,9 @@ import cn.axzo.basics.profiles.dto.basic.BasicDto;
import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.im.center.common.enums.AccountTypeEnum;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.GetAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoResponse;
import cn.axzo.im.dao.repository.AccountRegisterDao;
import cn.axzo.im.entity.AccountRegister;
import cn.axzo.im.service.AccountService;
@ -107,7 +107,7 @@ public class UpdateImAccountPersonInfoJob {
}
log.info("updateImAccountByAccountRegis, count: {}", accounts.size());
//1 获取IM账号
GetAccountInfoResponse imAccountList = accountService.getImAccount(accounts);
NimGetAccountInfoResponse imAccountList = accountService.getImAccount(accounts);
if (!imAccountList.isSuccess()) {
log.warn("get account info failed, {}", imAccountList);
@ -116,7 +116,7 @@ public class UpdateImAccountPersonInfoJob {
//2 构建PersonProfile
Map<Long, PersonProfileDto> id2PersonProfile = this.buildPersonProfileMap(accounts);
for (AccountRegister account : accounts) {
GetAccountInfoResponse.AccountInfo imAccount = imAccountList.findImAccountInfo(account.getImAccount()).orElse(null);
NimGetAccountInfoResponse.AccountInfo imAccount = imAccountList.findImAccountInfo(account.getImAccount()).orElse(null);
if (imAccount == null) continue;
//3 获取personProfile
PersonProfileDto person = this.fetchPersonById(account.getAccountId(), id2PersonProfile);
@ -125,7 +125,7 @@ public class UpdateImAccountPersonInfoJob {
continue;
}
//4 更新IM账号
UpdateAccountInfoResponse updateAccountInfoResponse
NimUpdateAccountInfoResponse updateAccountInfoResponse
= accountService.syncImAccount(account.getAccountId(), imAccount.getAccid()
,imAccount.getOrCreateExtObject(), person.getAvatarUrl(), person.getRealName());
//日志打印
@ -147,7 +147,7 @@ public class UpdateImAccountPersonInfoJob {
* 日志打印
* @param updateAccountInfoResponse
*/
private void successLog(UpdateAccountInfoResponse updateAccountInfoResponse) {
private void successLog(NimUpdateAccountInfoResponse updateAccountInfoResponse) {
if (updateAccountInfoResponse.isSuccess()) {
log.info("update account info success, {}", updateAccountInfoResponse);
} else {
@ -168,12 +168,12 @@ public class UpdateImAccountPersonInfoJob {
.collect(Collectors.toMap(BasicDto::getId, i -> i));
}
private UpdateAccountInfoResponse updateImAccount(AccountRegister account, GetAccountInfoResponse.AccountInfo accountInfo,Map<Long, PersonProfileDto> id2PersonProfile) {
private NimUpdateAccountInfoResponse updateImAccount(AccountRegister account, NimGetAccountInfoResponse.AccountInfo accountInfo, Map<Long, PersonProfileDto> id2PersonProfile) {
RateLimiter rateLimiter = RateLimiter.create(60);
JSONObject ext = accountInfo.getOrCreateExtObject();
ext.put("personId", account.getAccountId());
UpdateAccountInfoRequest updateAccountInfoRequest = new UpdateAccountInfoRequest();
NimUpdateAccountInfoRequest updateAccountInfoRequest = new NimUpdateAccountInfoRequest();
updateAccountInfoRequest.setImAccountId(accountInfo.getAccid());
updateAccountInfoRequest.addExt(ext);

View File

@ -1,6 +1,6 @@
package cn.axzo.im.send;
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse;
import cn.axzo.im.entity.HistoryRecordExt;
import cn.axzo.im.entity.MessageHistory;
@ -129,7 +129,7 @@ public class SendExecutor<T> implements Supplier<ExecResult> {
}
public void setBatchSendSuccess(List<MessageHistory> histories,
BatchSendCustomMessageResponse response,
NimBatchSendCustomMessageResponse response,
HistoryRecordExt updateExt) {
sendCount.addAndGet(histories.size());
sendManager.setBatchSendSuccess(histories, response, updateExt);

View File

@ -1,6 +1,6 @@
package cn.axzo.im.send;
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse;
import cn.axzo.im.dao.mapper.MessageHistoryMapper;
import cn.axzo.im.entity.HistoryRecordExt;
@ -226,7 +226,7 @@ public class SendManager {
}
void setBatchSendSuccess(List<MessageHistory> histories,
BatchSendCustomMessageResponse response,
NimBatchSendCustomMessageResponse response,
HistoryRecordExt updateExt) {
transactionTemplate.executeWithoutResult(unused -> {
messageHistoryService.setBatchSendSuccess(histories, response, updateExt);

View File

@ -1,8 +1,8 @@
package cn.axzo.im.send.handler;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageRequest;
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageRequest;
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageResponse;
import cn.axzo.im.entity.HistoryRecordExt;
import cn.axzo.im.entity.MessageHistory;
import cn.axzo.im.send.MessageHistoryNimLogger;
@ -36,7 +36,7 @@ public class CustomSendBatchHandler extends SendBatchHandler {
executor.log("batchSendCustomMessage - request record size: {}, batchNo={}",
histories.size(), histories.get(0).determineBatchNo().orElse(null));
MessageHistory sample = histories.get(0);
BatchSendCustomMessageRequest request = new BatchSendCustomMessageRequest();
NimBatchSendCustomMessageRequest request = new NimBatchSendCustomMessageRequest();
request.setFromAccount(sample.getFromAccount());
request.setToAccountJsonString(JSON.toJSONString(Lists.transform(
histories, MessageHistory::getToAccount)));
@ -45,7 +45,7 @@ public class CustomSendBatchHandler extends SendBatchHandler {
request.setSound(sample.getOrCreateRecordExt().getSound());
pushPropPopulator.populate(request, sample);
messageHistoryNimLogger.logAsync(histories, request);
BatchSendCustomMessageResponse response = nimClient.batchSendCustomMessage(request);
NimBatchSendCustomMessageResponse response = nimClient.batchSendCustomMessage(request);
HistoryRecordExt ext = new HistoryRecordExt();
ext.setSendApi("batchSendCustomMessage");
ext.setSendExecId(executor.sendExec().getExecId());

View File

@ -1,8 +1,8 @@
package cn.axzo.im.send.handler;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.SendCustomMessageRequest;
import cn.axzo.im.channel.netease.dto.SendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.NimSendCustomMessageRequest;
import cn.axzo.im.channel.netease.dto.NimSendCustomMessageResponse;
import cn.axzo.im.entity.MessageHistory;
import cn.axzo.im.send.MessageHistoryNimLogger;
import cn.axzo.im.send.SendExecutor;
@ -27,7 +27,7 @@ public class CustomSendOneHandler extends SendOneHandler {
executor.log("sendCustomMessage - historyId={}, taskId={}, bizId={}, batchNo={}",
history.getId(), history.getImMessageTaskId(),
history.getBizId(), history.determineBatchNo().orElse(null));
SendCustomMessageRequest request = new SendCustomMessageRequest();
NimSendCustomMessageRequest request = new NimSendCustomMessageRequest();
request.setFromAccount(history.getFromAccount());
request.setToAccount(history.getToAccount());
request.setAttachJsonString(history.getMessageBody());
@ -36,7 +36,7 @@ public class CustomSendOneHandler extends SendOneHandler {
request.setPayloadJsonString(history.getMessageBody());
request.setSound(history.getOrCreateRecordExt().getSound());
messageHistoryNimLogger.logAsync(history, request);
SendCustomMessageResponse response = nimClient.sendCustomMessage(request);
NimSendCustomMessageResponse response = nimClient.sendCustomMessage(request);
if (response.isRateLimited())
executor.scheduleRetrySend(history, null);
else if (response.isSuccess())

View File

@ -4,6 +4,7 @@ import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.api.vo.req.AccountAbsentQuery;
import cn.axzo.im.center.api.vo.req.AccountQuery;
import cn.axzo.im.center.api.vo.req.RobotAccountReq;
@ -15,15 +16,15 @@ import cn.axzo.im.center.common.enums.RobotStatusEnum;
import cn.axzo.im.channel.IMChannelProvider;
import cn.axzo.im.channel.netease.INotifyService;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.GetAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.GetAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.NimAccountInfo;
import cn.axzo.im.channel.netease.dto.RefreshTokenRequest;
import cn.axzo.im.channel.netease.dto.RefreshTokenResponse;
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.NimGetAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.NimRefreshTokenRequest;
import cn.axzo.im.channel.netease.dto.NimRefreshTokenResponse;
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.NimUpdateAccountInfoResponse;
import cn.axzo.im.channel.netease.dto.RegisterRequest;
import cn.axzo.im.channel.netease.dto.RegisterResponse;
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoRequest;
import cn.axzo.im.channel.netease.dto.UpdateAccountInfoResponse;
import cn.axzo.im.dao.mapper.AccountRegisterMapper;
import cn.axzo.im.dao.repository.AccountRegisterDao;
import cn.axzo.im.dao.repository.RobotInfoDao;
@ -31,6 +32,7 @@ import cn.axzo.im.entity.AccountRegister;
import cn.axzo.im.entity.RobotInfo;
import cn.axzo.im.entity.bo.AccountQueryParam;
import cn.axzo.im.gateway.ProfilesApiGateway;
import cn.axzo.im.service.domain.PersonImAccounts;
import cn.axzo.im.utils.MiscUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@ -248,8 +250,8 @@ public class AccountService {
}
private UserAccountResp createAccountRegister(String userId, String userIdWrapper, String appType,
String accountType, String headImageUrl, String nickName,
Long ouId) {
String accountType, String headImageUrl, String nickName,
Long ouId) {
//1.检查账户是否已经创建
String appKey = imChannelProvider.getProviderAppKey();
UserAccountResp userAccountResp = new UserAccountResp();
@ -293,10 +295,10 @@ public class AccountService {
//重复注册的用户 会返回imaccount 不会返回token
String token = accountResp.getToken();
if (token == null && accountResp.getDesc() != null && accountResp.getDesc().contains("already")) {
RefreshTokenRequest refreshTokenRequest = new RefreshTokenRequest();
NimRefreshTokenRequest refreshTokenRequest = new NimRefreshTokenRequest();
refreshTokenRequest.setAccid(userIdWrapper);
log.info("refreshing token for account={}", userIdWrapper);
RefreshTokenResponse refreshTokenResponse = nimClient.refreshToken(refreshTokenRequest);
NimRefreshTokenResponse refreshTokenResponse = nimClient.refreshToken(refreshTokenRequest);
if (refreshTokenResponse.isSuccess() && refreshTokenResponse.getInfo() != null) {
token = refreshTokenResponse.getInfo().getToken();
}
@ -341,6 +343,7 @@ public class AccountService {
/**
* 建议用更通用的AccountRegisterService.page解耦
*
* @param accountQuery
* @return
*/
@ -398,7 +401,7 @@ public class AccountService {
throw new ServiceException("请求参数AppType=[" + accountAbsentQuery.getAppType() + "]不支持!");
}
List<UserAccountResp> userAccountAll = Lists.newArrayList();
AppTypeEnum[] target = new AppTypeEnum[] {appType};
AppTypeEnum[] target = new AppTypeEnum[]{appType};
for (AppTypeEnum appTypeEnum : target) {
AccountRegisterService.ListAccountRegisterParam listAccountRegisterParam = AccountRegisterService.ListAccountRegisterParam.builder()
.appType(appTypeEnum.getCode())
@ -432,10 +435,10 @@ public class AccountService {
PersonProfileDto personProfileDto = profilesApiGateway.getPersonProfileById(Long.valueOf(accountAbsentQuery.getPersonId()));
userAccountReq.setNickName(StringUtils.isNotBlank(personProfileDto.getRealName())? personProfileDto.getRealName() : DEFAULT_NICK_NAME + accountAbsentQuery.getPersonId());
userAccountReq.setNickName(StringUtils.isNotBlank(personProfileDto.getRealName()) ? personProfileDto.getRealName() : DEFAULT_NICK_NAME + accountAbsentQuery.getPersonId());
userAccountReq.setHeadImageUrl(personProfileDto.getAvatarUrl());
// 管理版需要根据ou注册IM账号做数据隔离
if (appTypeEnum == AppTypeEnum.CMP && accountAbsentQuery.getOuId() != null && accountAbsentQuery.getOuId() != 0) {
if (appTypeEnum == AppTypeEnum.CMP && accountAbsentQuery.getOuId() != null && accountAbsentQuery.getOuId() != 0) {
userAccountReq.setOrganizationalUnitId(accountAbsentQuery.getOuId());
}
UserAccountResp accountResp = null;
@ -511,11 +514,11 @@ public class AccountService {
/**
* 同步网易云信账号
*/
public UpdateAccountInfoResponse syncImAccount(String accountId, String accid, JSONObject ext, String avatarUrl, String realName) {
public NimUpdateAccountInfoResponse syncImAccount(String accountId, String accid, JSONObject ext, String avatarUrl, String realName) {
RateLimiter rateLimiter = RateLimiter.create(60);
ext.put("personId", accountId);
UpdateAccountInfoRequest updateAccountInfoRequest = new UpdateAccountInfoRequest();
NimUpdateAccountInfoRequest updateAccountInfoRequest = new NimUpdateAccountInfoRequest();
updateAccountInfoRequest.setImAccountId(accid);
updateAccountInfoRequest.addExt(ext);
@ -530,41 +533,41 @@ public class AccountService {
rateLimiter.acquire();
log.info("syncImAccount-updateAccountInfoRequest, params:{}", JSON.toJSONString(updateAccountInfoRequest));
UpdateAccountInfoResponse updateAccountInfoResponse = nimClient.updateAccountInfo(updateAccountInfoRequest);
NimUpdateAccountInfoResponse updateAccountInfoResponse = nimClient.updateAccountInfo(updateAccountInfoRequest);
log.info("syncImAccount-updateAccountInfoRequest, result:{}", JSON.toJSONString(updateAccountInfoResponse));
return updateAccountInfoResponse;
}
public GetAccountInfoResponse getImAccount(List<AccountRegister> accounts) {
public NimGetAccountInfoResponse getImAccount(List<AccountRegister> accounts) {
if (CollectionUtils.isEmpty(accounts)) {
return null;
}
GetAccountInfoRequest getAccountInfoRequest = new GetAccountInfoRequest();
NimGetAccountInfoRequest getAccountInfoRequest = new NimGetAccountInfoRequest();
List<String> imAccount = accounts.stream().map(item -> item.getImAccount()).distinct().collect(toList());
getAccountInfoRequest.setAccids(JSON.toJSONString(imAccount));
// getAccountInfoRequest.setImAccountIds(accounts.stream().map(item -> item.getImAccount()).distinct().collect(toList()));
return nimClient.getAccountInfo(getAccountInfoRequest);
}
public List<GetAccountInfoResponse.AccountInfo> fetchImAccountsByImAccounts(List<String> accounts) {
public List<NimGetAccountInfoResponse.AccountInfo> fetchImAccountsByImAccounts(List<String> accounts) {
if (CollectionUtils.isEmpty(accounts)) {
return null;
}
GetAccountInfoRequest getAccountInfoRequest = new GetAccountInfoRequest();
NimGetAccountInfoRequest getAccountInfoRequest = new NimGetAccountInfoRequest();
// getAccountInfoRequest.setImAccountIds(accounts);
getAccountInfoRequest.setAccids(JSON.toJSONString(accounts));
GetAccountInfoResponse response = nimClient.getAccountInfo(getAccountInfoRequest);
NimGetAccountInfoResponse response = nimClient.getAccountInfo(getAccountInfoRequest);
if (Objects.isNull(response) || CollectionUtils.isEmpty(response.getUinfos())) {
return null;
}
return response.getUinfos();
}
public GetAccountInfoResponse.AccountInfo fetchImAccountInfoByImAccount(String imAccount) {
public NimGetAccountInfoResponse.AccountInfo fetchImAccountInfoByImAccount(String imAccount) {
if (StringUtils.isBlank(imAccount)) {
return null;
}
List<GetAccountInfoResponse.AccountInfo> accountInfos = this.fetchImAccountsByImAccounts(Lists.newArrayList(imAccount));
List<NimGetAccountInfoResponse.AccountInfo> accountInfos = this.fetchImAccountsByImAccounts(Lists.newArrayList(imAccount));
if (CollectionUtils.isEmpty(accountInfos)) {
return null;
}
@ -577,4 +580,20 @@ public class AccountService {
AccountTypeEnum.CUSTOM, AppTypeEnum.SYSTEM, appKey);
}
public PersonImAccounts findPersonAccounts(Collection<PersonAccountAttribute> persons) {
if (CollectionUtils.isEmpty(persons))
return new PersonImAccounts(Collections.emptyList());
List<AccountRegister> accounts = accountRegisterDao.lambdaQuery()
.nested(wrapper -> {
for (PersonAccountAttribute person : persons) {
wrapper.or()
.eq(AccountRegister::getAccountId, person.getPersonId())
.eq(AccountRegister::getAppType, person.getAppType().getCode())
.eq(person.getAppType() == AppTypeEnum.CMP, AccountRegister::getOuId, person.getOuId());
}
})
.list();
return new PersonImAccounts(accounts);
}
}

View File

@ -0,0 +1,65 @@
package cn.axzo.im.service.domain;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.entity.AccountRegister;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import static java.util.stream.Collectors.toSet;
/**
* @author yanglin
*/
@Setter
@RequiredArgsConstructor
public class PersonImAccounts {
private final List<AccountRegister> imAccounts;
public Set<PersonAccountAttribute>
getAccountNotFoundPersons(Set<PersonAccountAttribute> persons) {
if (CollectionUtils.isEmpty(persons))
return Collections.emptySet();
return persons.stream()
.filter(person -> !findAccount(person).isPresent())
.collect(toSet());
}
public Set<String> filterAccounts(Predicate<String> filter) {
return getAccounts().stream()
.filter(filter)
.collect(toSet());
}
public int getAccountSize() {
return imAccounts.size();
}
public Set<String> getAccounts() {
return imAccounts.stream()
.map(AccountRegister::getImAccount)
.collect(toSet());
}
public Optional<String> findAccount(PersonAccountAttribute person) {
return imAccounts.stream()
.filter(account -> isAccountMatchPerson(person, account))
.findFirst()
.map(AccountRegister::getImAccount);
}
private static boolean isAccountMatchPerson(PersonAccountAttribute person, AccountRegister account) {
return person.getPersonId().equals(account.getAccountId())
&& person.getAppType().getCode().equals(account.getAppType())
&& Objects.equals(person.getOuId(), account.getOuId());
}
}

View File

@ -27,6 +27,7 @@ import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
import cn.axzo.im.center.common.enums.ChatGroupUserDataSourceEnum;
import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
import cn.axzo.im.center.common.enums.GroupType;
import cn.axzo.im.center.common.enums.OperateLogTypeEnum;
import cn.axzo.im.channel.IMChannelProvider;
import cn.axzo.im.channel.netease.NimChannelService;
@ -46,6 +47,7 @@ import cn.axzo.im.config.JobCodeProperties;
import cn.axzo.im.config.MqProducer;
import cn.axzo.im.dao.mapper.ChatGroupMapper;
import cn.axzo.im.entity.ChatGroup;
import cn.axzo.im.entity.Group;
import cn.axzo.im.event.payload.ChatGroupCreatePayload;
import cn.axzo.im.gateway.OrganizationalNodeUserApiGateway;
import cn.axzo.im.gateway.ProfilesApiGateway;
@ -286,6 +288,16 @@ public class ChatGroupServiceImpl extends ServiceImpl<ChatGroupMapper, ChatGrou
Map<String, String> attachMap = new HashMap<>();
attachMap.put("workspaceId", req.getWorkspaceId().toString());
attachMap.put("workspaceName", workspaceName);
GroupType groupType = null;
if (req.getCrowType() == ChatGroupCreateReq.CrowTypeEnum.OU)
groupType = GroupType.WORKSPACE_OU;
else if (req.getCrowType() == ChatGroupCreateReq.CrowTypeEnum.TEAM)
groupType = GroupType.WORKSPACE_TEAM;
else if (req.getCrowType() == ChatGroupCreateReq.CrowTypeEnum.WORKSPACE)
groupType = GroupType.WORKSPACE;
if (groupType != null) {
attachMap.put(Group.ATTACHMENT_GROUP_TYPE, groupType.getCode());
}
return JSONUtil.toJsonStr(attachMap);//TODO
}

View File

@ -5,7 +5,7 @@ import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.im.center.api.vo.req.UpdateTemplateSendPriorityRequest;
import cn.axzo.im.channel.IMChannelProvider;
import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.NimBatchSendCustomMessageResponse;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchRequest;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse;
import cn.axzo.im.config.MqProducer;
@ -20,9 +20,6 @@ import cn.axzo.im.service.AccountService;
import cn.axzo.im.service.MessageHistoryService;
import cn.axzo.orggateway.api.unit.resp.OrgUnitResp;
import cn.axzo.im.utils.PropsUtils;
import cn.axzo.maokai.api.client.OrganizationalUnitApi;
import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery;
import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO;
import cn.axzo.pokonyan.client.RateLimiter;
import cn.axzo.pokonyan.client.RateLimiterClient;
import cn.axzo.pokonyan.dao.converter.PageConverter;
@ -222,7 +219,7 @@ public class MessageHistoryServiceImpl extends ServiceImpl<MessageHistoryMapper,
@Transactional(rollbackFor = Exception.class)
public void setBatchSendSuccess(List<? extends MessageHistory> histories,
BatchSendCustomMessageResponse response,
NimBatchSendCustomMessageResponse response,
HistoryRecordExt updateExt) {
ArrayList<MessageHistory> updates = new ArrayList<>(histories.size());
for (MessageHistory history : histories) {

View File

@ -3,6 +3,7 @@ package cn.axzo.im.updatable;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.im.center.api.vo.req.RevokeMessageRequest;
import cn.axzo.im.channel.netease.client.NimClient;
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
import cn.axzo.im.dao.repository.MessageHistoryColdDao;
import cn.axzo.im.dao.repository.MessageHistoryDao;
import cn.axzo.im.entity.MessageHistory;
@ -96,12 +97,12 @@ public class RevokeService {
}
private void revoke(NimMessageHistory history) {
cn.axzo.im.channel.netease.dto.RevokeMessageRequest nimRequest =
new cn.axzo.im.channel.netease.dto.RevokeMessageRequest();
NimRevokeMessageRequest nimRequest =
new NimRevokeMessageRequest();
nimRequest.setMessageId(history.getMessageId());
nimRequest.setFrom(history.getFromAccount());
nimRequest.setTo(history.getToAccount());
NimClient.CodeResponse resp = nimClient.revoke(nimRequest);
NimClient.NimCodeResponse resp = nimClient.revoke(nimRequest);
log.info("revoke messageId={}, code={}, desc={}", history.getMessageId(), resp.getCode(), resp.getDesc());
}

View File

@ -0,0 +1,32 @@
package cn.axzo.im.utils;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yanglin
*/
public class ImAccountParser {
private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z]+(\\d+)_cm(p?)_?(\\d+)?$");
public static Optional<PersonAccountAttribute> parsePerson(String imAccount) {
Matcher matcher = PATTERN.matcher(imAccount);
if (!matcher.find())
return Optional.empty();
String personId = matcher.group(1);
AppTypeEnum appType = matcher.group(2).isEmpty() ? AppTypeEnum.CM : AppTypeEnum.CMP;
String ouId = matcher.group(3) != null ? matcher.group(3) : null;
PersonAccountAttribute person = new PersonAccountAttribute();
person.setPersonId(personId);
person.setOuId(ouId != null ? Long.parseLong(ouId.substring(1)) : null);
person.setAppType(appType);
return Optional.of(person);
}
}

View File

@ -1,7 +1,7 @@
package cn.axzo.im.channel.netease.client;
import cn.axzo.im.Application;
import cn.axzo.im.channel.netease.dto.RevokeMessageRequest;
import cn.axzo.im.channel.netease.dto.NimRevokeMessageRequest;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -18,11 +18,11 @@ class NimClientTest {
@Test
void revoke() {
RevokeMessageRequest request = new RevokeMessageRequest();
NimRevokeMessageRequest request = new NimRevokeMessageRequest();
request.setMessageId("13017220665628");
request.setFrom("eac6c28d888c4cca87683e5c75a34ec4");
request.setTo("master177117_cmp_20350");
NimClient.CodeResponse revoke = nimClient.revoke(request);
NimClient.NimCodeResponse revoke = nimClient.revoke(request);
System.out.println();
}

View File

@ -0,0 +1,55 @@
package cn.axzo.im.group;
import cn.axzo.im.Application;
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.resp.GroupAddMembersResponse;
import cn.axzo.im.center.api.vo.resp.GroupCreateResponse;
import cn.axzo.im.center.common.enums.GroupType;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author yanglin
*/
@SpringBootTest(classes = Application.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
class GroupManagerTest {
private final GroupManager groupManager;
@Test
void createGroup() {
GroupCreateRequest request = new GroupCreateRequest();
request.setGroupType(GroupType.VISA);
request.setBizCode("yl-group-2");
request.setName("杨林测试群2");
request.setOwner(PersonAccountAttribute.cmp(9000399522L, 10616L));
request.addMember(PersonAccountAttribute.cm(9000399522L));
request.setAvatar("https://axzo-app.oss-cn-chengdu.aliyuncs.com/dev/common/92c412eb7e62542998cc10059fa7619e6.jpg");
GroupCreateResponse group = groupManager.createGroup(request);
System.out.println(group);
}
@Test
void dismissGroup() {
GroupDismissRequest request = new GroupDismissRequest();
request.setTid(32504762462L);
groupManager.dismissGroup(request);
}
@Test
void addMembers() {
GroupAddMembersRequest request = new GroupAddMembersRequest();
request.setTid(32505385179L);
request.addMember(PersonAccountAttribute.cmp(13335L, 8507L));
request.addMember(PersonAccountAttribute.cmp(13335000000L, 8507L));
GroupAddMembersResponse response = groupManager.addMembers(request);
System.out.println(response);
}
}