diff --git a/im-center-api/pom.xml b/im-center-api/pom.xml
index 415ad9d..594b434 100644
--- a/im-center-api/pom.xml
+++ b/im-center-api/pom.xml
@@ -48,6 +48,10 @@
cn.axzo.apollo
apollo-api
+
+ event-hub-api
+ cn.axzo.event-hub
+
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ChatGroupApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ChatGroupApi.java
index a2b75a2..0bc9aaa 100644
--- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ChatGroupApi.java
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ChatGroupApi.java
@@ -2,7 +2,9 @@ package cn.axzo.im.center.api.feign;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupQueryReq;
import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import cn.axzo.im.center.api.vo.resp.ChatGroupQueryResp;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@@ -23,5 +25,11 @@ public interface ChatGroupApi {
@PostMapping("api/im/chat/group/create")
ApiResult chatGroupCreate(@RequestBody @Validated ChatGroupCreateReq chatGroupCreateReq);
+ /**
+ * 获取群聊
+ */
+ @PostMapping("api/im/chat/group/query")
+ ApiResult chatGroupQuery(@RequestBody @Validated ChatGroupQueryReq chatGroupQueryReq);
+
}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ComplaintApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ComplaintApi.java
index fd995ab..6271754 100644
--- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ComplaintApi.java
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/ComplaintApi.java
@@ -2,6 +2,7 @@ package cn.axzo.im.center.api.feign;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ComplaintCreateReq;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@@ -20,7 +21,7 @@ public interface ComplaintApi {
* 投诉
*/
@PostMapping("api/im/complaint/create")
- ApiResult complaintCreate(@RequestBody @Validated ChatGroupCreateReq chatGroupCreateReq);
+ ApiResult complaintCreate(@RequestBody @Validated ComplaintCreateReq req);
}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupCreateReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupCreateReq.java
index 94a60de..0bf731a 100644
--- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupCreateReq.java
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupCreateReq.java
@@ -1,8 +1,10 @@
package cn.axzo.im.center.api.vo.req;
+import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
+import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
@@ -21,7 +23,7 @@ import java.io.Serializable;
public class ChatGroupCreateReq implements Serializable {
/**
- * 群类型,项目专属群:workspace
+ * 群类型,项目专属群:WORKSPACE
*/
@NotEmpty(message = "群类型不能为空")
private String groupType;
@@ -46,13 +48,33 @@ public class ChatGroupCreateReq implements Serializable {
private Long workspaceId;
/**
- * 人群类型,项目:workspace,单位:ou,班组:team
+ * 人群类型,项目:WORKSPACE,单位:OU,班组:TEAM
*/
- @NotEmpty(message = "人群类型不能为空")
- private String crowType;
+ @NotNull(message = "人群类型不能为空")
+ private CrowTypeEnum crowType;
/**
* 群主账号,accid,最大长度 32 位字符
*/
private Long owner;
+
+ /**
+ * 创建人
+ */
+ private Long creator;
+
+ private Long ouId;
+
+ @Getter
+ @AllArgsConstructor(access = AccessLevel.PRIVATE)
+ public enum CrowTypeEnum {
+ WORKSPACE("workspace", "项目"),
+ OU("ou", "单位"),
+ TEAM("team", "班组"),
+ ;
+
+ private String code;
+
+ private String desc;
+ }
}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupGenericSearchReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupGenericSearchReq.java
new file mode 100644
index 0000000..7b10bc0
--- /dev/null
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupGenericSearchReq.java
@@ -0,0 +1,22 @@
+package cn.axzo.im.center.api.vo.req;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatGroupGenericSearchReq {
+
+ private Long workspaceId;
+
+ private Long ouId;
+
+ private Long teamId;
+
+ private ChatGroupCreateReq.CrowTypeEnum crowType;
+
+}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupListReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupListReq.java
new file mode 100644
index 0000000..c99d5e8
--- /dev/null
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupListReq.java
@@ -0,0 +1,4 @@
+package cn.axzo.im.center.api.vo.req;
+
+public class ChatGroupListReq {
+}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupQueryReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupQueryReq.java
new file mode 100644
index 0000000..e7f06df
--- /dev/null
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupQueryReq.java
@@ -0,0 +1,36 @@
+package cn.axzo.im.center.api.vo.req;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 群聊创建请求
+ * @author xudawei
+ * @date 2024/11/04
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatGroupQueryReq implements Serializable {
+
+ /**
+ * 群 ID 列表,如["3083","3084"],一次最多查询 10 个群,最大长度 1024 位字符
+ */
+ @NotEmpty(message = "tids不能为空")
+ private List tids;
+
+
+ /**
+ * 1,表示带上群成员列表;0,表示不带群成员列表,只返回群信息
+ */
+ @NotNull(message = "ope不能为空")
+ private Integer ope;
+}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupUserGenericSearchReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupUserGenericSearchReq.java
new file mode 100644
index 0000000..e11ae80
--- /dev/null
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ChatGroupUserGenericSearchReq.java
@@ -0,0 +1,59 @@
+package cn.axzo.im.center.api.vo.req;
+
+import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatGroupUserGenericSearchReq {
+
+ /**
+ * 项目Id
+ */
+ private Long workspaceId;
+ /**
+ * 单位Id
+ */
+ private Long ouId;
+
+ /**
+ * 班组Id
+ */
+ private Long teamId;
+
+ /**
+ * 人群
+ */
+ private ChatGroupCreateReq.CrowTypeEnum crowType;
+
+ /**
+ * 群聊Id
+ */
+ private Long chatGroupId;
+
+ /**
+ * 网易云信唯一标识,网易返回
+ */
+ private String tid;
+ /**
+ * IM账号Id
+ */
+ private String accId;
+
+ /**
+ * 类型,OWNER:群主;USER:普通用户
+ */
+ private ChatGroupUserTypeEnum type;
+
+ /**
+ * 状态,SUCCESS:成功;FAIL:失败
+ */
+ private ChatGroupStatusEnum status;
+
+}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ComplaintCreateReq.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ComplaintCreateReq.java
index 9e37b6a..f16a8cf 100644
--- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ComplaintCreateReq.java
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/ComplaintCreateReq.java
@@ -1,8 +1,10 @@
package cn.axzo.im.center.api.vo.req;
+import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
+import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
@@ -25,14 +27,39 @@ public class ComplaintCreateReq {
private String complaintContent;
/**
- * 类型,私聊:private,群聊:group
+ * 类型,私聊:PRIVATE,群聊:GROUP
*/
@NotNull(message = "类型不能为空")
- private String type;
+ private ChatTypeEnum type;
+
+ /**
+ * 发送人
+ */
+ @NotNull(message = "发送人不能为空")
+ private String fromId;
/**
* 群聊Id或者IM账号Id,根据type而订
*/
@NotNull(message = "群聊Id或者IM账号Id,不能为空")
- private String t_acc_id;
+ private String taccId;
+
+ /**
+ * 创建人
+ */
+ private Long creator;
+
+ @Getter
+ @AllArgsConstructor(access = AccessLevel.PRIVATE)
+ public enum ChatTypeEnum {
+ PRIVATE("private", "私聊"),
+ GROUP("group", "群聊"),
+ ;
+
+ private String code;
+
+ private String desc;
+
+ }
+
}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupCreateResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupCreateResp.java
index 8878b5a..0cbaa7b 100644
--- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupCreateResp.java
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupCreateResp.java
@@ -17,8 +17,33 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class ChatGroupCreateResp {
+ /**
+ * code
+ */
+ private Integer code;
+
+ /**
+ * 网易云信服务器产生,群唯一标识
+ */
private String tid;
private JSONArray faccid;
+ /**
+ * 群名称
+ */
+ private String groupName;
+
+ /**
+ * 群头像
+ */
+ private String avatarUrl;
+
+ /**
+ * 描述
+ */
+ private String desc;
+
+
+
}
diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupQueryResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupQueryResp.java
new file mode 100644
index 0000000..1ddf4ed
--- /dev/null
+++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/ChatGroupQueryResp.java
@@ -0,0 +1,161 @@
+package cn.axzo.im.center.api.vo.resp;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 创建群聊
+ * @author xudawei@axzo.cn
+ * @date 2024/11/04
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatGroupQueryResp {
+
+ /**
+ * 状态码
+ */
+ private Integer code;
+
+ /**
+ * 网易云信服务器产生,群唯一标识
+ */
+ private List tinfos;
+
+ /**
+ * 描述
+ */
+ private String desc;
+
+ @Data
+ @Builder
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class TInfo {
+ /**
+ * 群名称
+ */
+ private String tname;
+ /**
+ * 群头像
+ */
+ private String icon;
+
+ /**
+ * 群主用户帐号
+ */
+ private String owner;
+ /**
+ * 群成员最大数量
+ */
+ private Integer maxusers;
+
+ /**
+ * 群 ID
+ */
+ private Long tid;
+
+ /**
+ * 当前群成员数量
+ */
+ private Integer size;
+
+ /**
+ * 群公告
+ */
+ private String announcement;
+
+ /**
+ * 群介绍
+ */
+ private String intro;
+
+ /**
+ * 申请入群的验证方式
+ * 0,不用验证;1,需要验证;2,不允许任何人加入
+ */
+ private Integer joinmode;
+
+ /**
+ * 群创建完成后,邀请入群时是否需要被邀请人的同意
+ * 0,需要同意(默认);1,不需要同意
+ */
+ private Integer beinvitemode;
+
+ /**
+ * 邀请权限,即谁可以邀请他人入群
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer invitemode;
+
+ /**
+ * 客户端修改群信息权限,即谁可以修改群信息
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer uptinfomode;
+
+ /**
+ * 客户端修改群自定义属性权限,即谁可以修改群自定义属性
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer upcustommode;
+
+ /**
+ * 群禁言类型
+ * 0,解除禁言;1,禁言普通成员;3,禁言整个群(包括群主)
+ */
+ private Integer muteType;
+
+ /**
+ * 群通知消息是否关闭在线发送(开启该功能才会有该字段)
+ */
+ private Boolean isNotifyCloseOnline;
+
+ /**
+ * 群通知消息是否关闭持久化存储(开启该功能才会有该字段)
+ */
+ private Boolean isNotifyClosePersistent;
+
+ /**
+ * 自定义高级群扩展属性
+ */
+ private String custom;
+
+ /**
+ * 客户端自定义字段
+ */
+ private String clientCustom;
+
+ /**
+ * 是否全员禁言
+ */
+ private Boolean mute;
+
+ /**
+ * 管理员账号
+ */
+ private String admins;
+
+ /**
+ * 群成员列表
+ */
+ private String members;
+
+ /**
+ * 创建时间
+ */
+ private Long createtime;
+
+ /**
+ * 更新时间
+ */
+ private Long updatetime;
+ }
+
+}
diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupStatusEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupStatusEnum.java
new file mode 100644
index 0000000..77b7c4e
--- /dev/null
+++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupStatusEnum.java
@@ -0,0 +1,32 @@
+package cn.axzo.im.center.common.enums;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 群聊状态
+ *
+ * @author xudawei
+ * @date 2024/11/12
+ */
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum ChatGroupStatusEnum {
+
+ /**
+ * 成功
+ */
+ SUCCESS("success", "成功"),
+ /**
+ * 失败
+ */
+ FAIL("fail", "失败"),
+ ;
+
+ private final String code;
+
+ private final String message;
+
+}
diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupUserTypeEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupUserTypeEnum.java
new file mode 100644
index 0000000..0be9a77
--- /dev/null
+++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChatGroupUserTypeEnum.java
@@ -0,0 +1,32 @@
+package cn.axzo.im.center.common.enums;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 群聊用户类型
+ *
+ * @author xudawei
+ * @date 2024/11/13
+ */
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum ChatGroupUserTypeEnum {
+
+ /**
+ * 群主
+ */
+ OWNER("owner", "群主"),
+ /**
+ * 普通用户
+ */
+ USER("user", "普通用户"),
+ ;
+
+ private final String code;
+
+ private final String message;
+
+}
diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/OperateLogTypeEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/OperateLogTypeEnum.java
new file mode 100644
index 0000000..a11897b
--- /dev/null
+++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/OperateLogTypeEnum.java
@@ -0,0 +1,45 @@
+package cn.axzo.im.center.common.enums;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 群聊状态
+ *
+ * @author xudawei
+ * @date 2024/11/12
+ */
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum OperateLogTypeEnum {
+
+ /**
+ * 群聊新增
+ */
+ GROUP_ADD("groupAdd", "群聊新增"),
+ /**
+ * 群聊解散
+ */
+ GROUP_DELETE("groupDelete", "群聊解散"),
+ /**
+ * 人员新增群聊
+ */
+ USER_ENTER_CHAT_GROUP("userEnterChatGroup", "人员进入群聊"),
+ /**
+ * 人员退出群聊
+ */
+ USER_EXIT_CHAT_GROUP("userExitChatGroup", "人员退出群聊"),
+
+ /**
+ * 更改群主
+ */
+ CHANGE_OWNER("changeOwner", "更改群主"),
+ ;
+
+ private final String code;
+
+ private final String message;
+
+}
diff --git a/im-center-server/pom.xml b/im-center-server/pom.xml
index cf7e363..b7a35b3 100644
--- a/im-center-server/pom.xml
+++ b/im-center-server/pom.xml
@@ -109,6 +109,12 @@
cn.axzo.maokai
maokai-api
+
+
+ event-hub-api
+ cn.axzo.event-hub
+
+
@@ -125,6 +131,12 @@
cn.axzo.tyr
tyr-api
+
+
+ com.aliyun
+ alibaba-dingtalk-service-sdk
+ 2.0.0
+
diff --git a/im-center-server/src/main/java/cn/axzo/im/ImCenterDevApplication.java b/im-center-server/src/main/java/cn/axzo/im/ImCenterDevApplication.java
new file mode 100644
index 0000000..002c829
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/ImCenterDevApplication.java
@@ -0,0 +1,56 @@
+package cn.axzo.im;
+
+import cn.axzo.framework.data.mybatisplus.config.MybatisPlusAutoConfiguration;
+import cn.axzo.im.config.RocketMQEventConfiguration;
+import lombok.extern.slf4j.Slf4j;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.env.Environment;
+
+@Slf4j
+//@SpringBootApplication(scanBasePackages = "cn.axzo", exclude = MybatisPlusAutoConfiguration.class)
+//@EnableFeignClients(basePackages = {"cn.axzo"})
+//@MapperScan(value = {"cn.axzo.im.dao.mapper"})
+//@EnableDiscoveryClient
+//@Import(RocketMQEventConfiguration.class)
+public class ImCenterDevApplication {
+ public static void main(String[] args) {
+ System.setProperty("spring.profiles.active","dev");
+ System.setProperty("NACOS_HOST","https://dev-nacos.axzo.cn");
+ System.setProperty("NACOS_PORT","443");
+ System.setProperty("NACOS_NAMESPACE_ID","35eada10-9574-4db8-9fea-bc6a4960b6c7");
+ System.setProperty("CUSTOM_ENV","dev");
+
+ System.setProperty("spring.redis.port","6379");
+ System.setProperty("spring.redis.host","172.16.2.219");
+ System.setProperty("spring.redis.password","!rHV2!fctYtV4vF");
+ System.setProperty("xxl.job.admin.addresses","http://dev-xxl-job.axzo.cn/xxl-job-admin");
+ System.setProperty("rocketmq.name-server", "172.16.2.82:9876");
+ SpringApplication application = new SpringApplication(ImCenterDevApplication.class);
+ ApplicationContext applicationContext = application.run(args);
+ Environment env = applicationContext.getEnvironment();
+ log.info(
+ "--------------------------------------------------------------------------------------------------------------------\n" +
+ "Application 【{}】 is running on 【{}】 environment!\n" +
+ "Api Local: \thttp://127.0.0.1:{}\n" +
+ "Mysql: \t{}\t username:{}\n" +
+ "Redis: \t{}:{}\t database:{}\n" +
+ "RabbitMQ: \t{}\t username:{}",
+ env.getProperty("spring.application.name"),
+ env.getProperty("spring.profiles.active"),
+ env.getProperty("server.port"),
+ env.getProperty("spring.datasource.url"),
+ env.getProperty("spring.datasource.username"),
+ env.getProperty("spring.redis.host"),
+ env.getProperty("spring.redis.port"),
+ env.getProperty("spring.redis.database"),
+ env.getProperty("spring.rabbitmq.addresses"),
+ env.getProperty("spring.rabbitmq.username") +
+ "\n----------------------------------------------------------");
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/IMChannelProvider.java b/im-center-server/src/main/java/cn/axzo/im/channel/IMChannelProvider.java
index b0963b6..6a38a0d 100644
--- a/im-center-server/src/main/java/cn/axzo/im/channel/IMChannelProvider.java
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/IMChannelProvider.java
@@ -1,7 +1,11 @@
package cn.axzo.im.channel;
+import cn.axzo.im.channel.netease.dto.ChangeOwnerRequest;
+import cn.axzo.im.channel.netease.dto.ChangeOwnerResponse;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateRequest;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateResponse;
+import cn.axzo.im.channel.netease.dto.ChatGroupQueryResponse;
+import cn.axzo.im.channel.netease.dto.KickChatGroupRequest;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchRequest;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse;
import cn.axzo.im.channel.netease.dto.MessageCustomDispatchRequest;
@@ -15,6 +19,7 @@ import cn.axzo.im.channel.netease.dto.UserAddChatGroupRequest;
import cn.axzo.im.channel.netease.dto.UserAddChatGroupResponse;
import javax.validation.Valid;
+import java.util.List;
/**
* im-center
@@ -80,6 +85,11 @@ public interface IMChannelProvider {
*/
RegisterResponse updateAccountProfile(RegisterUpdateRequest updateProfile);
+ /**
+ * 踢人出群
+ */
+ UserAddChatGroupResponse kickChatGroup(KickChatGroupRequest request);
+
/**
* 创建群聊
*/
@@ -89,4 +99,16 @@ public interface IMChannelProvider {
* 用户加入群聊
*/
UserAddChatGroupResponse userAddChatGroup(UserAddChatGroupRequest request);
+
+ /**
+ * 获取群聊
+ * @param tids 群 ID 列表
+ * @param ope 1,表示带上群成员列表;0,表示不带群成员列表,只返回群信息
+ */
+ ChatGroupQueryResponse chatGroupQueryByTids(List tids, Integer ope);
+
+ /**
+ * 转让群主
+ */
+ ChangeOwnerResponse changeOwner(ChangeOwnerRequest request);
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java
index 652e0eb..e3ec40e 100644
--- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java
@@ -3,8 +3,12 @@ package cn.axzo.im.channel.netease;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.im.channel.IMChannelProvider;
+import cn.axzo.im.channel.netease.dto.ChangeOwnerRequest;
+import cn.axzo.im.channel.netease.dto.ChangeOwnerResponse;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateRequest;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateResponse;
+import cn.axzo.im.channel.netease.dto.ChatGroupQueryResponse;
+import cn.axzo.im.channel.netease.dto.KickChatGroupRequest;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchRequest;
import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse;
import cn.axzo.im.channel.netease.dto.MessageCustomDispatchRequest;
@@ -24,6 +28,7 @@ import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import lombok.extern.slf4j.Slf4j;
@@ -34,7 +39,9 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -68,6 +75,21 @@ public class NimChannelService implements IMChannelProvider {
*/
private static final String USER_ADD_CHAT_GROUP = "https://api.netease.im/nimserver/team/add.action";
+ /**
+ * 踢人出群
+ */
+ private static final String KICK_CHAT_GROUP = "https://api.netease.im/nimserver/team/kick.action";
+
+ /**
+ * 获取群聊
+ */
+ private static final String CHAT_GROUP_QUERY = "https://api.netease.im/nimserver/team/query.action";
+
+ /**
+ * 转让群主
+ */
+ private static final String CHANGE_OWNER = "https://api.netease.im/nimserver/team/changeOwner.action";
+
public static final int SUCCESS_CODE = 200;
public static final int RATE_LIMITED_CODE = 416;
@@ -75,10 +97,6 @@ public class NimChannelService implements IMChannelProvider {
private static final int NIM_ACCOUNT_ALREADY_REGISTER = 414;
private static final String PROVIDER_NAME = "NIM";
- /**
- * 消息接收者的网易云信IM账号(accid)列表上限是500人,但是返回msgid最多100人,故目前设置100人
- */
- private static final int NIM_MSG_BATCH_MAX_NUM = 100;
/**
* 目前支持 100、自定义消息
*/
@@ -359,19 +377,22 @@ public class NimChannelService implements IMChannelProvider {
HttpResponse response = HttpRequest.post(CHAT_GROUP_CREATE).addHeaders(authHeaderMap)
.form(paramMap).timeout(5000).execute();
String result = response.body();
- if (response.getStatus() == SUCCESS_CODE) {
- ChatGroupCreateResponse chatGroupCreateResponse = JSONUtil.toBean(result, ChatGroupCreateResponse.class);
- if (chatGroupCreateResponse == null) {
- return ChatGroupCreateResponse.builder().desc("chatGroupCreate-请求网易云信Server异常[" + result + "],请联系管理员!").build();
- }
- if (chatGroupCreateResponse.getCode() != SUCCESS_CODE) {
- log.warn("chatGroupCreate-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_CREATE, result);
- }
- return chatGroupCreateResponse;
- } else {
+ log.info("chatGroupCreate-请求网易云信,result:{}", result);
+ if (response.getStatus() != SUCCESS_CODE) {
log.error("chatGroupCreate-请求网易云信Server:{},异常:{}", CHAT_GROUP_CREATE, result);
+ ChatGroupCreateResponse chatGroupCreateResponse = JSONUtil.toBean(result, ChatGroupCreateResponse.class);
+ throw new ServiceException("创建群聊异常," + chatGroupCreateResponse.getDesc());
}
- return ChatGroupCreateResponse.builder().desc("chatGroupCreate-请求网易云信Server异常,请联系管理员!").build();
+ ChatGroupCreateResponse chatGroupCreateResponse = JSONUtil.toBean(result, ChatGroupCreateResponse.class);
+ if (chatGroupCreateResponse == null) {
+ log.warn("chatGroupCreate-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_CREATE, result);
+ throw new ServiceException("创建群聊异常," + chatGroupCreateResponse.getDesc());
+ }
+ if (chatGroupCreateResponse.getCode() != SUCCESS_CODE) {
+ log.warn("chatGroupCreate-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_CREATE, result);
+ throw new ServiceException("创建群聊异常," + chatGroupCreateResponse.getDesc());
+ }
+ return chatGroupCreateResponse;
}
/**
@@ -399,6 +420,10 @@ public class NimChannelService implements IMChannelProvider {
*/
@Override
public UserAddChatGroupResponse userAddChatGroup(UserAddChatGroupRequest request) {
+ if (Objects.isNull(request) || CollectionUtils.isEmpty(request.getMembers())) {
+ log.info("userAddChatGroup-请求网易云信,members is empty");
+ return UserAddChatGroupResponse.builder().build();
+ }
//构建用户加入群聊
HashMap paramMap = this.buildUserAddChatGroup(request);
@@ -408,21 +433,67 @@ public class NimChannelService implements IMChannelProvider {
HttpResponse response = HttpRequest.post(USER_ADD_CHAT_GROUP).addHeaders(authHeaderMap)
.form(paramMap).timeout(5000).execute();
String result = response.body();
- if (response.getStatus() == SUCCESS_CODE) {
- UserAddChatGroupResponse userAddChatGroupResponse = JSONUtil.toBean(result, UserAddChatGroupResponse.class);
- if (userAddChatGroupResponse == null) {
- return UserAddChatGroupResponse.builder().desc("userAddChatGroup-请求网易云信Server异常[" + result + "],请联系管理员!").build();
- }
- if (userAddChatGroupResponse.getCode() != SUCCESS_CODE) {
- log.warn("userAddChatGroup-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_CREATE, result);
- }
- return userAddChatGroupResponse;
- } else {
+ log.info("userAddChatGroup-请求网易云信,result:{}", result);
+ if (response.getStatus() != SUCCESS_CODE) {
log.error("userAddChatGroup-请求网易云信Server:{},异常:{}", CHAT_GROUP_CREATE, result);
+ throw new ServiceException("userAddChatGroup-请求网易云信Server异常,result:" + result);
}
- return UserAddChatGroupResponse.builder().desc("userAddChatGroup-请求网易云信Server异常,请联系管理员!").build();
+ UserAddChatGroupResponse userAddChatGroupResponse = JSONUtil.toBean(result, UserAddChatGroupResponse.class);
+ if (userAddChatGroupResponse == null) {
+ throw new ServiceException("userAddChatGroup-请求网易云信Server异常[" + result + "],请联系管理员!");
+ }
+ if (userAddChatGroupResponse.getCode() != SUCCESS_CODE) {
+ log.warn("userAddChatGroup-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_CREATE, result);
+ throw new ServiceException(userAddChatGroupResponse.getCode(), "userAddChatGroup-请求网易云信Server异常,result:" + userAddChatGroupResponse.getDesc());
+ }
+
+ return userAddChatGroupResponse;
}
+ /**
+ * 踢人出群
+ */
+ @Override
+ public UserAddChatGroupResponse kickChatGroup(KickChatGroupRequest request) {
+ //构建用户加入群聊
+ HashMap paramMap = this.buildKickChatGroup(request);
+
+ Map authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
+ log.info("kickChatGroup-请求网易云信,URL:{},Header:{},请求参数:{}", KICK_CHAT_GROUP,
+ JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
+ HttpResponse response = HttpRequest.post(KICK_CHAT_GROUP).addHeaders(authHeaderMap)
+ .form(paramMap).timeout(5000).execute();
+ String result = response.body();
+ log.info("kickChatGroup-请求网易云信,result:{}", result);
+ if (response.getStatus() != SUCCESS_CODE) {
+ log.error("kickChatGroup-请求网易云信Server:{},异常:{}", KICK_CHAT_GROUP, result);
+ throw new ServiceException("kickChatGroup-请求网易云信Server:" + KICK_CHAT_GROUP +",异常:" + result);
+ }
+
+ UserAddChatGroupResponse kickChatGroupResponse = JSONUtil.toBean(result, UserAddChatGroupResponse.class);
+ if (kickChatGroupResponse == null) {
+ throw new ServiceException("kickChatGroup-请求网易云信Server异常[" + result + "],请联系管理员!");
+ }
+ if (kickChatGroupResponse.getCode() != SUCCESS_CODE) {
+ log.warn("kickChatGroup-请求网易云信Server:{},返回异常:{}", KICK_CHAT_GROUP, result);
+ throw new ServiceException(kickChatGroupResponse.getCode(), "kickChatGroup-请求网易云信Server:"+KICK_CHAT_GROUP+",返回异常:" + result);
+ }
+ return kickChatGroupResponse;
+ }
+
+ /**
+ * 构建创建群聊对象
+ */
+ private HashMap buildKickChatGroup(KickChatGroupRequest request) {
+ HashMap paramMap = Maps.newHashMap();
+ paramMap.put("tid", request.getTid());
+ paramMap.put("owner", request.getOwner());
+ paramMap.put("members", request.getMembers());
+ paramMap.put("attach",request.getAttach());
+ return paramMap;
+ }
+
+
/**
* 构建创建群聊对象
*/
@@ -430,10 +501,91 @@ public class NimChannelService implements IMChannelProvider {
HashMap paramMap = Maps.newHashMap();
paramMap.put("tid", request.getTid());
paramMap.put("owner", request.getOwner());
- paramMap.put("members", request.getMembers());
+ paramMap.put("members", JSON.toJSONString(request.getMembers()));
paramMap.put("magree", 0);
paramMap.put("msg",request.getMsg());
return paramMap;
}
+
+ @Override
+ public ChatGroupQueryResponse chatGroupQueryByTids(List tids, Integer ope) {
+ //构建创建群聊对象
+ HashMap paramMap = this.buildChatGroupQuery(tids, ope);
+
+ Map authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
+ log.info("chatGroupQuery-请求网易云信,URL:{},Header:{},请求参数:{}", CHAT_GROUP_QUERY,
+ JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
+ HttpResponse response = HttpRequest.post(CHAT_GROUP_QUERY).addHeaders(authHeaderMap)
+ .form(paramMap).timeout(5000).execute();
+ String result = response.body();
+ log.info("chatGroupQuery-请求网易云信,result:{}", result);
+ if (response.getStatus() != SUCCESS_CODE) {
+ log.error("chatGroupQuery-请求网易云信Server:{},异常:{}", CHAT_GROUP_QUERY, result);
+ return ChatGroupQueryResponse.builder().desc("chatGroupQuery-请求网易云信Server异常,请联系管理员!").build();
+ }
+
+ ChatGroupQueryResponse chatGroupQueryResponse = JSONUtil.toBean(result, ChatGroupQueryResponse.class);
+ if (chatGroupQueryResponse == null) {
+ return ChatGroupQueryResponse.builder().desc("chatGroupQuery-请求网易云信Server异常[" + result + "],请联系管理员!").build();
+ }
+ if (chatGroupQueryResponse.getCode() != SUCCESS_CODE) {
+ log.warn("chatGroupQuery-请求网易云信Server:{},返回异常:{}", CHAT_GROUP_QUERY, result);
+ }
+ return chatGroupQueryResponse;
+ }
+
+ /**
+ * 构建获取群聊对象
+ */
+ private HashMap buildChatGroupQuery(List tids, Integer ope) {
+ HashMap paramMap = Maps.newHashMap();
+ paramMap.put("tids", JSON.toJSONString(tids));
+ paramMap.put("ope", ope);
+ return paramMap;
+ }
+
+ /**
+ * 转让群主
+ */
+ public ChangeOwnerResponse changeOwner(ChangeOwnerRequest request) {
+ //构建创建群聊对象
+ HashMap paramMap = this.buildChangeOwner(request);
+
+ Map authHeaderMap = buildAuthHeader(getProviderAppKey(), getProviderAppSecret());
+ log.info("changeOwner-请求网易云信,URL:{},Header:{},请求参数:{}", CHANGE_OWNER,
+ JSONUtil.toJsonStr(authHeaderMap), JSONUtil.toJsonStr(paramMap));
+ HttpResponse response = HttpRequest.post(CHANGE_OWNER).addHeaders(authHeaderMap)
+ .form(paramMap).timeout(5000).execute();
+ String result = response.body();
+ log.info("changeOwner-请求网易云信,result:{}", result);
+ if (response.getStatus() != SUCCESS_CODE) {
+ log.error("changeOwner-请求网易云信Server:{},异常:{}", CHANGE_OWNER, result);
+ throw new ServiceException("changeOwner-请求网易云信Server:" + KICK_CHAT_GROUP +",异常:" + result);
+ }
+ ChangeOwnerResponse changeOwnerResponse = JSONUtil.toBean(result, ChangeOwnerResponse.class);
+ if (changeOwnerResponse == null) {
+ log.error("changeOwner-请求网易云信Server:{},异常:{}", CHANGE_OWNER, result);
+ throw new ServiceException("changeOwner-请求网易云信Server:" + KICK_CHAT_GROUP +",异常:" + result);
+ }
+ if (changeOwnerResponse.getCode() != SUCCESS_CODE) {
+ log.error("changeOwner-请求网易云信Server:{},异常:{}", CHANGE_OWNER, result);
+ throw new ServiceException("changeOwner-请求网易云信Server:" + KICK_CHAT_GROUP +",异常:" + result);
+ }
+ return changeOwnerResponse;
+ }
+
+ /**
+ * 构建转让群主
+ */
+ private HashMap buildChangeOwner(ChangeOwnerRequest request) {
+ HashMap paramMap = Maps.newHashMap();
+ paramMap.put("tid", request.getTid());
+ paramMap.put("owner", request.getOwner());
+ paramMap.put("newowner", request.getNewowner());
+ paramMap.put("leave", request.getLeave());
+ paramMap.put("attach", request.getAttach());
+ return paramMap;
+ }
+
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerRequest.java
new file mode 100644
index 0000000..3be6c2f
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerRequest.java
@@ -0,0 +1,41 @@
+package cn.axzo.im.channel.netease.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 转让群主
+ * 文档地址:https://doc.yunxin.163.com/messaging/server-apis/jM0MjQzODA?platform=server
+ * @author xudawei@axzo.cn
+ * @date 2024/11/08
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class ChangeOwnerRequest {
+ /**
+ * 云信服务器产生,群组唯一标识,创建群时会返回,最大长度 64 位长整型
+ */
+ private String tid;
+ /**
+ * 邀请人的用户帐号,accid,最大长度 32 位字符,按照群属性 invitemode(0:只有群主和管理员可以邀请他人入群,1:所有人都可以邀请)配置传入
+ */
+ private String owner;
+ /**
+ * 新群主帐号,accid,最大长度 32 位字符
+ */
+ private String newowner;
+ /**
+ * 1,群主身份转让后离开群;2,群主身份转让后成为普通成员
+ */
+ private Integer leave;
+
+ /**
+ * 自定义扩展字段,最大长度 512 位字符
+ */
+ private String attach;
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerResponse.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerResponse.java
new file mode 100644
index 0000000..4783691
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChangeOwnerResponse.java
@@ -0,0 +1,32 @@
+package cn.axzo.im.channel.netease.dto;
+
+import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 转让群主
+ * @author xudawei@axzo.cn
+ * @date 2024/11/08
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChangeOwnerResponse {
+
+ /**
+ * 状态码
+ */
+ private Integer code;
+
+ /**
+ * 描述
+ */
+ private String desc;
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateRequest.java
index 6b4f341..31e3770 100644
--- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateRequest.java
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateRequest.java
@@ -1,6 +1,8 @@
package cn.axzo.im.channel.netease.dto;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -47,17 +49,23 @@ public class ChatGroupCreateRequest {
*/
private String icon;
+ /**
+ * 自定义扩展字段,最大长度 512 位字符
+ */
+ private String attach;
+
/**
* 对象转换
*/
- public static ChatGroupCreateRequest convertRequest(ChatGroupCreateReq req, String owner) {
+ public static ChatGroupCreateRequest convertRequest(ChatGroupCreateReq req, String owner, String attach) {
return ChatGroupCreateRequest.builder()
.tname(req.getGroupName())
.owner(owner)
- .members(owner)
+ .members(JSON.toJSONString(Lists.newArrayList(owner)))
.msg("进入群聊," + req.getGroupName())
.icon(req.getAvatarUrl())
+ .attach(attach)
.build();
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateResponse.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateResponse.java
index a24d75b..9cc9572 100644
--- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateResponse.java
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupCreateResponse.java
@@ -42,9 +42,11 @@ public class ChatGroupCreateResponse {
private String desc;
- public static ChatGroupCreateResp convertResp(ChatGroupCreateResponse response) {
+ public static ChatGroupCreateResp convertResp(ChatGroupCreateResponse response, String groupName, String avatarUrl) {
return ChatGroupCreateResp.builder()
.tid(response.getTid())
+ .groupName(groupName)
+ .avatarUrl(avatarUrl)
.build();
}
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupQueryResponse.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupQueryResponse.java
new file mode 100644
index 0000000..c550461
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/ChatGroupQueryResponse.java
@@ -0,0 +1,163 @@
+package cn.axzo.im.channel.netease.dto;
+
+import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import com.alibaba.fastjson.JSONArray;
+import io.swagger.models.auth.In;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 获取群聊
+ * @author xudawei@axzo.cn
+ * @date 2024/11/08
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatGroupQueryResponse {
+
+ /**
+ * 状态码
+ */
+ private Integer code;
+
+ /**
+ * 网易云信服务器产生,群唯一标识
+ */
+ private List tinfos;
+
+ /**
+ * 描述
+ */
+ private String desc;
+
+ @Data
+ @Builder
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class TInfo {
+ /**
+ * 群名称
+ */
+ private String tname;
+ /**
+ * 群头像
+ */
+ private String icon;
+
+ /**
+ * 群主用户帐号
+ */
+ private String owner;
+ /**
+ * 群成员最大数量
+ */
+ private Integer maxusers;
+
+ /**
+ * 群 ID
+ */
+ private Long tid;
+
+ /**
+ * 当前群成员数量
+ */
+ private Integer size;
+
+ /**
+ * 群公告
+ */
+ private String announcement;
+
+ /**
+ * 群介绍
+ */
+ private String intro;
+
+ /**
+ * 申请入群的验证方式
+ * 0,不用验证;1,需要验证;2,不允许任何人加入
+ */
+ private Integer joinmode;
+
+ /**
+ * 群创建完成后,邀请入群时是否需要被邀请人的同意
+ * 0,需要同意(默认);1,不需要同意
+ */
+ private Integer beinvitemode;
+
+ /**
+ * 邀请权限,即谁可以邀请他人入群
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer invitemode;
+
+ /**
+ * 客户端修改群信息权限,即谁可以修改群信息
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer uptinfomode;
+
+ /**
+ * 客户端修改群自定义属性权限,即谁可以修改群自定义属性
+ * 0,群主和管理员(默认);1,所有人
+ */
+ private Integer upcustommode;
+
+ /**
+ * 群禁言类型
+ * 0,解除禁言;1,禁言普通成员;3,禁言整个群(包括群主)
+ */
+ private Integer muteType;
+
+ /**
+ * 群通知消息是否关闭在线发送(开启该功能才会有该字段)
+ */
+ private Boolean isNotifyCloseOnline;
+
+ /**
+ * 群通知消息是否关闭持久化存储(开启该功能才会有该字段)
+ */
+ private Boolean isNotifyClosePersistent;
+
+ /**
+ * 自定义高级群扩展属性
+ */
+ private String custom;
+
+ /**
+ * 客户端自定义字段
+ */
+ private String clientCustom;
+
+ /**
+ * 是否全员禁言
+ */
+ private Boolean mute;
+
+ /**
+ * 管理员账号
+ */
+ private String admins;
+
+ /**
+ * 群成员列表
+ */
+ private String members;
+
+ /**
+ * 创建时间
+ */
+ private Long createtime;
+
+ /**
+ * 更新时间
+ */
+ private Long updatetime;
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/KickChatGroupRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/KickChatGroupRequest.java
new file mode 100644
index 0000000..23a7891
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/KickChatGroupRequest.java
@@ -0,0 +1,43 @@
+package cn.axzo.im.channel.netease.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 踢人出群
+ * 文档地址:https://doc.yunxin.163.com/messaging/server-apis/TY4MDA5ODE?platform=server
+ * @author xudawei@axzo.cn
+ * @date 2024/11/08
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class KickChatGroupRequest {
+ /**
+ * 云信服务器产生,群组唯一标识,创建群时会返回,最大长度 64 位长整型
+ */
+ private String tid;
+ /**
+ * 邀请人的用户帐号,accid,最大长度 32 位字符,按照群属性 invitemode(0:只有群主和管理员可以邀请他人入群,1:所有人都可以邀请)配置传入
+ */
+ private String owner;
+
+ /**
+ * 当移除单个成员时必填
+ * 被移除的用户账号 accid,最大长度 32 位字符
+ * 相对于 members,优先使用 member 参数
+ */
+ private String member;
+ /**
+ * 被邀请入群的用户列表,\["aaa","bbb"\](JSONArray 对应的 accid,如果解析出错会报 414),一次最多邀请 200 个成员
+ */
+ private String members;
+ /**
+ * 自定义扩展字段,最大长度 512 位字符
+ */
+ private String attach;
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/UserAddChatGroupRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/UserAddChatGroupRequest.java
index 9259a49..9196ab5 100644
--- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/UserAddChatGroupRequest.java
+++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/UserAddChatGroupRequest.java
@@ -5,6 +5,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import java.util.List;
+
/**
* 用户加入群聊
* 文档地址:https://doc.yunxin.163.com/messaging/server-apis/DA2MzQ0MzA?platform=server
@@ -27,13 +29,13 @@ public class UserAddChatGroupRequest {
/**
* 被邀请入群的用户列表,\["aaa","bbb"\](JSONArray 对应的 accid,如果解析出错会报 414),一次最多邀请 200 个成员
*/
- private String members;
+ private List members;
/**
* 邀请发送的文字,最大长度 150 位字符
*/
private String msg;
- public static UserAddChatGroupRequest buildRequest(String tid, String owner, String members, String msg) {
+ public static UserAddChatGroupRequest buildRequest(String tid, String owner, List members, String msg) {
return UserAddChatGroupRequest.builder()
.tid(tid)
.owner(owner)
diff --git a/im-center-server/src/main/java/cn/axzo/im/config/BizResultCode.java b/im-center-server/src/main/java/cn/axzo/im/config/BizResultCode.java
index 669b1fa..cdb21c6 100644
--- a/im-center-server/src/main/java/cn/axzo/im/config/BizResultCode.java
+++ b/im-center-server/src/main/java/cn/axzo/im/config/BizResultCode.java
@@ -14,7 +14,16 @@ public enum BizResultCode implements ResultCode {
ALL_PERSSON_TYPE_NOT_EMPTY("103", "全员发送时,接收端不能为空"),
ACQUIRE_RATE_LIMITER_FAIL("104", "获取滑动窗口令牌失败"),
MESSAGE_TASK_STATUS_ERROR("105", "更新消息任务失败,状态异常"),
- MESSAGE_TASK_NOT_FOUND("106", "消息任务不存在"),;
+ MESSAGE_TASK_NOT_FOUND("106", "消息任务不存在"),
+
+ /**
+ * 群聊
+ */
+ CHAT_GROUP_ALREADY_EXISTS("501", "群聊已经存在"),
+ CHAT_GROUP_OVER_MEMBERS_COUNT_LIMIT("502", "超过人数限制"),
+
+
+ ;
private String errorCode;
diff --git a/im-center-server/src/main/java/cn/axzo/im/config/ChatGroupConfig.java b/im-center-server/src/main/java/cn/axzo/im/config/ChatGroupConfig.java
new file mode 100644
index 0000000..adefb05
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/config/ChatGroupConfig.java
@@ -0,0 +1,29 @@
+package cn.axzo.im.config;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 群聊配置
+ *
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ */
+@Slf4j
+@Configuration
+@RefreshScope
+@Data
+public class ChatGroupConfig {
+ //TODO
+ @Value("${chatgroup.dingTalkBotAccessToken:1}")
+ private String dingTalkBotAccessToken;
+ //TODO
+ @Value("${chatgroup.dingTalkBotSecret:1}")
+ private String dingTalkBotSecret;
+ //TODO
+ @Value("${chatgroup.dingTalkBotEnabled:false}")
+ private Boolean dingTalkBotEnabled;
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/config/RocketMQEventConfiguration.java b/im-center-server/src/main/java/cn/axzo/im/config/RocketMQEventConfiguration.java
index eb99e40..9c106d7 100644
--- a/im-center-server/src/main/java/cn/axzo/im/config/RocketMQEventConfiguration.java
+++ b/im-center-server/src/main/java/cn/axzo/im/config/RocketMQEventConfiguration.java
@@ -92,7 +92,7 @@ public class RocketMQEventConfiguration {
@Slf4j
@Component
- @RocketMQMessageListener(topic = "topic_profile_${spring.profiles.active}",
+ @RocketMQMessageListener(topic = "topic_im_center_${spring.profiles.active}",
consumerGroup = "GID_chat_group_create_${spring.application.name}_${spring.profiles.active}",
consumeMode = ConsumeMode.ORDERLY,
nameServer = "${rocketmq.name-server}"
@@ -108,4 +108,61 @@ public class RocketMQEventConfiguration {
super.onEvent(message, eventConsumer);
}
}
+
+ @Slf4j
+ @Component
+ @RocketMQMessageListener(topic = "topic_profile_${spring.profiles.active}",
+ consumerGroup = "GID_chat_group_create_${spring.application.name}_${spring.profiles.active}",
+ consumeMode = ConsumeMode.ORDERLY,
+ nameServer = "${rocketmq.name-server}"
+ )
+ public static class PersonProfileAvatarUpdateListener extends BaseListener implements RocketMQListener {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Override
+ public void onMessage(MessageExt message) {
+ log.info("PersonProfileAvatarUpdateListener onMessage,message:{}", JSON.toJSONString(message));
+ super.onEvent(message, eventConsumer);
+ }
+ }
+
+ @Slf4j
+ @Component
+ @RocketMQMessageListener(topic = "topic_organizational_${spring.profiles.active}",
+ consumerGroup = "GID_chat_group_create_${spring.application.name}_${spring.profiles.active}",
+ consumeMode = ConsumeMode.ORDERLY,
+ nameServer = "${rocketmq.name-server}"
+ )
+ public static class OrganizationalNodeUserChangeListener extends BaseListener implements RocketMQListener {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Override
+ public void onMessage(MessageExt message) {
+ log.info("OrganizationalNodeUserChangeListener onMessage,message:{}", JSON.toJSONString(message));
+ super.onEvent(message, eventConsumer);
+ }
+ }
+
+ @Slf4j
+ @Component
+ @RocketMQMessageListener(topic = "topic_tyr_${spring.profiles.active}",
+ consumerGroup = "GID_chat_group_change_owner_${spring.application.name}_${spring.profiles.active}",
+ consumeMode = ConsumeMode.ORDERLY,
+ nameServer = "${rocketmq.name-server}"
+ )
+ public static class ChatGroupChangeOwnerListener extends BaseListener implements RocketMQListener {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Override
+ public void onMessage(MessageExt message) {
+ log.info("ChatGroupChangeOwnerListener onMessage,message:{}", JSON.toJSONString(message));
+ super.onEvent(message, eventConsumer);
+ }
+ }
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/ChatGroupController.java b/im-center-server/src/main/java/cn/axzo/im/controller/ChatGroupController.java
index 2a5a5a1..93781be 100644
--- a/im-center-server/src/main/java/cn/axzo/im/controller/ChatGroupController.java
+++ b/im-center-server/src/main/java/cn/axzo/im/controller/ChatGroupController.java
@@ -3,13 +3,14 @@ package cn.axzo.im.controller;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.feign.ChatGroupApi;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupQueryReq;
import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
-import cn.axzo.im.channel.netease.dto.ChatGroupCreateRequest;
-import cn.axzo.im.channel.netease.dto.ChatGroupCreateResponse;
-import cn.axzo.im.service.AccountService;
+import cn.axzo.im.center.api.vo.resp.ChatGroupQueryResp;
import cn.axzo.im.service.ChatGroupService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@@ -29,6 +30,15 @@ public class ChatGroupController implements ChatGroupApi {
@Override
public ApiResult chatGroupCreate(ChatGroupCreateReq req) {
- return ApiResult.ok(chatGroupService.chatGroupCreate(req));
+ return chatGroupService.chatGroupCreate(req);
+ }
+
+
+ /**
+ * 获取群聊
+ */
+ @Override
+ public ApiResult chatGroupQuery(@RequestBody @Validated ChatGroupQueryReq req) {
+ return ApiResult.ok(chatGroupService.chatGroupQuery(req));
}
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/ComplaintController.java b/im-center-server/src/main/java/cn/axzo/im/controller/ComplaintController.java
index 05697b1..825d8ef 100644
--- a/im-center-server/src/main/java/cn/axzo/im/controller/ComplaintController.java
+++ b/im-center-server/src/main/java/cn/axzo/im/controller/ComplaintController.java
@@ -4,9 +4,12 @@ import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.feign.ChatGroupApi;
import cn.axzo.im.center.api.feign.ComplaintApi;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ComplaintCreateReq;
import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import cn.axzo.im.service.ComplaintService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -19,9 +22,12 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class ComplaintController implements ComplaintApi {
+ @Autowired
+ private ComplaintService complaintService;
@Override
- public ApiResult complaintCreate(ChatGroupCreateReq chatGroupCreateReq) {
- return null;
+ public ApiResult complaintCreate(ComplaintCreateReq req) {
+ complaintService.complaintCreate(req);
+ return ApiResult.ok();
}
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java
deleted file mode 100644
index ddded85..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package cn.axzo.im.dao.mapper;
-
-import cn.axzo.im.entity.BizData;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.springframework.stereotype.Repository;
-
-/**
- * @author yanglin
- */
-public interface BizDataMapper extends BaseMapper {
-}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java
deleted file mode 100644
index dff7def..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package cn.axzo.im.dao.mapper;
-
-import cn.axzo.im.entity.BizLog;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * @author yanglin
- */
-public interface BizLogMapper extends BaseMapper {
-}
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ChatGroupUserMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ChatGroupUserMapper.java
new file mode 100644
index 0000000..7a3fef5
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ChatGroupUserMapper.java
@@ -0,0 +1,12 @@
+package cn.axzo.im.dao.mapper;
+
+import cn.axzo.im.entity.ChatGroupUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/05
+ * @desc 群聊
+ */
+public interface ChatGroupUserMapper extends BaseMapper {
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ComplaintMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ComplaintMapper.java
new file mode 100644
index 0000000..536eac2
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/ComplaintMapper.java
@@ -0,0 +1,13 @@
+package cn.axzo.im.dao.mapper;
+
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.entity.Complaint;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/05
+ * @desc 群聊
+ */
+public interface ComplaintMapper extends BaseMapper {
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/OperateLogMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/OperateLogMapper.java
new file mode 100644
index 0000000..1e0f2e1
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/OperateLogMapper.java
@@ -0,0 +1,12 @@
+package cn.axzo.im.dao.mapper;
+
+import cn.axzo.im.entity.OperateLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ * @desc 操作日志
+ */
+public interface OperateLogMapper extends BaseMapper {
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java
deleted file mode 100644
index 945c1ab..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package cn.axzo.im.dao.repository;
-
-import cn.axzo.im.dao.mapper.BizDataMapper;
-import cn.axzo.im.entity.BizData;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.stereotype.Repository;
-
-/**
- * @author yanglin
- */
-@Repository("bizDataDao")
-public class BizDataDao extends ServiceImpl {
-}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java
deleted file mode 100644
index 7e8bd4e..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package cn.axzo.im.dao.repository;
-
-import cn.axzo.im.dao.mapper.BizLogMapper;
-import cn.axzo.im.entity.BizLog;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.stereotype.Repository;
-
-/**
- * @author yanglin
- */
-@Repository("bizLogDao")
-public class BizLogDao extends ServiceImpl {
-}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/BizData.java b/im-center-server/src/main/java/cn/axzo/im/entity/BizData.java
deleted file mode 100644
index 7558742..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/entity/BizData.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package cn.axzo.im.entity;
-
-import cn.axzo.im.center.common.enums.AppTypeEnum;
-import cn.axzo.im.utils.YesNo;
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.Date;
-
-/**
- * @author yanglin
- */
-@Setter
-@Getter
-@TableName(value = "im_biz_data", autoResultMap = true)
-public class BizData {
-
- private Long id;
- private String bizId;
- private Long taskId;
- private Long receiverPersonId;
- private Long receiverOuId;
- private AppTypeEnum appType;
- private YesNo valid;
- private String bizMessageId;
- private Long initHistoryId;
- private Long updateHistoryId;
- private Long retryHistoryId;
- private JSONObject messageBody;
- private JSONObject bizBody;
- private Long dataVersion;
- private Long ackDataVersion;
- private Date ackTime;
- private Integer retryCount;
- private RecordExt recordExt;
- private Long isDelete;
- private Date createAt;
- private Date updateAt;
-
- @Getter
- @Setter
- public static class RecordExt {
- }
-
-}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java
deleted file mode 100644
index 9257147..0000000
--- a/im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.axzo.im.entity;
-
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.Date;
-
-/**
- * @author yanglin
- */
-@Setter
-@Getter
-@TableName(value = "im_biz_log", autoResultMap = true)
-public class BizLog {
- private Long id;
- private String bizId;
- private String bizMessageId;
- private Long initHistoryId;
- private Long updateHistoryId;
- private JSONObject messageBody;
- private JSONObject bizBody;
- private Long dataVersion;
- private Long isDelete;
- private Date createAt;
- private Date updateAt;
-}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroup.java b/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroup.java
index 436e677..574f9b8 100644
--- a/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroup.java
+++ b/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroup.java
@@ -1,5 +1,6 @@
package cn.axzo.im.entity;
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -47,45 +48,57 @@ public class ChatGroup implements Serializable {
* 群头像
*/
@TableField("avatar_url")
- private String avatar_url;
+ private String avatarUrl;
/**
* 项目Id
*/
@TableField("workspace_id")
- private Long workspace_id;
+ private Long workspaceId;
+
+ /**
+ * 单位Id
+ */
+ @TableField("ou_id")
+ private Long ouId;
+
+ /**
+ * 班组Id
+ */
+ @TableField("team_id")
+ private Long teamId;
/**
* 群类型,项目专属群:workspace
*/
@TableField("group_type")
- private String group_type;
+ private String groupType;
/**
* 人群类型,项目:workspace,单位:ou,班组:team
*/
@TableField("crow_type")
- private String crow_type;
-
- /**
- * 关联id,单位Id或者班组Id或者项目Id
- */
- @TableField("relate_id")
- private Long relate_id;
-
- /**
- * 群主名称
- */
- @TableField("group_owner_name")
- private String group_owner_name;
+ private ChatGroupCreateReq.CrowTypeEnum crowType;
/**
* 群主
*/
@TableField("group_owner")
- private Long group_owner;
+ private String groupOwner;
+
+ /**
+ * 状态,成功:success;失败:fail
+ */
+ @TableField("status")
+ private String status;
+
+ /**
+ * 备注,目前记录创建群失败原因
+ */
+ @TableField("remark")
+ private String remark;
/**
* 创建人
@@ -97,7 +110,7 @@ public class ChatGroup implements Serializable {
* 是否删除
*/
@TableField("is_delete")
- private Integer isDelete;
+ private Long isDelete;
/**
* 创建时间
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupUser.java b/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupUser.java
new file mode 100644
index 0000000..e084477
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupUser.java
@@ -0,0 +1,103 @@
+package cn.axzo.im.entity;
+
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 群聊成员
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ */
+@TableName("im_chat_group_user")
+@Data
+@SuperBuilder
+@Accessors(chain = true)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ChatGroupUser implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 群聊Id
+ */
+ @TableField("chat_group_id")
+ private Long chatGroupId;
+
+ /**
+ * 网易云信唯一标识,网易返回
+ */
+ @TableField("tid")
+ private String tid;
+
+ /**
+ * IM账号Id
+ */
+ @TableField("acc_id")
+ private String accId;
+
+
+ /**
+ * 类型,OWNER:群主;USER:普通用户
+ */
+ @TableField("type")
+ private ChatGroupUserTypeEnum type;
+
+ /**
+ * 状态,success:成功;fail:失败
+ */
+ @TableField("status")
+ private ChatGroupStatusEnum status;
+
+ /**
+ * 状态,success:成功;fail:失败
+ */
+ @TableField("crow_type")
+ private ChatGroupCreateReq.CrowTypeEnum crowType;
+
+ /**
+ * 备注,目前记录群成员失败加入群原因
+ */
+ @TableField("remark")
+ private String remark;
+
+ /**
+ * 创建人
+ */
+ @TableField("creator")
+ private Long creator;
+
+ /**
+ * 是否删除,0:未删除;非0:已删除
+ */
+ @TableField("is_delete")
+ private Long isDelete;
+
+ /**
+ * 创建时间
+ */
+ @TableField("create_at")
+ private Date createAt;
+
+ /**
+ * 更新时间
+ */
+ @TableField("update_at")
+ private Date updateAt;
+}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/Complaint.java b/im-center-server/src/main/java/cn/axzo/im/entity/Complaint.java
index 561b198..9f75e3e 100644
--- a/im-center-server/src/main/java/cn/axzo/im/entity/Complaint.java
+++ b/im-center-server/src/main/java/cn/axzo/im/entity/Complaint.java
@@ -1,5 +1,6 @@
package cn.axzo.im.entity;
+import cn.axzo.im.center.api.vo.req.ComplaintCreateReq;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -41,31 +42,26 @@ public class Complaint implements Serializable {
* 类型,私聊:private,群聊:group
*/
@TableField("type")
- private String type;
+ private ComplaintCreateReq.ChatTypeEnum type;
/**
* 发送人Id
*/
@TableField("from_id")
- private Long fromId;
-
- /**
- * 发送人名称
- */
- @TableField("from_name")
- private String fromName;
+ private String fromId;
/**
* 接收人Id
*/
@TableField("to_id")
- private Long toId;
+ private String toId;
/**
- * 接收人名称
+ * 群Id
*/
- @TableField("to_name")
- private String toName;
+ @TableField("tid")
+ private String tid;
+
/**
* 创建人
@@ -77,7 +73,7 @@ public class Complaint implements Serializable {
* 是否删除
*/
@TableField("is_delete")
- private Integer isDelete;
+ private Long isDelete;
/**
* 创建时间
diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupChangeLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/OperateLog.java
similarity index 72%
rename from im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupChangeLog.java
rename to im-center-server/src/main/java/cn/axzo/im/entity/OperateLog.java
index 40bb8f7..4307c82 100644
--- a/im-center-server/src/main/java/cn/axzo/im/entity/ChatGroupChangeLog.java
+++ b/im-center-server/src/main/java/cn/axzo/im/entity/OperateLog.java
@@ -1,5 +1,6 @@
package cn.axzo.im.entity;
+import cn.axzo.im.center.common.enums.OperateLogTypeEnum;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -14,47 +15,28 @@ import java.io.Serializable;
import java.util.Date;
/**
- * 群聊日志
+ * 操作日志
* @author xudawei@axzo.cn
* @date 2024/11/05
*/
-@TableName("im_chat_group_change_log")
+@TableName("im_operate_log")
@Data
@SuperBuilder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
-public class ChatGroupChangeLog implements Serializable {
+public class OperateLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
- /**
- * 群聊名称
- */
- @TableField("name")
- private String name;
-
- /**
- * 群聊Id
- */
- @TableField("group_chat_id")
- private String group_chat_id;
-
- /**
- * 项目Id
- */
- @TableField("workspace_id")
- private Long workspace_id;
-
-
/**
* 变更类型,群变更:groupAdd;群变更:groupUpdate;人员变更:userAdd;人员变更:userUpdate
*/
- @TableField("change_type")
- private String change_type;
+ @TableField("type")
+ private OperateLogTypeEnum type;
/**
* 变更内容
@@ -72,7 +54,7 @@ public class ChatGroupChangeLog implements Serializable {
* 是否删除
*/
@TableField("is_delete")
- private Integer isDelete;
+ private Long isDelete;
/**
* 创建时间
diff --git a/im-center-server/src/main/java/cn/axzo/im/event/inner/EventTypeEnum.java b/im-center-server/src/main/java/cn/axzo/im/event/inner/EventTypeEnum.java
index e65cd63..a56f97e 100644
--- a/im-center-server/src/main/java/cn/axzo/im/event/inner/EventTypeEnum.java
+++ b/im-center-server/src/main/java/cn/axzo/im/event/inner/EventTypeEnum.java
@@ -14,6 +14,13 @@ 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", "群聊创建"),
+ UPDATE_AVATAR("profile", "update-avatar", "头像更新"),
+ NODE_USER_CREATE("node-user", "node-user-create", "节点用户创建"),
+ NODE_USER_UPDATE("node-user", "node-user-update", "节点用户修改"),
+ NODE_USER_DELETE("node-user", "node-user-delete", "节点用户删除"),
+ NODE_USER_UPSERTED("node-user", "node-user-upserted", "节点用户更新"),
+ SAAS_ROLE_USER_RELATION_REMOVED("saas-role-user-relation", "saas-role-user-relation-removed", "删除用户角色信息"),
+ SAAS_ROLE_USER_RELATION_UPSERT("saas-role-user-relation", "saas-role-user-relation-upsert", "更新用户角色信息"),
;
EventTypeEnum(String model, String name, String desc) {
diff --git a/im-center-server/src/main/java/cn/axzo/im/event/payload/ChatGroupCreatePayload.java b/im-center-server/src/main/java/cn/axzo/im/event/payload/ChatGroupCreatePayload.java
index 3f71ae3..d484821 100644
--- a/im-center-server/src/main/java/cn/axzo/im/event/payload/ChatGroupCreatePayload.java
+++ b/im-center-server/src/main/java/cn/axzo/im/event/payload/ChatGroupCreatePayload.java
@@ -20,4 +20,7 @@ public class ChatGroupCreatePayload implements Serializable {
private String owner;
+ private String imAccountOwner;
+
+ private Long chatGroupId;
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/event/payload/OrganizationalNodeUserPayload.java b/im-center-server/src/main/java/cn/axzo/im/event/payload/OrganizationalNodeUserPayload.java
new file mode 100644
index 0000000..bb1b96f
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/event/payload/OrganizationalNodeUserPayload.java
@@ -0,0 +1,131 @@
+package cn.axzo.im.event.payload;
+
+import cn.axzo.maokai.api.vo.response.NodeUserPersonGroupLike;
+import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
+import cn.axzo.trade.datasecurity.core.annotation.CryptField;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.springframework.beans.BeanUtils;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 组织人员表表实体类
+ *
+ * @author makejava
+ * @since 2022-06-05 10:59:31
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class OrganizationalNodeUserPayload{
+
+ private static final long serialVersionUID = 297636361732939329L;
+
+ /**
+ * identity_id
+ */
+ private Long identityId;
+
+ /**
+ * 身份类型 0-无效类型, 1-工人, 2-班组长, 3-从业人员, 4-政务人员,5-运营人员
+ */
+ private Integer identityType;
+
+ /**
+ * 自然人id
+ */
+ private Long personId;
+
+ /**
+ * 主电话
+ */
+ private String phone;
+
+ /**
+ * 名字
+ */
+ private String realName;
+
+ /**
+ * 身份证号
+ */
+ private String idNumber;
+
+ /**
+ * 单位id
+ */
+ private Long organizationalUnitId;
+
+ /**
+ * 组织节点id
+ */
+ private Long organizationalNodeId;
+
+ /**
+ * 岗位id
+ */
+ private Long organizationalJobId;
+
+ /**
+ * 是否是主岗位
+ * 0:普通岗位、1:主岗位
+ */
+ private Integer primaryJob;
+ /**
+ * 是否允许进入工地 1.允许 2.不允许
+ */
+ private Integer isAllowed;
+
+ /**
+ * 加入时间
+ */
+ private Date joinAt;
+
+ /**
+ * 是否是部门管理员
+ */
+ private Boolean manager;
+
+ /**
+ * 离开时间
+ */
+ private Date leaveAt;
+
+ /**
+ * 迁移数据临时源id
+ */
+ private Long tempSourceId;
+
+ /**
+ * 顶级节点的nodeId
+ */
+ private Long topNodeId;
+
+ /** 数据同步ID **/
+ private Long syncDataId;
+
+ /** 工号 - 需要作为查询条件和排序 不使用扩展字段**/
+ private String jobNumber;
+
+ /**
+ * workspace id
+ */
+ private Long workspaceId;
+
+ private String groupNodeIds = "";
+
+ private String groupJobIds = "";
+
+ private String groupIdentityIds = "";
+}
+
diff --git a/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationRemovePayload.java b/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationRemovePayload.java
new file mode 100644
index 0000000..9b53f02
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationRemovePayload.java
@@ -0,0 +1,19 @@
+package cn.axzo.im.event.payload;
+
+import cn.axzo.basics.auth.dto.SaasRoleUserRelation;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SaasRoleUserRelationRemovePayload {
+
+ private List values;
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationUpsertPayload.java b/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationUpsertPayload.java
new file mode 100644
index 0000000..1b099f5
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/event/payload/SaasRoleUserRelationUpsertPayload.java
@@ -0,0 +1,30 @@
+package cn.axzo.im.event.payload;
+
+import cn.axzo.basics.auth.dto.SaasRoleUserRelation;
+import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.beans.BeanUtils;
+
+import java.util.List;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SaasRoleUserRelationUpsertPayload {
+
+ private List oldValues;
+
+ private List newValues;
+
+ public static SaasRoleUserRelation from(SaasRoleUserV2DTO saasRoleUserV2DTO) {
+ SaasRoleUserRelation result = new SaasRoleUserRelation();
+ BeanUtils.copyProperties(saasRoleUserV2DTO, result);
+ result.setNaturalPersonId(saasRoleUserV2DTO.getSaasRoleUser().getPersonId());
+ return result;
+ }
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/OrgJobApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/OrgJobApiGateway.java
new file mode 100644
index 0000000..38bc8c3
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/OrgJobApiGateway.java
@@ -0,0 +1,29 @@
+package cn.axzo.im.gateway;
+
+import cn.axzo.im.utils.BizAssertions;
+import cn.axzo.maokai.api.client.OrgJobApi;
+import cn.axzo.maokai.api.vo.request.OrgJobListReq;
+import cn.axzo.maokai.api.vo.response.OrgJobRes;
+import cn.axzo.pokonyan.util.RpcUtil;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service("orgJobApiGateway")
+@Slf4j
+@RequiredArgsConstructor
+public class OrgJobApiGateway {
+
+ private final OrgJobApi orgJobApi;
+
+ public OrgJobRes fetchJobCodeByJobId(Long jobId) {
+ OrgJobListReq req = new OrgJobListReq();
+ req.setJobIdList(Lists.newArrayList(jobId));
+ List orgJobRes = RpcUtil.rpcApiResultProcessor(() -> orgJobApi.list(req), "fetchJobCodeByJobId", jobId);
+ BizAssertions.assertNotEmpty(orgJobRes, "根据jobId:{}获取Job为空", jobId);
+ return orgJobRes.get(0);
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java
new file mode 100644
index 0000000..01d8ca9
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java
@@ -0,0 +1,29 @@
+package cn.axzo.im.gateway;
+
+import cn.axzo.maokai.api.client.OrganizationalNodeApi;
+import cn.axzo.maokai.api.vo.request.OrganizationalNodeBatchQueryVO;
+import cn.axzo.maokai.api.vo.response.OrganizationalNodeVO;
+import cn.axzo.pokonyan.util.RpcUtil;
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service("organizationalNodeApiGateway")
+@Slf4j
+@RequiredArgsConstructor
+public class OrganizationalNodeApiGateway {
+
+ private final OrganizationalNodeApi organizationalNodeApi;
+
+ public List fetchNodesByNodeIds(List nodeIdList) {
+ OrganizationalNodeBatchQueryVO organizationalNodeBatchQueryVO = new OrganizationalNodeBatchQueryVO();
+ organizationalNodeBatchQueryVO.setNodeIds(nodeIdList);
+ organizationalNodeBatchQueryVO.setContainsDeleted(false);
+ List nodeVOList = organizationalNodeApi.listNode(organizationalNodeBatchQueryVO).getData();
+ return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeApi.listNode(organizationalNodeBatchQueryVO), "fetchNodesByNodeIds", JSON.toJSONString(nodeVOList));
+ }
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeUserApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeUserApiGateway.java
new file mode 100644
index 0000000..327f004
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeUserApiGateway.java
@@ -0,0 +1,54 @@
+package cn.axzo.im.gateway;
+
+import cn.axzo.maokai.api.client.OrganizationalNodeUserApi;
+import cn.axzo.maokai.api.vo.request.OrganizationalNodeUserSearchReq;
+import cn.axzo.maokai.api.vo.response.OrganizationalNodeUserVO;
+import cn.axzo.pokonyan.util.RpcUtil;
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Set;
+
+@Service("organizationalNodeUserApiGateway")
+@Slf4j
+@RequiredArgsConstructor
+public class OrganizationalNodeUserApiGateway {
+
+ private final OrganizationalNodeUserApi organizationalNodeUserApi;
+
+ public List searchNodeUser(Long nodeId, Long workspaceId, Set jobCodes, Long personId) {
+ OrganizationalNodeUserSearchReq searchReq = new OrganizationalNodeUserSearchReq();
+ searchReq.setOrganizationNodeId(nodeId);
+ searchReq.setWorkspaceId(workspaceId);
+ searchReq.setJobCodes(jobCodes);
+ searchReq.setPersonId(personId);
+ return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeUserApi.list(searchReq), "searchNodeUser", JSON.toJSONString(searchReq));
+ }
+
+ public List fetchNodeUsersByWorkspaceNodeIdJobCodes(Long workspaceId, Long nodeId,Set jobCodes) {
+ OrganizationalNodeUserSearchReq searchReq = new OrganizationalNodeUserSearchReq();
+ searchReq.setWorkspaceId(workspaceId);
+ searchReq.setOrganizationNodeId(nodeId);
+ searchReq.setJobCodes(jobCodes);//班组长/带班长
+ return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeUserApi.list(searchReq), "fetchNodeUsersByWorkspaceOuIdJobCodes", JSON.toJSONString(searchReq));
+ }
+
+ public List fetchNodeUsersByWorkspaceIdJobCodes(Long workspaceId, Set jobCodes) {
+ OrganizationalNodeUserSearchReq searchReq = new OrganizationalNodeUserSearchReq();
+ searchReq.setWorkspaceId(workspaceId);
+ searchReq.setJobCodes(jobCodes);
+ return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeUserApi.list(searchReq), "fetchNodeUsersByWorkspaceIdJobCodes", JSON.toJSONString(searchReq));
+ }
+
+ public List fetchNodeUsersByWorkspaceOuIdJobCodes(Long workspaceId, Long ouId, Set jobCodes) {
+ OrganizationalNodeUserSearchReq searchReq = new OrganizationalNodeUserSearchReq();
+ searchReq.setWorkspaceId(workspaceId);
+ searchReq.setOrganizationalUnitId(ouId);
+ searchReq.setJobCodes(jobCodes);
+ return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeUserApi.list(searchReq), "fetchNodeUsersByWorkspaceOuIdJobCodes", JSON.toJSONString(searchReq));
+ }
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/ProfilesApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/ProfilesApiGateway.java
index fb099a7..91da4b1 100644
--- a/im-center-server/src/main/java/cn/axzo/im/gateway/ProfilesApiGateway.java
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/ProfilesApiGateway.java
@@ -3,11 +3,16 @@ package cn.axzo.im.gateway;
import cn.axzo.basics.profiles.api.UserProfileServiceApi;
import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.im.entity.dto.PlatPersonDto;
+import cn.axzo.pokonyan.util.RpcUtil;
import cn.azxo.framework.common.logger.MethodAroundLog;
import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
+
+import java.util.List;
import java.util.Objects;
+
+import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
@@ -35,4 +40,12 @@ public class ProfilesApiGateway {
}
return BeanUtil.copyProperties(response.getData(), PlatPersonDto.class);
}
+
+ public PersonProfileDto getPersonProfileById(Long personId) {
+ return RpcUtil.rpcCommonProcessor(() -> userProfileServiceApi.getPersonProfile(personId), "getPersonProfileById", personId);
+ }
+
+ public List getPersonProfilesByIds(List personIds) {
+ return RpcUtil.rpcCommonProcessor(() -> userProfileServiceApi.getPersonProfiles(personIds), "getPersonProfilesByIds", JSON.toJSONString(personIds));
+ }
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/TyrApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/TyrApiGateway.java
new file mode 100644
index 0000000..9e1f30b
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/TyrApiGateway.java
@@ -0,0 +1,31 @@
+package cn.axzo.im.gateway;
+
+import cn.axzo.pokonyan.util.RpcUtil;
+import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
+import cn.axzo.tyr.client.feign.TyrSaasRoleUserApi;
+import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO;
+import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Service("tyrApiGateway")
+@Slf4j
+@RequiredArgsConstructor
+public class TyrApiGateway {
+
+ private final TyrSaasRoleUserApi tyrSaasRoleUserApi;
+
+ public List fetchSaasRoleUserByWorkspaceOuIdRoleTypes(Long workspaceId, Long ouId, Set roleTypeEnumSet) {
+ ArrayList workspaceOuPairs = Lists.newArrayList(ListRoleUserRelationParam.WorkspaceOuPair.builder().workspaceId(workspaceId).ouId(ouId).build());
+ ListRoleUserRelationParam listRoleUserRelationParam = ListRoleUserRelationParam.builder().roleTypes(roleTypeEnumSet).workspaceOuPairs(workspaceOuPairs).build();
+ return RpcUtil.rpcApiResultProcessor(() -> tyrSaasRoleUserApi.roleUserListV2(listRoleUserRelationParam), "fetchSaasRoleUserByWorkspaceOuIdRoleTypes", JSON.toJSONString(listRoleUserRelationParam));
+ }
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/WorkspaceApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/WorkspaceApiGateway.java
new file mode 100644
index 0000000..4f6efc9
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/gateway/WorkspaceApiGateway.java
@@ -0,0 +1,41 @@
+package cn.axzo.im.gateway;
+
+import cn.axzo.apollo.core.web.Result;
+import cn.axzo.apollo.workspace.api.workspace.WorkspaceApi;
+import cn.axzo.apollo.workspace.api.workspace.res.GetDetailRes;
+import cn.axzo.pokonyan.util.RpcUtil;
+import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
+import cn.axzo.tyr.client.feign.TyrSaasRoleUserApi;
+import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO;
+import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Service("workspaceApiGateway")
+@Slf4j
+@RequiredArgsConstructor
+public class WorkspaceApiGateway {
+
+ private final WorkspaceApi workspaceApi;
+
+ public GetDetailRes getWorkspaceById(Long workspaceId) {
+ try {
+ log.info("getWorkspaceById-param:{}", workspaceId);
+ Result result = workspaceApi.getById(workspaceId);
+ log.info("getWorkspaceById-result:{}", JSON.toJSONString(result));
+ return result.getData();
+ } catch (Exception e) {
+ log.error("getWorkspaceById-error", e);
+ throw e;
+ }
+ }
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupChangeOwnerEventHandler.java b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupChangeOwnerEventHandler.java
new file mode 100644
index 0000000..0a3d638
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupChangeOwnerEventHandler.java
@@ -0,0 +1,161 @@
+package cn.axzo.im.handler.chatgroup;
+
+import cn.axzo.basics.auth.dto.SaasRoleUserRelation;
+import cn.axzo.basics.common.exception.ServiceException;
+import cn.axzo.framework.rocketmq.Event;
+import cn.axzo.framework.rocketmq.EventConsumer;
+import cn.axzo.framework.rocketmq.EventHandler;
+import cn.axzo.im.center.api.vo.req.ChatGroupUserGenericSearchReq;
+import cn.axzo.im.center.common.enums.AppTypeEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import cn.axzo.im.entity.ChatGroupUser;
+import cn.axzo.im.event.inner.EventTypeEnum;
+import cn.axzo.im.event.payload.SaasRoleUserRelationRemovePayload;
+import cn.axzo.im.event.payload.SaasRoleUserRelationUpsertPayload;
+import cn.axzo.im.gateway.TyrApiGateway;
+import cn.axzo.im.service.AccountService;
+import cn.axzo.im.service.ChatGroupService;
+import cn.axzo.im.service.ChatGroupUserService;
+import cn.axzo.im.utils.BizAssertions;
+import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
+import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/07
+ * @desc 群聊创建MQ消费
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ChatGroupChangeOwnerEventHandler implements EventHandler, InitializingBean {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Autowired
+ private TyrApiGateway tyrApiGateway;
+
+ @Autowired
+ private ChatGroupUserService chatGroupUserService;
+
+ @Autowired
+ private AccountService accountService;
+
+ @Autowired
+ private ChatGroupService chatGroupService;
+
+ @Override
+ public void onEvent(Event event, EventConsumer.Context context) {
+ if (!EventTypeEnum.SAAS_ROLE_USER_RELATION_REMOVED.getName().equalsIgnoreCase(event.getEventCode().getName())
+ && !EventTypeEnum.SAAS_ROLE_USER_RELATION_UPSERT.getName().equalsIgnoreCase(event.getEventCode().getName())) {
+ return;
+ }
+
+ log.info("ChatGroupChangeOwnerEventHandler-start - handle mq event, event={}", JSON.toJSONString(event));
+ long start = System.currentTimeMillis();
+ try {
+ //角色删除
+ if (EventTypeEnum.SAAS_ROLE_USER_RELATION_REMOVED.getName().equalsIgnoreCase(event.getEventCode().getName())) {
+ this.handleMqMessageRoleRemove(event);
+ log.info("ChatGroupChangeOwnerEventHandler-handle-roleRemove mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start);
+ return;
+ }
+ //角色变更
+ if (EventTypeEnum.SAAS_ROLE_USER_RELATION_UPSERT.getName().equalsIgnoreCase(event.getEventCode().getName())) {
+ this.handleMqMessageRoleUpsert(event);
+ log.info("ChatGroupChangeOwnerEventHandler-handle-upsert mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start);
+ }
+ } catch (Exception e) {
+ log.warn("ChatGroupChangeOwnerEventHandler-handle error mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start, e);
+ }
+
+ }
+
+ /**
+ * 业务逻辑-角色删除
+ */
+ private void handleMqMessageRoleRemove(Event event) {
+ //解析数据
+// SaasRoleUserRelationRemovePayload payload = event.normalizedData(SaasRoleUserRelationRemovePayload.class);
+ }
+
+ /**
+ * 业务逻辑-角色变更
+ * 1 用户是群主的群聊集合
+ * 2 更换群主
+ */
+ private void handleMqMessageRoleUpsert(Event event) {
+ //解析数据
+ SaasRoleUserRelationUpsertPayload payload = event.normalizedData(SaasRoleUserRelationUpsertPayload.class);
+ if (Objects.isNull(payload) || CollectionUtils.isEmpty(payload.getOldValues())) {
+ log.info("ChatGroupChangeOwnerEventHandler-角色变更mq对象为空");
+ return;
+ }
+ for(SaasRoleUserRelation item : payload.getOldValues()) {
+ ChatGroupUserGenericSearchReq req = new ChatGroupUserGenericSearchReq();
+
+ String oldImAccountOwner = accountService.buildUserIdWrapper(item.getNaturalPersonId().toString(), AppTypeEnum.CMP.getCode(), item.getOuId());
+
+ //1 用户是群主的群聊集合
+ req.setAccId(oldImAccountOwner);
+ req.setType(ChatGroupUserTypeEnum.OWNER);
+ List chatGroupUsers = chatGroupUserService.genericQuery(req);
+ if (CollectionUtils.isEmpty(chatGroupUsers)) {
+ log.info("ChatGroupChangeOwnerEventHandler-角色变更,personId:{},没有群是群主", item.getNaturalPersonId());
+ continue;
+ }
+ for(ChatGroupUser chatGroupUser : chatGroupUsers) {
+ switch (chatGroupUser.getCrowType()) {
+ case WORKSPACE:
+ // 2 更换群主
+ this.changeOwner(item.getWorkspaceId(), null, Sets.newHashSet(RoleTypeEnum.SUPER_ADMIN), item, oldImAccountOwner, chatGroupUser);
+ break;
+ case OU:
+ //2 更换群主
+ this.changeOwner(item.getWorkspaceId(), item.getOuId(), Sets.newHashSet(RoleTypeEnum.ADMIN), item, oldImAccountOwner, chatGroupUser);
+ break;
+ case TEAM:
+ break;
+ default:
+ throw new ServiceException("人群不存在");
+ }
+ }
+ }
+ }
+
+ /**
+ * 更换群主
+ */
+ private void changeOwner(Long workspaceId, Long ouId, Set roleTypeEnumSet,SaasRoleUserRelation item, String oldImAccountOwner, ChatGroupUser chatGroupUser) {
+ //获取最新群主
+ List saasRoleUserV2DTOS = tyrApiGateway.fetchSaasRoleUserByWorkspaceOuIdRoleTypes(workspaceId, ouId, roleTypeEnumSet);
+ if (CollectionUtils.isEmpty(saasRoleUserV2DTOS)) {
+ this.chatGroupService.sendDingRobot(String.format("创建群聊,personId:%d在workspaceId:%d,ouId:%d,人群:%s,找不到群主信息", item.getNaturalPersonId(), item.getWorkspaceId(), ouId, chatGroupUser.getCrowType()));
+ }
+ BizAssertions.assertNotEmpty(saasRoleUserV2DTOS, "personId:{}在workspaceId:{},ouId:%d,找不到群主信息", item.getNaturalPersonId(), item.getWorkspaceId(), ouId);
+ SaasRoleUserV2DTO saasRoleUserV2DTOWhenOu = saasRoleUserV2DTOS.get(0);
+ String newImAccountOwnerWhenOu = accountService.buildUserIdWrapper(saasRoleUserV2DTOWhenOu.getSaasRoleUser().getPersonId().toString(), AppTypeEnum.CMP.getCode(), item.getOuId());
+ this.chatGroupService.changeOwner(chatGroupUser.getChatGroupId(), oldImAccountOwner, newImAccountOwnerWhenOu);
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ Event.EventCode removedEventCode = new Event.EventCode(EventTypeEnum.SAAS_ROLE_USER_RELATION_REMOVED.getModel(), EventTypeEnum.SAAS_ROLE_USER_RELATION_REMOVED.getName());
+ Event.EventCode upsertEventCode = new Event.EventCode(EventTypeEnum.SAAS_ROLE_USER_RELATION_UPSERT.getModel(), EventTypeEnum.SAAS_ROLE_USER_RELATION_UPSERT.getName());
+ eventConsumer.registerHandler(removedEventCode, this);
+ eventConsumer.registerHandler(upsertEventCode, this);
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupEventHandler.java b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupEventHandler.java
index bffb40e..bda8086 100644
--- a/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupEventHandler.java
+++ b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/ChatGroupEventHandler.java
@@ -1,28 +1,42 @@
package cn.axzo.im.handler.chatgroup;
+import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
import cn.axzo.im.center.api.vo.req.UserAccountReq;
+import cn.axzo.im.center.api.vo.resp.UserAccountResp;
import cn.axzo.im.center.common.enums.AppTypeEnum;
-import cn.axzo.im.channel.IMChannelProvider;
import cn.axzo.im.channel.netease.INotifyService;
-import cn.axzo.im.channel.netease.dto.UserAddChatGroupRequest;
import cn.axzo.im.event.inner.EventTypeEnum;
import cn.axzo.im.event.payload.ChatGroupCreatePayload;
+import cn.axzo.im.gateway.ProfilesApiGateway;
import cn.axzo.im.service.AccountService;
+import cn.axzo.im.service.ChatGroupService;
+import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
- * @author yanglin
+ * @author xudawei@axzo.cn
+ * @date 2024/11/07
+ * @desc 群聊创建MQ消费
*/
@Slf4j
@Component
@@ -32,8 +46,6 @@ public class ChatGroupEventHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
- @Autowired
- private IMChannelProvider imChannelProvider;
@Autowired
private AccountService accountService;
@@ -41,8 +53,17 @@ public class ChatGroupEventHandler implements EventHandler, InitializingBean {
@Resource
private INotifyService iNotifyService;
+ @Autowired
+ private ChatGroupService chatGroupService;
+
+ @Autowired
+ private ProfilesApiGateway profilesApiGateway;
+
@Override
public void onEvent(Event event, EventConsumer.Context context) {
+ if (!EventTypeEnum.MESSAGE_CHAT_GROUP_CREATE.getName().equalsIgnoreCase(event.getEventCode().getName())) {
+ return;
+ }
log.info("ChatGroupEventHandler-start - handle mq event, event={}", JSON.toJSONString(event));
try {
@@ -51,27 +72,90 @@ public class ChatGroupEventHandler implements EventHandler, InitializingBean {
long end = System.currentTimeMillis();
log.info("ChatGroupEventHandlerend-handle mq event, used={}ms, event={}", end - start, JSON.toJSONString(event));
} catch (Exception e) {
- log.warn("ChatGroupEventHandler-error - handle mq event, event={}", JSON.toJSONString(event));
+ log.warn("ChatGroupEventHandler-error - handle mq event, event={}", JSON.toJSONString(event), e);
}
}
-
+ /**
+ * 业务逻辑
+ */
private void handleMqMessage(Event event) {
//解析数据
ChatGroupCreatePayload payload = event.normalizedData(ChatGroupCreatePayload.class);
ChatGroupCreateReq req = payload.getReq();
+
String tid = payload.getTid();
- String owner = payload.getOwner();
+ String imAccountOwner = payload.getImAccountOwner();
- String members = "";//TODO
- UserAccountReq userAccountReq = UserAccountReq.builder().appType(AppTypeEnum.SYSTEM.getCode()).userId("").nickName("").headImageUrl("").organizationalUnitId(1L).build();
- accountService.generateAccount(userAccountReq, iNotifyService);
+ List members = this.chatGroupOfMembers(req);
+ this.chatGroupService.userAddChatGroup(payload.getChatGroupId(), tid,imAccountOwner,members, req.getGroupName());
+ }
- imChannelProvider.userAddChatGroup(UserAddChatGroupRequest.buildRequest(tid, owner, members,"进入群:" + req.getGroupName()));
+ /**
+ * 构建群成员信息
+ */
+ public List chatGroupOfMembers(ChatGroupCreateReq req) {
+ //Pair,key:管理人员集合;value:工人集合
+ Pair, Set> adminWorkerSet = chatGroupService.fetchUsersByWorkspaceOuId(req.getCrowType(), req.getWorkspaceId(), req.getOuId(), this.chatGroupService.buildJobCodesByCrowType(req.getCrowType()), req.getCreator());
+ Set adminSet = adminWorkerSet.getKey();
+ Set workerSet = adminWorkerSet.getValue();
+
+ Map personProfileMap = this.buildPersonProfileMap(adminWorkerSet);
+
+ List userAccountRespList = Lists.newArrayList();
+
+ for (Long item : adminSet) {
+ PersonProfileDto personProfileDto = personProfileMap.get(item);
+ if (Objects.isNull(personProfileDto) || Objects.isNull(personProfileDto.getId())) {
+ log.info("admin-personProfileDto is null");
+ continue;
+ }
+ UserAccountReq userAccountReq = UserAccountReq.builder()
+ .appType(AppTypeEnum.CMP.getCode())//管理端:AppTypeEnum.CMP.getCode();工人端:AppTypeEnum.CM.getCode()
+ .userId(item.toString()).nickName(personProfileDto.getRealName())
+ .headImageUrl(personProfileDto.getAvatarUrl())
+ .organizationalUnitId(req.getOuId())
+ .build();
+ userAccountRespList.add(accountService.generateAccount(userAccountReq, iNotifyService));
+ }
+
+ for (Long worker : workerSet) {
+ PersonProfileDto personProfileDto = personProfileMap.get(worker);
+ if (Objects.isNull(personProfileDto) || Objects.isNull(personProfileDto.getId())) {
+ log.info("worker-personProfileDto is null");
+ continue;
+ }
+ UserAccountReq userAccountReq = UserAccountReq.builder()
+ .appType(AppTypeEnum.CM.getCode())//管理端:AppTypeEnum.CMP.getCode();工人端:AppTypeEnum.CM.getCode()
+ .userId(worker.toString()).nickName(personProfileDto.getRealName())
+ .headImageUrl(personProfileDto.getAvatarUrl())
+ .organizationalUnitId(req.getOuId())
+ .build();
+ userAccountRespList.add(accountService.generateAccount(userAccountReq, iNotifyService));
+ }
+
+ return userAccountRespList.stream().map(UserAccountResp::getImAccount).collect(Collectors.toList());
+ }
+
+ /**
+ * 构建personProfile的Map
+ * key:id,value:personProfile
+ */
+ private Map buildPersonProfileMap(Pair, Set> adminWorkerSet) {
+
+ Set adminSet = CollectionUtils.isNotEmpty(adminWorkerSet.getKey()) ? adminWorkerSet.getKey() : Sets.newHashSet();
+ Set workerSet = CollectionUtils.isNotEmpty(adminWorkerSet.getValue()) ? adminWorkerSet.getValue() : Sets.newHashSet();
+
+ List adminWorkerList = Lists.newArrayList();
+ adminWorkerList.addAll(Lists.newArrayList(adminSet));
+ adminWorkerList.addAll(Lists.newArrayList(workerSet));
+
+ List personProfileDtoList = profilesApiGateway.getPersonProfilesByIds(adminWorkerList);
+ return personProfileDtoList.stream().collect(Collectors.toMap(PersonProfileDto::getId, Function.identity(), (x, y) -> x));
}
@Override
- public void afterPropertiesSet() throws Exception {
+ public void afterPropertiesSet() {
Event.EventCode eventCode = new Event.EventCode(EventTypeEnum.MESSAGE_CHAT_GROUP_CREATE.getModel(), EventTypeEnum.MESSAGE_CHAT_GROUP_CREATE.getName());
eventConsumer.registerHandler(eventCode, this);
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/OrganizationalNodeUserChangeEventHandler.java b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/OrganizationalNodeUserChangeEventHandler.java
new file mode 100644
index 0000000..c0ff823
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/OrganizationalNodeUserChangeEventHandler.java
@@ -0,0 +1,261 @@
+package cn.axzo.im.handler.chatgroup;
+
+import cn.axzo.apollo.core.service.ServiceException;
+import cn.axzo.framework.rocketmq.Event;
+import cn.axzo.framework.rocketmq.EventConsumer;
+import cn.axzo.framework.rocketmq.EventHandler;
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupGenericSearchReq;
+import cn.axzo.im.center.common.enums.AppTypeEnum;
+import cn.axzo.im.channel.netease.NimChannelService;
+import cn.axzo.im.channel.netease.dto.ChatGroupQueryResponse;
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.event.inner.EventTypeEnum;
+import cn.axzo.im.event.payload.OrganizationalNodeUserPayload;
+import cn.axzo.im.gateway.OrgJobApiGateway;
+import cn.axzo.im.service.AccountService;
+import cn.axzo.im.service.ChatGroupService;
+import cn.axzo.im.utils.BizAssertions;
+import cn.axzo.im.utils.JobCodeUtils;
+import cn.axzo.maokai.api.vo.response.OrgJobRes;
+import com.alibaba.excel.util.StringUtils;
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/07
+ * @desc 群聊创建MQ消费-同步网易云信
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class OrganizationalNodeUserChangeEventHandler implements EventHandler, InitializingBean {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Autowired
+ private ChatGroupService chatGroupService;
+
+ @Autowired
+ private NimChannelService nimChannelService;
+
+ @Autowired
+ private OrgJobApiGateway orgJobApiGateway;
+
+ @Autowired
+ private AccountService accountService;
+
+ @Override
+ public void onEvent(Event event, EventConsumer.Context context) {
+
+ if (!EventTypeEnum.NODE_USER_CREATE.getName().equalsIgnoreCase(event.getEventCode().getName())
+ && !EventTypeEnum.NODE_USER_UPDATE.getName().equalsIgnoreCase(event.getEventCode().getName())
+ && !EventTypeEnum.NODE_USER_DELETE.getName().equalsIgnoreCase(event.getEventCode().getName())
+ && !EventTypeEnum.NODE_USER_UPSERTED.getName().equalsIgnoreCase(event.getEventCode().getName())) {
+ return;
+ }
+ log.info("im-organizationalNodeUserChange-start mq,eventModule:{},eventName:{}, event:{}", event.getEventCode().getModule(), event.getEventCode().getName(), JSON.toJSONString(event));
+ long start = System.currentTimeMillis();
+ try {
+ handleMqMessage(event);
+ log.info("OrganizationalNodeUserChangeEventHandler-handle mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start);
+ } catch (Exception e) {
+ log.warn("OrganizationalNodeUserChangeEventHandler-handle error mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start, e);
+ }
+
+ }
+
+ /**
+ * 业务逻辑
+ */
+ private void handleMqMessage(Event event) {
+ //解析数据
+ OrganizationalNodeUserPayload payload = event.normalizedData(OrganizationalNodeUserPayload.class);
+
+ Long workspaceId = payload.getWorkspaceId();
+ Long ouId = payload.getOrganizationalUnitId();
+ Long nodeId = payload.getOrganizationalNodeId();
+
+
+ this.enterOrExitChatGroupByCrowType(ChatGroupCreateReq.CrowTypeEnum.WORKSPACE, workspaceId, null, null, payload, event.getEventCode().getName());
+ this.enterOrExitChatGroupByCrowType(ChatGroupCreateReq.CrowTypeEnum.OU, workspaceId, ouId, null, payload, event.getEventCode().getName());
+ this.enterOrExitChatGroupByCrowType(ChatGroupCreateReq.CrowTypeEnum.TEAM, workspaceId, null, nodeId, payload, event.getEventCode().getName());
+ }
+
+ /**
+ * 加入群聊或者退出群聊-通过人群
+ * @param crowTypeEnum 人群
+ * @param workspaceId 项目ID
+ * @param ouId 单位Id
+ * @param teamId 班组Id
+ * @param payload mq消息
+ * @param tag mq的tag
+ */
+ private void enterOrExitChatGroupByCrowType(ChatGroupCreateReq.CrowTypeEnum crowTypeEnum, Long workspaceId,Long ouId, Long teamId, OrganizationalNodeUserPayload payload, String tag) {
+ List workspaceChatGroups = chatGroupService.genericQuery(ChatGroupGenericSearchReq.builder()
+ .crowType(crowTypeEnum)
+ .workspaceId(workspaceId)
+ .ouId(ouId)
+ .teamId(teamId)
+ .build());
+ if (CollectionUtils.isEmpty(workspaceChatGroups)) {
+ log.info("群聊监控orgNodeUser,人员变更查询不到群聊,workspaceId:{},crowType:{}", workspaceId, crowTypeEnum);
+ return;
+ }
+ //人群类型:项目,同一个项目只存在一个群聊
+ ChatGroup chatGroup = workspaceChatGroups.get(0);
+ String jobCode = fetchJobCodeByJobId(payload.getOrganizationalJobId());
+ if (StringUtils.isBlank(jobCode)) {
+ log.info("群聊监控orgNodeUser,人员变更根据jobId获取jobCode为空,jobId:{}", payload.getOrganizationalJobId());
+ chatGroupService.sendDingRobot(String.format("群聊监控orgNodeUser,人员变更根据jobId获取jobCode为空,jobId:%s,nodeId:%d", payload.getOrganizationalJobId(), payload.getOrganizationalNodeId()));
+ return;
+ }
+
+ String imAccount = this.buildImAccount(payload.getPersonId(), payload.getOrganizationalUnitId(), jobCode, chatGroup.getCrowType());
+ this.enterOrExitChatGroup(tag, chatGroup, jobCode, imAccount);
+ }
+
+ /**
+ * 获取岗位code
+ */
+ private String fetchJobCodeByJobId(Long jobId) {
+ OrgJobRes orgJobRes = orgJobApiGateway.fetchJobCodeByJobId(jobId);
+ if (Objects.nonNull(orgJobRes)) {
+ return orgJobRes.getCode();
+ }
+ return "";
+ }
+
+ /**
+ * 加入群聊或者退出群聊
+ * @param tag MQ的tag
+ * @param chatGroup 群聊对象
+ * @param currentJobCode 岗位code
+ * @param currentImAccount im账号
+ */
+ private void enterOrExitChatGroup(String tag, ChatGroup chatGroup, String currentJobCode, String currentImAccount) {
+ String tid = chatGroup.getTid();
+ ChatGroupQueryResponse chatGroupQueryResponse = nimChannelService.chatGroupQueryByTids(Lists.newArrayList(tid), 1);
+ if (Objects.isNull(chatGroupQueryResponse) || CollectionUtils.isEmpty(chatGroupQueryResponse.getTinfos())) {
+ log.info("enterOrExitChatGroup-chatGroupQueryByTids response is empty or tinfos is empty");
+ return;
+ }
+
+ ChatGroupQueryResponse.TInfo tInfo = chatGroupQueryResponse.getTinfos().get(0);
+ String members = tInfo.getMembers();
+
+ switch (chatGroup.getCrowType()) {
+ case WORKSPACE:
+ case OU:
+ //加入群聊或者退出群聊-执行动作
+ this.doEnterOrExitChatGroup(tag, chatGroup, currentImAccount, members, tInfo.getOwner(), JobCodeUtils.isAdmin(currentJobCode));
+ break;
+ case TEAM:
+ //当处理项目内班组时,更改群组
+ this.changeOwnerWithTeam(chatGroup, currentImAccount, JobCodeUtils.isProjectTeamLeader(currentJobCode));
+ //加入群聊或者退出群聊-执行动作
+ this.doEnterOrExitChatGroup(tag, chatGroup, currentImAccount, members, tInfo.getOwner(), JobCodeUtils.isTeam(currentJobCode));
+ }
+ }
+
+ /**
+ * 当处理项目内班组时,更改群组
+ * @param chatGroup 群聊对象
+ * @param currentImAccount 当前Im账号
+ * @param isProjectTeamLeader 岗位是否时班组长
+ */
+ private void changeOwnerWithTeam(ChatGroup chatGroup, String currentImAccount, boolean isProjectTeamLeader) {
+ if(!isProjectTeamLeader) {
+ log.info("岗位不是班组长,不执行更改群主逻辑,currentImAccount:{},chatGroupId:{}", currentImAccount, chatGroup.getId());
+ return;
+ }
+ if (chatGroup.getGroupOwner().equals(currentImAccount)) {
+ log.info("班组长并未变更,不执行更改群主逻辑,currentImAccount:{},chatGroupId:{}", currentImAccount, chatGroup.getId());
+ return;
+ }
+ this.chatGroupService.changeOwner(chatGroup.getId(), chatGroup.getGroupOwner(), currentImAccount);
+ }
+
+ /**
+ * 加入群聊或者退出群聊-执行动作
+ * @param tag MQ的tag
+ * @param chatGroup 群聊对象
+ * @param currentImAccount im账号
+ * @param members 群聊已有会员
+ * @param owner 群主
+ * @param containJobCode 包含job
+ */
+ private void doEnterOrExitChatGroup(String tag, ChatGroup chatGroup, String currentImAccount,String members, String owner, boolean containJobCode) {
+
+ //orgNodeUser新增人,并且当前群不包含人
+ if (tag.equals(EventTypeEnum.NODE_USER_CREATE.getName()) && containJobCode &&!members.contains(currentImAccount)) {
+ //拉人进群
+ this.chatGroupService.userAddChatGroup(chatGroup.getId(), chatGroup.getTid(), owner, Lists.newArrayList(currentImAccount), chatGroup.getName());
+ return;
+ }
+ //orgNodeUser删除人 并且当前成员不包含人
+ if (tag.equals(EventTypeEnum.NODE_USER_DELETE.getName()) && members.contains(currentImAccount)) {
+ //踢出群
+ this.chatGroupService.kickChatGroup(chatGroup.getId(), chatGroup.getTid(), owner,currentImAccount);
+ return;
+ }
+ //以下orgNodeUser更新
+ if (!tag.equals(EventTypeEnum.NODE_USER_UPDATE.getName())) {
+ return;
+ }
+
+ if (containJobCode && !members.contains(currentImAccount)) {
+ //拉人进群
+ this.chatGroupService.userAddChatGroup(chatGroup.getId(), chatGroup.getTid(), owner, Lists.newArrayList(currentImAccount), chatGroup.getName());
+ return;
+ }
+ if (!containJobCode && members.contains(currentImAccount)) {
+ //踢出群
+ this.chatGroupService.kickChatGroup(chatGroup.getId(), chatGroup.getTid(), owner, currentImAccount);
+ }
+
+ }
+
+ /**
+ * 构建IM账号
+ */
+ private String buildImAccount(Long personId, Long ouId, String jobCode, ChatGroupCreateReq.CrowTypeEnum crowTypeEnum) {
+ switch (crowTypeEnum) {
+ case WORKSPACE:
+ case OU:
+ return accountService.buildUserIdWrapper(personId.toString(), AppTypeEnum.CMP.getCode(), ouId);
+ case TEAM:
+ if (jobCode.equals("projTeamLeader") || jobCode.equals("projectTeamManager")) {
+ return accountService.buildUserIdWrapper(personId.toString(), AppTypeEnum.CMP.getCode(), ouId);
+ }
+ return accountService.buildUserIdWrapper(personId.toString(), AppTypeEnum.CM.getCode(), ouId);
+ default:
+ BizAssertions.assertTrue(false, "人群类型不匹配");
+ }
+ throw new ServiceException("人群类型不匹配");
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ Event.EventCode nodeUserCreateEventCode = new Event.EventCode(EventTypeEnum.NODE_USER_CREATE.getModel(), EventTypeEnum.NODE_USER_CREATE.getName());
+ Event.EventCode nodeUserUpdateEventCode = new Event.EventCode(EventTypeEnum.NODE_USER_UPDATE.getModel(), EventTypeEnum.NODE_USER_UPDATE.getName());
+ Event.EventCode nodeUserDeleteEventCode = new Event.EventCode(EventTypeEnum.NODE_USER_DELETE.getModel(), EventTypeEnum.NODE_USER_DELETE.getName());
+ Event.EventCode nodeUserUpsertedEventCode = new Event.EventCode(EventTypeEnum.NODE_USER_UPSERTED.getModel(), EventTypeEnum.NODE_USER_UPSERTED.getName());
+
+ eventConsumer.registerHandler(nodeUserCreateEventCode, this);
+ eventConsumer.registerHandler(nodeUserUpdateEventCode, this);
+ eventConsumer.registerHandler(nodeUserDeleteEventCode, this);
+ eventConsumer.registerHandler(nodeUserUpsertedEventCode, this);
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/PersonProfileAvatarUpdateEventHandler.java b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/PersonProfileAvatarUpdateEventHandler.java
new file mode 100644
index 0000000..b48304c
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/handler/chatgroup/PersonProfileAvatarUpdateEventHandler.java
@@ -0,0 +1,96 @@
+package cn.axzo.im.handler.chatgroup;
+
+import cn.axzo.basics.profiles.api.UserProfileServiceApi;
+import cn.axzo.basics.profiles.common.enums.ProfileMQEventEnum;
+import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
+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.entity.AccountRegister;
+import cn.axzo.im.entity.bo.AccountQueryParam;
+import cn.axzo.im.event.inner.EventTypeEnum;
+import cn.axzo.im.service.AccountService;
+import cn.azxo.framework.common.model.CommonResponse;
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/07
+ * @desc 群聊创建MQ消费-同步网易云信
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class PersonProfileAvatarUpdateEventHandler implements EventHandler, InitializingBean {
+
+ @Autowired
+ private EventConsumer eventConsumer;
+
+ @Autowired
+ private AccountService accountService;
+
+ @Autowired
+ private UserProfileServiceApi userProfileServiceApi;
+ @Override
+ public void onEvent(Event event, EventConsumer.Context context) {
+
+ if (!ProfileMQEventEnum.UPDATE_AVATAR.getTag().equalsIgnoreCase(event.getEventCode().getName())) {
+ return;
+ }
+ log.info("im-updateAvatar-start mq,eventModule:{},eventName:{}, event:{}", event.getEventCode().getModule(), event.getEventCode().getName(), JSON.toJSONString(event));
+ long start = System.currentTimeMillis();
+ try {
+ handleMqMessage(event);
+ log.info("PersonProfileAvatarUpdateEventHandler-handle mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start);
+ } catch (Exception e) {
+ log.warn("PersonProfileAvatarUpdateEventHandler-handle error mq event, event={},used={}ms", JSON.toJSONString(event), System.currentTimeMillis() - start, e);
+ }
+
+ }
+
+ /**
+ * 业务逻辑
+ */
+ private void handleMqMessage(Event event) {
+ //解析数据
+ ProfileMQBaseEntity eventData = event.normalizedData(ProfileMQBaseEntity.class);
+
+ AccountQueryParam param = new AccountQueryParam();
+ param.setAccountId(eventData.getId().toString());
+ //1 获取账号信息
+ List accountRegisters = accountService.listAccount(param);
+ if (CollectionUtils.isEmpty(accountRegisters)) {
+ log.info("未找到im账号信息,personId:{}", eventData.getId().toString());
+ return;
+ }
+ //2 获取PersonProfile
+ CommonResponse personProfileDtoCommonResponse = userProfileServiceApi.getPersonProfile(eventData.getId());
+ if (Objects.isNull(personProfileDtoCommonResponse) || Objects.isNull(personProfileDtoCommonResponse.getData())) {
+ log.info("未找到personProfile信息,personId:{}", eventData.getId().toString());
+ return;
+ }
+ //3 同步网易云信账号
+ PersonProfileDto personProfileDto = personProfileDtoCommonResponse.getData();
+ for (AccountRegister accountRegister : accountRegisters) {
+ GetAccountInfoResponse.AccountInfo accountInfo = accountService.fetchImAccountInfoByImAccount(accountRegister.getImAccount());
+ accountService.syncImAccount(accountRegister.getAccountId(), accountRegister.getImAccount(), accountInfo.getOrCreateExtObject(),personProfileDto.getAvatarUrl(), personProfileDto.getRealName());
+ }
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ Event.EventCode eventCode = new Event.EventCode(EventTypeEnum.UPDATE_AVATAR.getModel(), EventTypeEnum.UPDATE_AVATAR.getName());
+ eventConsumer.registerHandler(eventCode, this);
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/job/UpdateImAccountPersonInfoJob.java b/im-center-server/src/main/java/cn/axzo/im/job/UpdateImAccountPersonInfoJob.java
index 73b3edf..e9ec322 100644
--- a/im-center-server/src/main/java/cn/axzo/im/job/UpdateImAccountPersonInfoJob.java
+++ b/im-center-server/src/main/java/cn/axzo/im/job/UpdateImAccountPersonInfoJob.java
@@ -6,12 +6,12 @@ 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.GetAccountInfoRequest;
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.dao.repository.AccountRegisterDao;
import cn.axzo.im.entity.AccountRegister;
+import cn.axzo.im.service.AccountService;
import cn.axzo.im.utils.BizAssertions;
import cn.axzo.maokai.api.util.Ref;
import com.alibaba.fastjson.JSONObject;
@@ -44,6 +44,8 @@ public class UpdateImAccountPersonInfoJob {
private final NimClient nimClient;
private final UserProfileServiceApi userProfileServiceApi;
+ private final AccountService accountService;
+
@SuppressWarnings("unused")
@XxlJob("updateImAccountPersonInfoJob")
public ReturnT execute(String jsonStr) throws Exception {
@@ -59,66 +61,99 @@ public class UpdateImAccountPersonInfoJob {
}
private void executeImpl() {
- RateLimiter rateLimiter = RateLimiter.create(60);
+
Supplier> cursor = accountsCursor();
int count = 0;
for (List accounts = cursor.get(); !accounts.isEmpty(); accounts = cursor.get()) {
log.info("update account info, count: {}", count += accounts.size());
- GetAccountInfoRequest getAccountInfoRequest = new GetAccountInfoRequest();
- getAccountInfoRequest.setImAccountIds(accounts.stream()
- .map(AccountRegister::getImAccount)
- .distinct()
- .collect(toList()));
- GetAccountInfoResponse getAccountInfoResponse = nimClient.getAccountInfo(getAccountInfoRequest);
- if (!getAccountInfoResponse.isSuccess()) {
- log.warn("get account info failed, {}", getAccountInfoResponse);
+ //1 获取IM账号
+ GetAccountInfoResponse imAccountList = accountService.getImAccount(accounts);
+
+ if (!imAccountList.isSuccess()) {
+ log.warn("get account info failed, {}", imAccountList);
continue;
}
- List personIds = accounts.stream()
- .map(AccountRegister::getAccountId)
- .filter(Objects::nonNull)
- .filter(NumberUtils::isCreatable)
- .map(Long::parseLong)
- .distinct()
- .collect(toList());
- Map id2PersonProfile = BizAssertions.assertResponse(
- userProfileServiceApi.getPersonProfiles(personIds)).stream()
- .collect(Collectors.toMap(BasicDto::getId, i -> i));
+ //2 构建PersonProfile
+ Map id2PersonProfile = this.buildPersonProfileMap(accounts);
for (AccountRegister account : accounts) {
- GetAccountInfoResponse.AccountInfo accountInfo = getAccountInfoResponse
- .findImAccountInfo(account.getImAccount()).orElse(null);
- if (accountInfo == null) continue;
-
- JSONObject ext = accountInfo.getOrCreateExtObject();
- ext.put("personId", account.getAccountId());
-
- UpdateAccountInfoRequest updateAccountInfoRequest = new UpdateAccountInfoRequest();
- updateAccountInfoRequest.setImAccountId(accountInfo.getAccid());
- updateAccountInfoRequest.addExt(ext);
-
- if (account.getAccountId() != null && NumberUtils.isCreatable(account.getAccountId())) {
- long personId = Long.parseLong(account.getAccountId());
- PersonProfileDto person = id2PersonProfile.get(personId);
- if (person != null) {
- if (StringUtils.isNotBlank(person.getAvatarUrl())) {
- updateAccountInfoRequest.setIcon(person.getAvatarUrl());
- }
- if (StringUtils.isNotBlank(person.getRealName())) {
- updateAccountInfoRequest.setName(person.getRealName());
- }
- }
+ GetAccountInfoResponse.AccountInfo imAccount = imAccountList.findImAccountInfo(account.getImAccount()).orElse(null);
+ if (imAccount == null) continue;
+ //3 获取personProfile
+ PersonProfileDto person = this.fetchPersonById(account.getAccountId(), id2PersonProfile);
+ if (Objects.isNull(person) || Objects.isNull(person.getId())) {
+ log.info("personProfile为空,personId:{}", account.getAccountId());
+ continue;
}
+ //4 更新IM账号
+ UpdateAccountInfoResponse updateAccountInfoResponse
+ = accountService.syncImAccount(account.getAccountId(), imAccount.getAccid()
+ ,imAccount.getOrCreateExtObject(), person.getAvatarUrl(), person.getRealName());
+ //日志打印
+ this.successLog(updateAccountInfoResponse);
+ }
+ }
+ }
- rateLimiter.acquire();
- UpdateAccountInfoResponse updateAccountInfoResponse = nimClient
- .updateAccountInfo(updateAccountInfoRequest);
- if (updateAccountInfoResponse.isSuccess()) {
- log.info("update account info success, {}", updateAccountInfoResponse);
- } else {
- log.warn("update account info failed, {}", updateAccountInfoResponse);
+ private PersonProfileDto fetchPersonById(String accountId, Map id2PersonProfile) {
+ long personId = Long.parseLong(accountId);
+ PersonProfileDto person = id2PersonProfile.get(personId);
+ if (Objects.isNull(person) || Objects.isNull(person.getId())) {
+ log.info("personProfile为空,personId:{}", person);
+ return null;
+ }
+ return person;
+ }
+
+ /**
+ * 日志打印
+ * @param updateAccountInfoResponse
+ */
+ private void successLog(UpdateAccountInfoResponse updateAccountInfoResponse) {
+ if (updateAccountInfoResponse.isSuccess()) {
+ log.info("update account info success, {}", updateAccountInfoResponse);
+ } else {
+ log.warn("update account info failed, {}", updateAccountInfoResponse);
+ }
+ }
+
+ private Map buildPersonProfileMap(List accounts) {
+ List personIds = accounts.stream()
+ .map(AccountRegister::getAccountId)
+ .filter(Objects::nonNull)
+ .filter(NumberUtils::isCreatable)
+ .map(Long::parseLong)
+ .distinct()
+ .collect(toList());
+ return BizAssertions.assertResponse(
+ userProfileServiceApi.getPersonProfiles(personIds)).stream()
+ .collect(Collectors.toMap(BasicDto::getId, i -> i));
+ }
+
+ private UpdateAccountInfoResponse updateImAccount(AccountRegister account, GetAccountInfoResponse.AccountInfo accountInfo,Map id2PersonProfile) {
+ RateLimiter rateLimiter = RateLimiter.create(60);
+ JSONObject ext = accountInfo.getOrCreateExtObject();
+ ext.put("personId", account.getAccountId());
+
+ UpdateAccountInfoRequest updateAccountInfoRequest = new UpdateAccountInfoRequest();
+ updateAccountInfoRequest.setImAccountId(accountInfo.getAccid());
+ updateAccountInfoRequest.addExt(ext);
+
+ if (account.getAccountId() != null && NumberUtils.isCreatable(account.getAccountId())) {
+ long personId = Long.parseLong(account.getAccountId());
+ PersonProfileDto person = id2PersonProfile.get(personId);
+ if (person != null) {
+ if (StringUtils.isNotBlank(person.getAvatarUrl())) {
+ updateAccountInfoRequest.setIcon(person.getAvatarUrl());
+ }
+ if (StringUtils.isNotBlank(person.getRealName())) {
+ updateAccountInfoRequest.setName(person.getRealName());
}
}
}
+
+ rateLimiter.acquire();
+ return nimClient
+ .updateAccountInfo(updateAccountInfoRequest);
}
private Supplier> accountsCursor() {
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java b/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java
index 809550d..6808fb6 100644
--- a/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java
+++ b/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java
@@ -14,11 +14,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.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;
@@ -27,13 +31,16 @@ import cn.axzo.im.entity.RobotInfo;
import cn.axzo.im.entity.bo.AccountQueryParam;
import cn.axzo.im.utils.MiscUtils;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.RateLimiter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
@@ -55,6 +62,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import static cn.axzo.im.utils.Queries.query;
+import static java.util.stream.Collectors.toList;
/**
* IM账户服务
@@ -459,4 +467,61 @@ public class AccountService {
}
return lambdaQuery;
}
+
+ /**
+ * 同步网易云信账号
+ */
+ public UpdateAccountInfoResponse syncImAccount(String accountId, String accid, JSONObject ext, String avatarUrl, String realName) {
+ RateLimiter rateLimiter = RateLimiter.create(60);
+ ext.put("personId", accountId);
+
+ UpdateAccountInfoRequest updateAccountInfoRequest = new UpdateAccountInfoRequest();
+ updateAccountInfoRequest.setImAccountId(accid);
+ updateAccountInfoRequest.addExt(ext);
+
+ if (accountId != null && NumberUtils.isCreatable(accountId)) {
+ if (StringUtils.isNotBlank(avatarUrl)) {
+ updateAccountInfoRequest.setIcon(avatarUrl);
+ }
+ if (StringUtils.isNotBlank(realName)) {
+ updateAccountInfoRequest.setName(realName);
+ }
+ }
+
+ rateLimiter.acquire();
+ return nimClient.updateAccountInfo(updateAccountInfoRequest);
+ }
+
+ public GetAccountInfoResponse getImAccount(List accounts) {
+ if (CollectionUtils.isEmpty(accounts)) {
+ return null;
+ }
+ GetAccountInfoRequest getAccountInfoRequest = new GetAccountInfoRequest();
+ getAccountInfoRequest.setImAccountIds(accounts.stream().map(item -> item.getImAccount()).distinct().collect(toList()));
+ return nimClient.getAccountInfo(getAccountInfoRequest);
+ }
+
+ public List fetchImAccountsByImAccounts(List accounts) {
+ if (CollectionUtils.isEmpty(accounts)) {
+ return null;
+ }
+ GetAccountInfoRequest getAccountInfoRequest = new GetAccountInfoRequest();
+ getAccountInfoRequest.setImAccountIds(accounts);
+ GetAccountInfoResponse response = nimClient.getAccountInfo(getAccountInfoRequest);
+ if (Objects.isNull(response) || CollectionUtils.isEmpty(response.getUinfos())) {
+ return null;
+ }
+ return response.getUinfos();
+ }
+
+ public GetAccountInfoResponse.AccountInfo fetchImAccountInfoByImAccount(String imAccount) {
+ if (StringUtils.isBlank(imAccount)) {
+ return null;
+ }
+ List accountInfos = this.fetchImAccountsByImAccounts(Lists.newArrayList(imAccount));
+ if (CollectionUtils.isEmpty(accountInfos)) {
+ return null;
+ }
+ return accountInfos.get(0);
+ }
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupService.java b/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupService.java
index c511ecb..aa4b107 100644
--- a/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupService.java
+++ b/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupService.java
@@ -1,12 +1,18 @@
package cn.axzo.im.service;
+import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupGenericSearchReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupQueryReq;
import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
-import cn.axzo.im.channel.netease.dto.ChatGroupCreateRequest;
-import cn.axzo.im.channel.netease.dto.ChatGroupCreateResponse;
+import cn.axzo.im.center.api.vo.resp.ChatGroupQueryResp;
import cn.axzo.im.entity.ChatGroup;
+import cn.hutool.core.lang.Pair;
import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.List;
+import java.util.Set;
+
/**
* @author xudawei@axzo.cn
* @date 2024/11/05
@@ -17,6 +23,63 @@ public interface ChatGroupService extends IService {
/**
* 创建群聊
*/
- ChatGroupCreateResp chatGroupCreate(ChatGroupCreateReq req);
+ ApiResult chatGroupCreate(ChatGroupCreateReq req);
+
+ /**
+ * 获取IM账号
+ */
+ Pair, Set> fetchUsersByWorkspaceOuId(ChatGroupCreateReq.CrowTypeEnum crowType, Long workspaceId, Long ouId, Set jobCodes, Long personId);
+
+ /**
+ * 通用查询-少条件后期在补
+ */
+ List genericQuery(ChatGroupGenericSearchReq req);
+
+ /**
+ * 通过Id查询群聊
+ */
+ ChatGroup getById(Long id);
+
+ /**
+ * 获取群聊
+ */
+ ChatGroupQueryResp chatGroupQuery(ChatGroupQueryReq req);
+
+
+ /**
+ * 根据人群,返回jobCode
+ */
+ Set buildJobCodesByCrowType(ChatGroupCreateReq.CrowTypeEnum crowType);
+
+ /**
+ * 发送钉钉群
+ */
+ void sendDingRobot(String content);
+
+ /**
+ * 拉人进群
+ * @param tid 群Id
+ * @param owner 群主
+ * @param members im账号集合
+ * @param groupName 群名称
+ */
+ void userAddChatGroup(Long chatGroupId, String tid, String owner, List members, String groupName);
+
+ /**
+ * 踢出群
+ * @param tid 群Id
+ * @param owner 群主
+ * @param imAccount im账号
+ */
+ void kickChatGroup(Long chatGroupId, String tid, String owner, String imAccount);
+
+ /**
+ * 更改群主
+ * @param chatGroupId 群聊Id
+ * @param oldAccIdOwner 原群主账号
+ * @param newAccIdOwner 新群主账号
+ */
+ void changeOwner(Long chatGroupId, String oldAccIdOwner, String newAccIdOwner);
+
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupUserService.java b/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupUserService.java
new file mode 100644
index 0000000..b8bdd84
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/ChatGroupUserService.java
@@ -0,0 +1,54 @@
+package cn.axzo.im.service;
+
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupUserGenericSearchReq;
+import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.entity.ChatGroupUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ * @desc 群聊成员
+ */
+public interface ChatGroupUserService extends IService {
+
+ /**
+ * 创建群聊成员
+ */
+ Boolean chatGroupUserCreate(Long chatGroupId, String tid,String accId, ChatGroupStatusEnum status, String remark, ChatGroupCreateReq.CrowTypeEnum crowType, ChatGroupUserTypeEnum type);
+
+ /**
+ * 批量创建群聊成员
+ */
+ Boolean chatGroupUserBatchCreate(Long chatGroupId, String tid, List members, ChatGroupStatusEnum status, String remark, ChatGroupCreateReq.CrowTypeEnum crowType);
+
+ /**
+ * 删除群聊成员
+ */
+ Boolean chatGroupUserDelete(Long chatGroupId, String accId);
+
+
+ /**
+ * 通用查询-少条件后期在补
+ */
+ List genericQuery(ChatGroupUserGenericSearchReq req);
+
+ /**
+ * 通用查询数量
+ */
+ Integer genericQueryCount(ChatGroupUserGenericSearchReq req);
+
+ /**
+ * 更改群主
+ * @param chatGroupId 群聊Id
+ * @param oldAccId 原有账号
+ * @param newAccId 新账号
+ */
+ void changeOwner(Long chatGroupId, String oldAccId, String newAccId);
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/ComplaintService.java b/im-center-server/src/main/java/cn/axzo/im/service/ComplaintService.java
new file mode 100644
index 0000000..061ac27
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/ComplaintService.java
@@ -0,0 +1,22 @@
+package cn.axzo.im.service;
+
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ComplaintCreateReq;
+import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.entity.Complaint;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/05
+ * @desc 群聊
+ */
+public interface ComplaintService extends IService {
+
+ /**
+ * 创建群聊
+ */
+ void complaintCreate(ComplaintCreateReq req);
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/DingDingRobotService.java b/im-center-server/src/main/java/cn/axzo/im/service/DingDingRobotService.java
new file mode 100644
index 0000000..70d3bca
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/DingDingRobotService.java
@@ -0,0 +1,10 @@
+package cn.axzo.im.service;
+
+public interface DingDingRobotService {
+ /**
+ * 发钉钉消息。先默认是泰州的,后续如果不同三方对应的行为不一致,再调整
+ *
+ * @param content
+ */
+ void send(String content);
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/OperateLogService.java b/im-center-server/src/main/java/cn/axzo/im/service/OperateLogService.java
new file mode 100644
index 0000000..68c0ae4
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/OperateLogService.java
@@ -0,0 +1,18 @@
+package cn.axzo.im.service;
+
+import cn.axzo.im.center.common.enums.OperateLogTypeEnum;
+import cn.axzo.im.entity.OperateLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ * @desc 操作日志
+ */
+public interface OperateLogService extends IService {
+ /**
+ * 创建操作日志
+ */
+ Boolean create(OperateLogTypeEnum typeEnum, String content);
+
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupServiceImpl.java
index 99ec915..3d38f9e 100644
--- a/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupServiceImpl.java
+++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupServiceImpl.java
@@ -1,20 +1,77 @@
package cn.axzo.im.service.impl;
+import cn.axzo.apollo.workspace.api.workspace.res.GetDetailRes;
+import cn.axzo.basics.common.exception.ServiceException;
+import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
+import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupGenericSearchReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupQueryReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupUserGenericSearchReq;
+import cn.axzo.im.center.api.vo.req.UserAccountReq;
import cn.axzo.im.center.api.vo.resp.ChatGroupCreateResp;
+import cn.axzo.im.center.api.vo.resp.ChatGroupQueryResp;
+import cn.axzo.im.center.api.vo.resp.UserAccountResp;
+import cn.axzo.im.center.common.enums.AppTypeEnum;
+import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import cn.axzo.im.center.common.enums.OperateLogTypeEnum;
import cn.axzo.im.channel.IMChannelProvider;
+import cn.axzo.im.channel.netease.INotifyService;
+import cn.axzo.im.channel.netease.NimChannelService;
+import cn.axzo.im.channel.netease.dto.ChangeOwnerRequest;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateRequest;
import cn.axzo.im.channel.netease.dto.ChatGroupCreateResponse;
+import cn.axzo.im.channel.netease.dto.ChatGroupQueryResponse;
+import cn.axzo.im.channel.netease.dto.KickChatGroupRequest;
+import cn.axzo.im.channel.netease.dto.UserAddChatGroupRequest;
+import cn.axzo.im.config.BizResultCode;
+import cn.axzo.im.config.ChatGroupConfig;
import cn.axzo.im.config.MqProducer;
import cn.axzo.im.dao.mapper.ChatGroupMapper;
import cn.axzo.im.entity.ChatGroup;
import cn.axzo.im.event.payload.ChatGroupCreatePayload;
+import cn.axzo.im.gateway.OrganizationalNodeApiGateway;
+import cn.axzo.im.gateway.OrganizationalNodeUserApiGateway;
+import cn.axzo.im.gateway.ProfilesApiGateway;
+import cn.axzo.im.gateway.TyrApiGateway;
+import cn.axzo.im.gateway.WorkspaceApiGateway;
+import cn.axzo.im.service.AccountService;
import cn.axzo.im.service.ChatGroupService;
+import cn.axzo.im.service.ChatGroupUserService;
+import cn.axzo.im.service.OperateLogService;
+import cn.axzo.im.utils.BizAssertions;
+import cn.axzo.im.utils.DingTalkUtil;
+import cn.axzo.im.utils.JobCodeUtils;
+import cn.axzo.maokai.api.vo.request.OrganizationalNodeUserSearchReq;
+import cn.axzo.maokai.api.vo.response.OrganizationalNodeUserVO;
+import cn.axzo.maokai.api.vo.response.OrganizationalNodeVO;
+import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
+import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Sets;
+import lombok.Data;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import static cn.axzo.im.event.inner.EventTypeEnum.MESSAGE_CHAT_GROUP_CREATE;
@@ -25,6 +82,7 @@ import static cn.axzo.im.event.inner.EventTypeEnum.MESSAGE_CHAT_GROUP_CREATE;
*/
@Slf4j
@Service
+@RefreshScope
public class ChatGroupServiceImpl extends ServiceImpl
implements ChatGroupService {
@@ -34,27 +92,434 @@ public class ChatGroupServiceImpl extends ServiceImpl chatGroupCreate(ChatGroupCreateReq req) {
+ // 超管账号
+ ChatGroupFetchOwnerResp resp = this.fetchAdminByWorkspaceOuId(req.getCrowType(), req.getWorkspaceId(), req.getOuId(), req.getCreator());
+ req.setOwner(resp.getOwnerId());
+ //创建群聊-校验
+ ApiResult apiResult = this.checkChatGroupCreate(req, resp.getTeamNodeId());
+ if (Objects.nonNull(apiResult)) {
+ return apiResult;
+ }
+
+ // 群聊创建
+ ChatGroupCreateResponse response = imChannelProvider.chatGroupCreate(ChatGroupCreateRequest.convertRequest(req, resp.getOwnerImAccount(), this.buildAttach(req)));
+ ChatGroup chatGroup = this.buildChatGroupCreate(req, response, resp.getOwnerImAccount(), req.getOuId(), resp.getTeamNodeId());
+ // 保存db
+ this.saveOrUpdate(chatGroup);
+ this.chatGroupUserService.chatGroupUserCreate(chatGroup.getId(), response.getTid(), resp.getOwnerImAccount(), ChatGroupStatusEnum.SUCCESS, null, req.getCrowType(), ChatGroupUserTypeEnum.OWNER);
+ // 群聊创建成功发送MQ
+ this.chatGroupCreateSendMQ(req, response, resp.getOwnerId().toString(), resp.getOwnerImAccount(), chatGroup.getId());
+ // 返回
+ return ApiResult.ok(ChatGroupCreateResponse.convertResp(response, req.getGroupName(), req.getAvatarUrl()));
+ }
+
+ /**
+ * 创建群聊-校验
+ */
+ public ApiResult checkChatGroupCreate(ChatGroupCreateReq req , Long teamId) {
+
+ ChatGroup chatGroup = null;
+ StringBuilder desc = new StringBuilder();
+ //1 是否已经创建群聊校验
+ switch (req.getCrowType()) {
+ case WORKSPACE:
+ List chatGroups = this.genericQuery(ChatGroupGenericSearchReq.builder().workspaceId(req.getWorkspaceId()).crowType(req.getCrowType()).build());
+ chatGroup = CollectionUtils.isNotEmpty(chatGroups) ? chatGroups.get(0) : null;
+ desc.append("人群类型:项目,群聊已存在,workspaceId:").append(req.getWorkspaceId());
+ break;
+ case OU:
+ List chatGroupsWhenOu = this.genericQuery(ChatGroupGenericSearchReq.builder().workspaceId(req.getWorkspaceId()).ouId(req.getOuId()).crowType(req.getCrowType()).build());
+ chatGroup = CollectionUtils.isNotEmpty(chatGroupsWhenOu) ? chatGroupsWhenOu.get(0) : null;
+ desc.append("人群类型:单位,群聊已存在,workspaceId:").append(req.getWorkspaceId());
+ break;
+ case TEAM:
+ List chatGroupsWhenTeam = this.genericQuery(ChatGroupGenericSearchReq.builder().workspaceId(req.getWorkspaceId()).teamId(teamId).crowType(req.getCrowType()).build());
+ chatGroup = CollectionUtils.isNotEmpty(chatGroupsWhenTeam) ? chatGroupsWhenTeam.get(0) : null;
+ desc.append("人群类型:班组,群聊已存在,workspaceId:").append(req.getWorkspaceId());
+ break;
+ default:
+ BizAssertions.assertTrue(false, "人群不存在");
+ }
+
+ if (Objects.nonNull(chatGroup)) {
+ return ApiResult.ok(ChatGroupCreateResp.builder().tid(chatGroup.getTid())
+ .groupName(chatGroup.getName()).avatarUrl(chatGroup.getAvatarUrl())
+ .code(Integer.parseInt(BizResultCode.CHAT_GROUP_ALREADY_EXISTS.getErrorCode()))
+ .desc(desc.toString())
+ .build());
+ }
+
+ //2 人数超限校验
+ //Pair,key:管理人员集合;value:工人集合
+ Pair, Set> adminWorkerSet = this.fetchUsersByWorkspaceOuId(req.getCrowType(), req.getWorkspaceId(), req.getOuId(), this.buildJobCodesByCrowType(req.getCrowType()), req.getCreator());
+ Set adminSet = adminWorkerSet.getKey();
+ Set workerSet = adminWorkerSet.getValue();
+ if ((adminSet.size() + workerSet.size()) > chatGroupMaxMemberCount) {
+ String error = String.format("创建群组:%s,超过人数:%d,项目Id:%d,人群类型:%s", req.getGroupName(), chatGroupMaxMemberCount, req.getWorkspaceId(), req.getCrowType());
+ this.sendDingRobot(error);
+ return ApiResult.ok(ChatGroupCreateResp.builder()
+ .code(Integer.parseInt(BizResultCode.CHAT_GROUP_OVER_MEMBERS_COUNT_LIMIT.getErrorCode()))
+ .desc(BizResultCode.CHAT_GROUP_OVER_MEMBERS_COUNT_LIMIT.getErrorMessage()).build());
+ }
+
+ return null;
+ }
+
+ public List genericQuery(ChatGroupGenericSearchReq req) {
+ return this.lambdaQuery().eq(Objects.nonNull(req.getWorkspaceId()), ChatGroup::getWorkspaceId, req.getWorkspaceId())
+ .eq(Objects.nonNull(req.getOuId()), ChatGroup::getOuId, req.getOuId())
+ .eq(Objects.nonNull(req.getTeamId()), ChatGroup::getTeamId, req.getTeamId())
+ .eq(Objects.nonNull(req.getCrowType()), ChatGroup::getCrowType, req.getCrowType())
+ .eq(ChatGroup::getIsDelete,0)
+ .list();
+ }
+
+ /**
+ * 通过Id查询群聊
+ */
+ public ChatGroup getById(Long id) {
+ return this.lambdaQuery().eq(ChatGroup::getId, id)
+ .one();
+ }
+
+ /**
+ * 构建群聊创建对象
+ */
+ private ChatGroup buildChatGroupCreate(ChatGroupCreateReq req, ChatGroupCreateResponse response, String groupOwner, Long ouId, Long teamNodeId) {
+ return ChatGroup.builder()
+ .name(req.getGroupName())
+ .tid(response.getTid())
+ .avatarUrl(req.getAvatarUrl())
+ .workspaceId(req.getWorkspaceId())
+ .groupType(req.getGroupType())
+ .crowType(req.getCrowType())
+ .ouId(req.getCrowType() == ChatGroupCreateReq.CrowTypeEnum.OU ? ouId : null)
+ .teamId(req.getCrowType() == ChatGroupCreateReq.CrowTypeEnum.TEAM ? teamNodeId : null)
+ .groupOwner(groupOwner)
+ .creator(req.getCreator())
+ .build();
+ }
+
+ /**
+ * 构建自定义扩展字段
+ */
+ private String buildAttach(ChatGroupCreateReq req) {
+ GetDetailRes getDetailRes = workspaceApiGateway.getWorkspaceById(req.getWorkspaceId());
+ String workspaceName = getDetailRes.getName();
+ Map attachMap = new HashMap<>();
+ attachMap.put("workspaceId", req.getWorkspaceId().toString());
+ attachMap.put("workspaceName", workspaceName);
+ return JSONUtil.toJsonStr(attachMap);//TODO
}
/**
* 群聊创建成功发送MQ
*/
- private void chatGroupCreateSendMQ(ChatGroupCreateReq req, ChatGroupCreateResponse response) {
+ private void chatGroupCreateSendMQ(ChatGroupCreateReq req, ChatGroupCreateResponse response, String owner, String imAccountOwner, Long chatGroupId) {
Event event = Event.builder()
.targetId(String.valueOf(response.getTid()))
.targetType(MESSAGE_CHAT_GROUP_CREATE.getModel())
.eventCode(MESSAGE_CHAT_GROUP_CREATE.getEventCode())
- .data(ChatGroupCreatePayload.builder().req(req).tid(response.getTid()).build())
+ .data(ChatGroupCreatePayload.builder().req(req).tid(response.getTid()).owner(owner).imAccountOwner(imAccountOwner).chatGroupId(chatGroupId).build())
.build();
mqProducer.send(event);
}
+
+ /**
+ * 获取超管账号
+ */
+ private ChatGroupFetchOwnerResp fetchAdminByWorkspaceOuId(ChatGroupCreateReq.CrowTypeEnum crowType, Long workspaceId, Long ouId, Long personId) {
+ ChatGroupFetchOwnerResp resp = new ChatGroupFetchOwnerResp();
+
+ Long ownerId = null;
+ switch (crowType) {
+ case WORKSPACE:
+ List saasRoleUserV2DTOS = tyrApiGateway.fetchSaasRoleUserByWorkspaceOuIdRoleTypes(workspaceId, null, Sets.newHashSet(RoleTypeEnum.SUPER_ADMIN));
+ if (CollectionUtils.isEmpty(saasRoleUserV2DTOS)) {
+ this.sendDingRobot(String.format("创建群聊,personId:%d在workspaceId:%d,人群:%s,找不到群主信息", personId, workspaceId,crowType));
+ }
+ BizAssertions.assertNotEmpty(saasRoleUserV2DTOS, "personId:{}在workspaceId:{},找不到群主信息", personId, workspaceId);
+ ownerId = saasRoleUserV2DTOS.get(0).getSaasRoleUser().getPersonId();
+ break;
+ case OU:
+ List saasRoleUserV2DTOSWhenOu = tyrApiGateway.fetchSaasRoleUserByWorkspaceOuIdRoleTypes(workspaceId, ouId, Sets.newHashSet(RoleTypeEnum.ADMIN));
+ if (CollectionUtils.isEmpty(saasRoleUserV2DTOSWhenOu)) {
+ this.sendDingRobot(String.format("创建群聊,personId:%d在workspaceId:%d,ouId:%d,人群:%s,找不到群主信息", personId, workspaceId,ouId, crowType));
+ }
+ BizAssertions.assertNotEmpty(saasRoleUserV2DTOSWhenOu, "personId:{}在workspaceId:{},ouId:{},找不到群主信息", personId, workspaceId, ouId);
+ ownerId = saasRoleUserV2DTOSWhenOu.get(0).getSaasRoleUser().getPersonId();
+ break;
+ case TEAM:
+ //获取personId,在workspaceId下项目班组对象,(角色列表:班组长/带班长/小组长/工人)
+ OrganizationalNodeVO projectTeamUser = this.fetchProjectTeamByWorkspacePersonIdJobCodes(workspaceId, personId, JobCodeUtils.CHAT_GROUP_TEAM_MEMBER_JOB_CODES);
+ resp.setTeamNodeId(projectTeamUser.getId());
+
+ //查询项目Id下的班组的班组长
+ List projectTeamLeaderList = this.organizationalNodeUserApiGateway.searchNodeUser( projectTeamUser.getId(), workspaceId, Sets.newHashSet(JobCodeUtils.WORKSPACE_TEAM_ADMIN_JOB_CODE), null);
+ BizAssertions.assertNotEmpty(projectTeamLeaderList, "personId:{}在workspaceId:{},找不到班组长信息");
+ ownerId = projectTeamLeaderList.get(0).getPersonId();
+ break;
+ default:
+ BizAssertions.assertTrue(false, "人群不存在");
+ }
+ if (Objects.isNull(ownerId)) {
+ this.sendDingRobot(String.format("群主用户不存在,workspaceId:%d,ouId:%d,personId:%d,crowType:%s", workspaceId, ouId, personId, crowType));
+ }
+ BizAssertions.assertNotNull(ownerId, "群主用户不存在");
+
+ PersonProfileDto owner = profilesApiGateway.getPersonProfileById(ownerId);
+ BizAssertions.assertFalse(Objects.isNull(owner) || Objects.isNull(owner.getId()), "超管用户不存在");
+
+ UserAccountReq userAccountReq = UserAccountReq.builder()
+ .appType(AppTypeEnum.CMP.getCode())//管理端:AppTypeEnum.CMP.getCode();工人端:AppTypeEnum.CM.getCode()
+ .userId(owner.getId().toString()).nickName(owner.getRealName())
+ .headImageUrl(owner.getAvatarUrl())
+ .organizationalUnitId(ouId)
+ .build();
+ UserAccountResp userAccountResp = accountService.generateAccount(userAccountReq, iNotifyService);
+ resp.setOwnerId(owner.getId());
+ resp.setOwnerImAccount(userAccountResp.getImAccount());
+ return resp;
+ }
+
+ /**
+ * 获取IM账号
+ */
+ public Pair, Set> fetchUsersByWorkspaceOuId(ChatGroupCreateReq.CrowTypeEnum crowType, Long workspaceId, Long ouId, Set jobCodes,Long personId) {
+ switch (crowType) {
+ case WORKSPACE:
+ List userListWhenWorkspace = organizationalNodeUserApiGateway.fetchNodeUsersByWorkspaceIdJobCodes(workspaceId, jobCodes);
+ BizAssertions.assertNotEmpty(userListWhenWorkspace, "人群是项目时,管理员为空,workspaceId:{},ouId:{}", workspaceId, ouId);
+ Set personListWhenWorkspace = userListWhenWorkspace.stream().map(OrganizationalNodeUserVO::getPersonId).collect(Collectors.toSet());
+ return Pair.of(personListWhenWorkspace, Sets.newHashSet());
+ case OU:
+ List userListWhenOuId = organizationalNodeUserApiGateway.fetchNodeUsersByWorkspaceOuIdJobCodes(workspaceId,ouId,jobCodes);
+ BizAssertions.assertNotEmpty(userListWhenOuId, "人群是单位时,管理员为空,workspaceId:{},ouId:{}", workspaceId, ouId);
+ Set personIdListWhenOuId = userListWhenOuId.stream().map(OrganizationalNodeUserVO::getPersonId).collect(Collectors.toSet());
+ return Pair.of(personIdListWhenOuId, Sets.newHashSet());
+ case TEAM:
+ //获取personId,在workspaceId下项目班组对象,(角色列表:班组长/带班长/小组长/工人)
+ OrganizationalNodeVO projectTeamUser = this.fetchProjectTeamByWorkspacePersonIdJobCodes(workspaceId, personId, JobCodeUtils.CHAT_GROUP_TEAM_MEMBER_JOB_CODES);
+
+ //班组长/带班长
+ Set teamerList = this.fetchUsersByWorkspaceIdJobCodes(workspaceId, ouId,projectTeamUser.getId(), Sets.newHashSet("projTeamLeader", "projectTeamManager"), true);
+ Set workerList = this.fetchUsersByWorkspaceIdJobCodes(workspaceId, ouId,projectTeamUser.getId(), Sets.newHashSet("projWorker","projectTeamGPLeader"), false);
+ return Pair.of(teamerList,workerList);
+ default:
+ throw new ServiceException("人群不存在");
+ }
+ }
+
+ /**
+ * 通过项目Id/人员Id/岗位code集合,获取项目内班组的OrganizationalNode组织对象
+ * @param workspaceId 项目Id
+ * @param personId 人员Id
+ * @param jobCodes 岗位code集合
+ * @return 项目内班组的OrganizationalNode组织对象
+ */
+ private OrganizationalNodeVO fetchProjectTeamByWorkspacePersonIdJobCodes(Long workspaceId,Long personId, Set jobCodes) {
+ List projectTeamUserList = this.organizationalNodeUserApiGateway.searchNodeUser( null, workspaceId, jobCodes, personId);
+ BizAssertions.assertNotEmpty(projectTeamUserList, "personId:{}在workspaceId:{},找不到项目内组织用户信息");
+
+ List nodeIdList = projectTeamUserList.stream().map(OrganizationalNodeUserVO::getOrganizationalNodeId).collect(Collectors.toList());
+ BizAssertions.assertNotEmpty(nodeIdList, "personId:{}在workspaceId:{},找不到项目内组织信息");
+
+ List nodeVOList = organizationalNodeApiGateway.fetchNodesByNodeIds(nodeIdList);
+ BizAssertions.assertNotEmpty(nodeVOList, "personId:{}在workspaceId:{},找不到项目内组织信息");
+
+ //只获取项目内班组,节点类型 1.部门 2.平台班组 3.小组 4.项目内班组 5.项目内小组
+ List nodeVOTeamList = nodeVOList.stream().filter(item -> item.getNodeType().equals(4)).collect(Collectors.toList());
+ BizAssertions.assertNotEmpty(nodeVOTeamList, "personId:{}在workspaceId:{},找不到项目内班组信息");
+
+ return nodeVOTeamList.get(0);
+ }
+
+ private Set fetchUsersByWorkspaceIdJobCodes(Long workspaceId, Long ouId, Long nodeId,Set jobCodes, boolean isAdmin) {
+ List userListWhenTeamer = organizationalNodeUserApiGateway.fetchNodeUsersByWorkspaceNodeIdJobCodes(workspaceId, nodeId, jobCodes);
+ BizAssertions.assertTrue(CollectionUtils.isNotEmpty(userListWhenTeamer) && isAdmin, "人群是班组时,{}为空,workspaceId:{},ouId:{}", isAdmin ? "班组长/带班长" : "工人", workspaceId, ouId);
+ return userListWhenTeamer.stream().map(OrganizationalNodeUserVO::getPersonId).collect(Collectors.toSet());
+ }
+
+ /**
+ * 获取群聊
+ */
+ @Override
+ public ChatGroupQueryResp chatGroupQuery(ChatGroupQueryReq req) {
+ ChatGroupQueryResponse chatGroupQueryResponse = this.imChannelProvider.chatGroupQueryByTids(req.getTids(), req.getOpe());
+ return BeanUtil.copyProperties(chatGroupQueryResponse, ChatGroupQueryResp.class);
+ }
+
+ /**
+ * 根据人群,返回jobCode
+ */
+ @Override
+ public Set buildJobCodesByCrowType(ChatGroupCreateReq.CrowTypeEnum crowType) {
+ switch (crowType) {
+ case WORKSPACE:
+ return Sets.newHashSet(JobCodeUtils.CHAT_GROUP_ADMIN_JOB_CODES);
+ case OU:
+ return Sets.newHashSet(JobCodeUtils.CHAT_GROUP_ADMIN_JOB_CODES);
+ case TEAM:
+ return Sets.newHashSet(JobCodeUtils.CHAT_GROUP_TEAM_MEMBER_JOB_CODES);// TODO
+ default:
+ throw new ServiceException("人群不存在");
+ }
+ }
+
+ /**
+ * 更改群主
+ * @param chatGroupId 群聊Id
+ * @param oldAccIdOwner 原群主账号
+ * @param newAccIdOwner 新群主账号
+ */
+ @Override
+ public void changeOwner(Long chatGroupId, String oldAccIdOwner, String newAccIdOwner) {
+
+ //根据群聊Id,获取群聊
+ ChatGroup chatGroup = this.getById(chatGroupId);
+ try {
+ ChangeOwnerRequest changeOwnerRequest = new ChangeOwnerRequest();
+ changeOwnerRequest.setTid(chatGroup.getTid());
+ changeOwnerRequest.setOwner(oldAccIdOwner);
+ changeOwnerRequest.setNewowner(newAccIdOwner);
+ changeOwnerRequest.setLeave(1);
+
+ nimChannelService.changeOwner(changeOwnerRequest);
+ chatGroupUserService.changeOwner(chatGroupId, oldAccIdOwner, newAccIdOwner);
+ operateLogService.create(OperateLogTypeEnum.USER_ENTER_CHAT_GROUP, String.format("changeOwner-success,chatGroupId:%d,oldAccIdOwner:%s,newAccIdOwner:%s", chatGroupId, oldAccIdOwner, newAccIdOwner));
+ } catch (ServiceException serviceException) {
+ operateLogService.create(OperateLogTypeEnum.CHANGE_OWNER, String.format("changeOwner-fail,chatGroupId:%d,oldAccIdOwner:%s,newAccIdOwner:%s", chatGroupId, oldAccIdOwner, newAccIdOwner));
+ String error = String.format("更改群主失败,群聊名称:%s,原群主账号:%s,新群主账号:%s", chatGroup.getName(), oldAccIdOwner, newAccIdOwner);
+ this.sendDingRobot(error);
+ }
+ }
+
+
+ /**
+ * 拉人进群
+ * @param chatGroupId 群Id
+ * @param tid 网易云信群Id
+ * @param owner 群主
+ * @param members im账号集合
+ * @param groupName 群名称
+ */
+ @Override
+ public void userAddChatGroup(Long chatGroupId, String tid, String owner, List members, String groupName) {
+ ChatGroupUserGenericSearchReq req = new ChatGroupUserGenericSearchReq();
+ req.setChatGroupId(chatGroupId);
+
+ Integer chatGroupCount = this.chatGroupUserService.genericQueryCount(req);
+ ChatGroup chatGroup = this.getById(chatGroupId);
+
+ //超过限制人数校验
+ if (chatGroupCount > chatGroupMaxMemberCount) {
+ String error = String.format("群组:%s,超过人数:%d,项目Id:%d,人群类型:%s", groupName, chatGroupMaxMemberCount, chatGroup.getWorkspaceId(), chatGroup.getCrowType());
+ this.sendDingRobot(error);
+ return ;
+ }
+
+ UserAddChatGroupRequest request = new UserAddChatGroupRequest();
+ request.setTid(tid);
+ request.setOwner(owner);
+
+ request.setMembers(members);
+ request.setMsg("进入群:" + groupName);
+ try {
+ nimChannelService.userAddChatGroup(request);
+ chatGroupUserService.chatGroupUserBatchCreate(chatGroupId,tid, members, ChatGroupStatusEnum.SUCCESS,null, chatGroup.getCrowType());
+ operateLogService.create(OperateLogTypeEnum.USER_ENTER_CHAT_GROUP, String.format("user:%s,enter chatGroup:%s,owner:%s", JSON.toJSONString(members), tid, owner));
+ } catch (ServiceException serviceException) {
+ String error = String.format("errorCode: %s;msg:%s" ,serviceException.getErrorCode(), serviceException.getMessage());
+ chatGroupUserService.chatGroupUserBatchCreate(chatGroupId,tid, members, ChatGroupStatusEnum.FAIL,error, chatGroup.getCrowType());
+ operateLogService.create(OperateLogTypeEnum.USER_ENTER_CHAT_GROUP, String.format("user:%s,enter-error chatGroup:%s,owner:%s", JSON.toJSONString(members), tid, owner));
+ this.sendDingRobot(error);
+ }
+ }
+
+ /**
+ * 踢出群
+ * @param tid 群Id
+ * @param owner 群主
+ * @param imAccount im账号
+ */
+ @Override
+ public void kickChatGroup(Long chatGroupId, String tid, String owner, String imAccount) {
+ KickChatGroupRequest kickChatGroupRequest = new KickChatGroupRequest();
+ kickChatGroupRequest.setTid(tid);
+ kickChatGroupRequest.setOwner(owner);
+ kickChatGroupRequest.setMember(imAccount);
+ try {
+ nimChannelService.kickChatGroup(kickChatGroupRequest);
+ chatGroupUserService.chatGroupUserDelete(chatGroupId, imAccount);
+ operateLogService.create(OperateLogTypeEnum.USER_EXIT_CHAT_GROUP, String.format("user:%s,exit chatGroup:%s,owner:%s", imAccount, tid, owner));
+ } catch (ServiceException serviceException) {
+ String error = String.format("errorCode: %s;msg:%s" ,serviceException.getErrorCode(), serviceException.getMessage());
+ operateLogService.create(OperateLogTypeEnum.USER_EXIT_CHAT_GROUP, String.format("user:%s,exit-error chatGroup:%s,owner:%s,error", imAccount, tid, owner, error));
+ this.sendDingRobot(error);
+ }
+ }
+
+ @Override
+ public void sendDingRobot(String content) {
+ if (BooleanUtils.isNotTrue(chatGroupConfig.getDingTalkBotEnabled())) {
+ log.info("DingTalkBotEnabled = false,屏蔽钉钉消息发送。content = {}", content);
+ return;
+ }
+ DingTalkUtil.sendMessage("【环境:" + env + "】\n" + content, chatGroupConfig.getDingTalkBotAccessToken(), chatGroupConfig.getDingTalkBotSecret());
+ }
+
+}
+
+@Data
+class ChatGroupFetchOwnerResp {
+ private Long ownerId;
+
+ private String ownerImAccount;
+
+ private Long teamNodeId;
+
+ private String desc;
}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupUserServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupUserServiceImpl.java
new file mode 100644
index 0000000..77645da
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/ChatGroupUserServiceImpl.java
@@ -0,0 +1,106 @@
+package cn.axzo.im.service.impl;
+
+import cn.axzo.im.center.api.vo.req.ChatGroupCreateReq;
+import cn.axzo.im.center.api.vo.req.ChatGroupUserGenericSearchReq;
+import cn.axzo.im.center.common.enums.ChatGroupStatusEnum;
+import cn.axzo.im.center.common.enums.ChatGroupUserTypeEnum;
+import cn.axzo.im.dao.mapper.ChatGroupUserMapper;
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.entity.ChatGroupUser;
+import cn.axzo.im.service.ChatGroupService;
+import cn.axzo.im.service.ChatGroupUserService;
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/05
+ * @desc 群聊用户
+ */
+@Slf4j
+@Service
+@RefreshScope
+public class ChatGroupUserServiceImpl extends ServiceImpl
+ implements ChatGroupUserService {
+
+ @Autowired
+ private ChatGroupService chatGroupService;
+
+ @Override
+ public Boolean chatGroupUserCreate(Long chatGroupId, String tid, String accId, ChatGroupStatusEnum status, String remark, ChatGroupCreateReq.CrowTypeEnum crowType, ChatGroupUserTypeEnum type) {
+ return this.saveOrUpdate(ChatGroupUser.builder()
+ .chatGroupId(chatGroupId)
+ .tid(tid)
+ .accId(accId)
+ .status(status)
+ .crowType(crowType)
+ .type(type)
+ .remark(remark).build());
+ }
+
+ /**
+ * 更改群主
+ * @param chatGroupId 群聊Id
+ * @param oldAccId 原有账号
+ * @param newAccId 新账号
+ */
+ @Override
+ public void changeOwner(Long chatGroupId, String oldAccId, String newAccId) {
+
+ this.chatGroupUserDelete(chatGroupId, oldAccId);
+ ChatGroup chatGroup = chatGroupService.getById(chatGroupId);
+ this.chatGroupUserCreate(chatGroupId, chatGroup.getTid(), newAccId, ChatGroupStatusEnum.SUCCESS, null, chatGroup.getCrowType(), ChatGroupUserTypeEnum.OWNER);
+ }
+
+
+
+ @Override
+ public Boolean chatGroupUserBatchCreate(Long chatGroupId, String tid, List members, ChatGroupStatusEnum status, String remark, ChatGroupCreateReq.CrowTypeEnum crowType) {
+ if (CollectionUtils.isEmpty(members)) {
+ return true;
+ }
+ List chatGroupUsers = members.stream().map(item -> ChatGroupUser.builder().
+ chatGroupId(chatGroupId)
+ .tid(tid)
+ .accId(item)
+ .status(status)
+ .crowType(crowType)
+ .remark(remark).build()).collect(Collectors.toList());
+ return this.saveBatch(chatGroupUsers);
+ }
+
+ @Override
+ public Boolean chatGroupUserDelete(Long chatGroupId, String accId) {
+ return this.lambdaUpdate().eq(ChatGroupUser::getChatGroupId, chatGroupId)
+ .eq(ChatGroupUser::getAccId, accId)
+ .eq(ChatGroupUser::getIsDelete, 0)
+ .setSql("is_delete = id").update();
+ }
+
+ private LambdaQueryChainWrapper genericQueryWrapper(ChatGroupUserGenericSearchReq req) {
+ return this.lambdaQuery()
+ .eq(Objects.nonNull(req.getChatGroupId()), ChatGroupUser::getChatGroupId, req.getChatGroupId())
+ .eq(StringUtils.isNotBlank(req.getTid()), ChatGroupUser::getTid, req.getTid())
+ .eq(StringUtils.isNotBlank(req.getAccId()), ChatGroupUser::getAccId, req.getAccId());
+ }
+
+ @Override
+ public List genericQuery(ChatGroupUserGenericSearchReq req) {
+ return this.genericQueryWrapper(req).list();
+ }
+
+ @Override
+ public Integer genericQueryCount(ChatGroupUserGenericSearchReq req) {
+ return this.genericQueryWrapper(req).count();
+ }
+}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/ComplaintServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/ComplaintServiceImpl.java
new file mode 100644
index 0000000..1c6fd96
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/ComplaintServiceImpl.java
@@ -0,0 +1,59 @@
+package cn.axzo.im.service.impl;
+
+import cn.axzo.im.center.api.vo.req.ComplaintCreateReq;
+import cn.axzo.im.dao.mapper.ComplaintMapper;
+import cn.axzo.im.entity.Complaint;
+import cn.axzo.im.service.ChatGroupService;
+import cn.axzo.im.service.ComplaintService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/05
+ * @desc 群聊
+ */
+@Slf4j
+@Service
+public class ComplaintServiceImpl extends ServiceImpl
+ implements ComplaintService {
+
+
+ @Autowired
+ private ChatGroupService chatGroupService;
+
+ @Override
+ public void complaintCreate(ComplaintCreateReq req) {
+ chatGroupService.sendDingRobot(String.format("IM投诉,fromId:%s,toId:%s,类型:%s,投诉内容:%s", req.getFromId(), req.getTaccId(), req.getType(), req.getComplaintContent()));
+ // 保存投诉
+ this.saveOrUpdate(this.buildComplaint(req));
+ }
+
+ /**
+ * 构建投诉
+ */
+ private Complaint buildComplaint(ComplaintCreateReq req) {
+ String toId = "";
+ String tid = "";
+ switch (req.getType()) {
+ case GROUP:
+ tid = req.getTaccId();
+ break;
+ case PRIVATE:
+ toId = req.getTaccId();
+ break;
+
+ }
+
+ return Complaint.builder()
+ .complaintContent(req.getComplaintContent())
+ .type(req.getType())
+ .fromId(req.getFromId())
+ .toId(toId)
+ .tid(tid)
+ .creator(req.getCreator())
+ .build();
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/DingDingRobotServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/DingDingRobotServiceImpl.java
new file mode 100644
index 0000000..2851db1
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/DingDingRobotServiceImpl.java
@@ -0,0 +1,28 @@
+package cn.axzo.im.service.impl;
+
+import cn.axzo.im.config.ChatGroupConfig;
+import cn.axzo.im.service.DingDingRobotService;
+import cn.axzo.im.utils.DingTalkUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.BooleanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DingDingRobotServiceImpl implements DingDingRobotService {
+ @Value("${spring.profiles.active}")
+ private String env;
+ private final ChatGroupConfig chatGroupConfig;
+
+ @Override
+ public void send(String content) {
+ if (BooleanUtils.isNotTrue(chatGroupConfig.getDingTalkBotEnabled())) {
+ log.info("DingTalkBotEnabled = false,屏蔽钉钉消息发送。content = {}", content);
+ return;
+ }
+ DingTalkUtil.sendMessage("【环境:" + env + "】\n" + content, chatGroupConfig.getDingTalkBotAccessToken(), chatGroupConfig.getDingTalkBotSecret());
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/OperateLogServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/OperateLogServiceImpl.java
new file mode 100644
index 0000000..b4f83ac
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/OperateLogServiceImpl.java
@@ -0,0 +1,38 @@
+package cn.axzo.im.service.impl;
+
+import cn.axzo.im.center.api.vo.req.ChatGroupUserGenericSearchReq;
+import cn.axzo.im.center.common.enums.OperateLogTypeEnum;
+import cn.axzo.im.dao.mapper.ChatGroupUserMapper;
+import cn.axzo.im.dao.mapper.OperateLogMapper;
+import cn.axzo.im.entity.ChatGroup;
+import cn.axzo.im.entity.ChatGroupUser;
+import cn.axzo.im.entity.OperateLog;
+import cn.axzo.im.service.ChatGroupUserService;
+import cn.axzo.im.service.OperateLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2024/11/12
+ * @desc 操作日志
+ */
+@Slf4j
+@Service
+@RefreshScope
+public class OperateLogServiceImpl extends ServiceImpl
+ implements OperateLogService {
+
+
+ /**
+ * 创建操作日志
+ */
+ @Override
+ public Boolean create(OperateLogTypeEnum typeEnum, String content) {
+ return this.saveOrUpdate(OperateLog.builder().type(typeEnum).content(content).build());
+ }
+}
\ No newline at end of file
diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/DingTalkUtil.java b/im-center-server/src/main/java/cn/axzo/im/utils/DingTalkUtil.java
new file mode 100644
index 0000000..c276072
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/utils/DingTalkUtil.java
@@ -0,0 +1,58 @@
+package cn.axzo.im.utils;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiRobotSendRequest;
+import com.dingtalk.api.response.OapiRobotSendResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+@Slf4j
+public class DingTalkUtil {
+
+ public static void sendMessage(String content, String accessToken, String secret) {
+ Long timestamp = System.currentTimeMillis();
+ String sign = getSign(timestamp, secret);
+ if (StrUtil.isBlank(sign)) {
+ return;
+ }
+
+ String url = StrUtil.format("https://oapi.dingtalk.com/robot/send?access_token={}&sign={}×tamp={}",
+ accessToken, sign, String.valueOf(timestamp));
+ DingTalkClient client = new DefaultDingTalkClient(url);
+
+ OapiRobotSendRequest req = new OapiRobotSendRequest();
+ OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
+ text.setContent(content);
+ OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
+ at.setIsAtAll(false);
+ req.setMsgtype("text");
+ req.setText(text);
+ req.setAt(at);
+ try {
+ OapiRobotSendResponse response = client.execute(req);
+ log.info("发送钉钉消息结果:{}", response);
+ } catch (Exception error) {
+ log.error("发送钉钉消息失败:{}", error.getMessage());
+ }
+ }
+
+ private static String getSign(Long timestamp, String secret) {
+ try {
+ String stringToSign = timestamp + "\n" + secret;
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+ byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+ return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
+ } catch (Exception ignored) {
+ return "";
+ }
+ }
+}
diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/JobCodeUtils.java b/im-center-server/src/main/java/cn/axzo/im/utils/JobCodeUtils.java
new file mode 100644
index 0000000..cfc17cf
--- /dev/null
+++ b/im-center-server/src/main/java/cn/axzo/im/utils/JobCodeUtils.java
@@ -0,0 +1,76 @@
+package cn.axzo.im.utils;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2014/11/07
+ */
+public class JobCodeUtils {
+
+ public static Set CHAT_GROUP_ADMIN_JOB_CODES = Sets.newHashSet("HumanResources","YoElse","YoUserexperiencedesi","YoProdcutmanager","YoHardwareengineer","YoTestengineer","YoOperationmanager","YoOperationandmainte","YoProjectimplementer","YoIndustryresearcher","YoDeveloper","YoTheprojectmanager"
+ ,"YoChieftechnologyoff","YoChieffinancialoffi","YoChiefoperatingoffi","YoChiefexecutiveoffi","cms:ou_staff","cms:ou_archivist","cms:ou_seal_administrator","cms:ou_office_staff","cms:ou_office_chief","cms:ou_legal_staff","EwLegaldepartmentman","cms:ou_hr_staff","EwHumanresourcesmana","cms:ou_accountant_staff"
+ ,"cms:ou_cashier_staff","EwFinancialcontrolle","cms:ou_contract_staff","EwContractdepartment","cms:ou_budget_staff","cms:ou_calculate_engnieer","cms:ou_cost_engnieer","cms:ou_business_manager","cms:ou_buyer","cms:ou_operation_staff","EwBusinessdepartment","cms:ou_business_vice_officer","cms:ou_chief_economic_manager"
+ ,"cms:ou_technology_staff","EwTechnicaldepartmen","EwChiefengineer","cms:ou_machine_staff","cms:ou_securityt_staff","EwSecuritydepartment","cms:ou_quantity_staff","EwQualitydepartmentm","cms:ou_material_staff","cms:ou_professional_engineer","cms:ou_engineering_manager","EwProductiongeneralm","EwThesecretary","EwThegeneralmanager"
+ ,"cms:ou_executive_director","cms:ou_chairman","other","OtProjectManager","scother","scresposible","scinspector","scSafetydesignedprison","cms:jl_project_professional_supervision_engineer","scCostofthedesignedprison","scTherepresentative","scProjectmanager","cuother","cms:js_project_security_chief","cuTheprojectintheindustry","curesposible"
+ ,"cuThecostengineer","cuProfessionalengineers","cuProjectmanager","lsother","istreasurer1","isTreasurer","lsTestsamplingmember","lsQualitativecheckmember","lsThequalitysupervis","cms:lafb_project_machine_staff","lssafe","lsSecuritychief","lsMaterialkeeper","lsGoodsreceiving","lsSuppliestobuyer","lsCompetentmaterials","cms:lafb_project_contract_staff"
+ ,"lsBudgetmember","lsLabormember","cms:lafb_project_business_vice_officer","lssurveyor","ziliaoyuan","lsThetechnician","lsTechnicalmanager","cms:lafb_project_trainee_construction_staff","lsThebuilder","isEngineeringmanager","lsProductionvicemanager","lsTheprojectmanager","psother","pstreasurer1","cms:mafb_project_accountant_staff","PSTreasurer"
+ ,"psresposible","psTestsamplingmember","psQualitativecheckmember","psThequalitysupervisor","pssafe","psSecuritychief","cms:mafb_project_machine_staff","psMaterialwarehousekeeper","psGoodsreceiving","psSuppliestobuyer","psCompetentmaterials","psLabormember","psThecontractadministrator","psBudgetmember","cms:mafb_project_business_vice_officer"
+ ,"cms:mafb_project_standard_staff","pssurveyor","psThetechnician","psTechnicalmanage","cms:mafb_project_technology_president","cms:mafb_project_electricity_manager","cms:mafb_project_trainee_construction_staff","psThebuilder","cms:mafb_project_schedule_manager","psEngineeringmanager","psProductionvicemanager","cms:mafb_project_secretary"
+ ,"cms:mafb_project_manger","psTheprojectmanager","coother","coLogisticsAdministr","coOfficeclerk","cms:zb_project_comperhensive_chief","cotreasurer","coaccounting","cms:zb_project_financial_chief","coresposible","cms:zb_project_referenc_chief","coTheoperator","coQualitativecheckmember","cms:zb_project_quantity_chief","cosafe","cms:zb_project_security_chief"
+ ,"coMechanicaladministrator","coMaterialwarehousekeeper","coGoodsreceiving","coSuppliestobuyer","cms:zb_project_material_chief","coLabormember","cms:zb_project_contract_manager","coBudgetmember","coTheprojectbusinessassistantmanager","coStandardOfficer","cosurveyor","coMeasuringhead","coThetechnician","cms:zb_project_technology_commissioner","coTechnicalprojectgeneral"
+ ,"coNearwatertheelectricityintheadministrator","coTraineebuilder","coThebuilder","coPlanadministrator","comechanicalandelectricalmanager","cms:zb_project_engineering_commissioner","coProductionvicemanageroftheproject","coSecretaryoftheproject","coProjectimplementationmanager","coTheprojectmanager");
+
+
+ /**
+ * 班组长/带班长/小组长工人
+ */
+ public static Set CHAT_GROUP_TEAM_MEMBER_JOB_CODES = Sets.newHashSet("projTeamLeader","projectTeamManager","projectTeamGPLeader","projWorker");
+
+ /**
+ * 班组长岗位code
+ */
+ private static String PROJECT_TEAM_LEADER_JOB_CODE = "projTeamLeader";
+
+
+ /**
+ * 项目管理员岗位code
+ */
+ public static final String WORKSPACE_ADMIN_JOB_CODE = "super admin";
+ /**
+ * 项目单位下的管理员岗位code
+ */
+ public static final String WORKSPACE_OU_ADMIN_JOB_CODE = "sub admin";
+ /**
+ * 项目内班组长
+ */
+ public static final String WORKSPACE_TEAM_ADMIN_JOB_CODE = "projTeamLeader";
+
+
+
+
+ /**
+ * 是否管理岗位
+ */
+ public static boolean isAdmin(String jobCode) {
+ return CHAT_GROUP_ADMIN_JOB_CODES.contains(jobCode);
+ }
+
+ /**
+ * 是否项目班组内的岗位(班组长/带班长/小组长工人)
+ *
+ */
+ public static boolean isTeam(String jobCode) {
+ return CHAT_GROUP_TEAM_MEMBER_JOB_CODES.contains(jobCode);
+ }
+
+ /**
+ * 是否是班组长
+ */
+ public static boolean isProjectTeamLeader(String jobCode) {
+ return PROJECT_TEAM_LEADER_JOB_CODE.equals(jobCode);
+ }
+
+}