From 01bd03652fef2399c2acfb1930d044b106393f85 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Thu, 21 Aug 2025 03:35:37 +0800
Subject: [PATCH] 1
---
ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateUserBusinessDto.java | 26
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserAccountUpdateVo.java | 43
ruoyi-admin/src/main/java/com/ruoyi/im/config/DynamicRequestBodyBuilder.java | 190 +++++
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java | 1
ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java | 114 +++
ruoyi-admin/src/main/java/com/ruoyi/web/controller/group/ImGroupController.java | 33 +
ruoyi-admin/src/main/java/com/ruoyi/im/config/YunxinClientConfig.java | 26
ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/NeteaseTeamServiceImpl.java | 181 +++++
ruoyi-system/src/main/java/com/ruoyi/system/domain/NeteaseTeam.java | 138 ++++
ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateAccountRequest.java | 34 +
ruoyi-admin/src/main/resources/application.yml | 16
ruoyi-admin/src/main/java/com/ruoyi/im/dto/CreateGroupDto.java | 102 +++
ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateGroupDto.java | 94 ++
ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java | 393 +++++++++++
ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java | 69 +
ruoyi-admin/src/main/java/com/ruoyi/im/service/NeteaseTeamService.java | 16
ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java | 11
ruoyi-admin/src/main/java/com/ruoyi/im/config/CreateTeamRequest.java | 124 +++
ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java | 7
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 2
ruoyi-system/src/main/java/com/ruoyi/system/mapper/NeteaseTeamMapper.java | 9
ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java | 2
ruoyi-admin/src/main/java/com/ruoyi/im/config/NeteaseResponse.java | 38 +
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java | 15
ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateUserInfoRequest.java | 207 ++++++
25 files changed, 1,836 insertions(+), 55 deletions(-)
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
index d37e153..8b94057 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -4,6 +4,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
/**
* 启动程序
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java b/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
index 6b12c7b..2ab0c4a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -2,15 +2,29 @@
import com.ruoyi.common.core.domain.R;
import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.dto.UpdateUserBusinessDto;
import com.ruoyi.im.service.ImApiServcie;
import com.ruoyi.im.dto.RegisterDto;
+import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
@RestController
@RequestMapping("/im/api")
@@ -19,6 +33,14 @@
@Autowired
private ImApiServcie imApiServcie;
+
+
+ @Value("${file.upload-dir}")
+ private String uploadDir;
+
+ @Value("${file.upload-prefix}")
+ private String prefix;
+
/**
* 获取参数配置列表
@@ -32,4 +54,88 @@
return Result.error("注册失败,请稍后再试!");
}
}
+
+
+ @PostMapping("/upload")
+ public Result uploadFile(@RequestParam("file") MultipartFile file,@RequestParam("accountId") String accountId) {
+ try {
+
+ // 1. 验证文件类型
+ String contentType = file.getContentType();
+ if (contentType == null ||
+ (!contentType.equals("image/jpeg") &&
+ !contentType.equals("image/png") &&
+ !contentType.equals("image/gif"))) {
+
+ return Result.error("只支持JPEG、PNG和GIF格式的图片!");
+ }
+
+ // 确保上传目录存在
+ File directory = new File(uploadDir);
+ if (!directory.exists()) {
+ directory.mkdirs();
+ // 设置目录权限为755 (rwxr-xr-x)
+ setPermissions(directory, "rwxr-xr-x");
+ }
+
+ // 生成唯一文件名
+ String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
+ Path filePath = Paths.get(uploadDir, fileName);
+
+ // 保存文件
+ Files.write(filePath, file.getBytes());
+
+ // 设置文件权限为644 (rw-r--r--)
+ setPermissions(filePath.toFile(), "rw-r--r--");
+
+ // 5. 调用网易云信API更新头像
+ fileName = prefix+"/"+fileName;
+ UpdateUserBusinessDto dto = new UpdateUserBusinessDto();
+ dto.setAvatar(fileName);
+ Map<String, Object> result = imApiServcie.updateUserAvatar(accountId,dto);
+ if ((Boolean) result.get("success")) {
+ return Result.success("文件上传成功");
+ } else {
+ return Result.error("上传失败!");
+ }
+ } catch (IOException e) {
+ return Result.error("文件上传失败");
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("设置文件权限失败!");
+ }
+ return Result.success();
+ }
+
+ /**
+ * 设置文件/目录权限
+ * @param file 文件或目录对象
+ * @param perm 权限字符串,如 "rw-r--r--"
+ */
+ private void setPermissions(File file, String perm) throws IOException {
+ // 获取当前操作系统
+ String os = System.getProperty("os.name").toLowerCase();
+
+ // 只有在Unix/Linux系统上才能设置POSIX权限
+ if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
+ Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(perm);
+ Files.setPosixFilePermissions(file.toPath(), permissions);
+ } else {
+ // Windows系统不支持POSIX权限,可以设置基本权限
+ file.setReadable(true, false); // 所有用户可读
+ if (perm.startsWith("rw")) {
+ file.setWritable(true, false); // 所有用户可写(如果权限字符串以rw开头)
+ } else {
+ file.setWritable(false, false);
+ }
+
+ // 如果是目录且包含执行权限,设置可执行
+ if (file.isDirectory() && perm.contains("x")) {
+ file.setExecutable(true, false);
+ }
+ }
+ }
+
+
+
}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/CreateTeamRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/CreateTeamRequest.java
new file mode 100644
index 0000000..154dc5f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/CreateTeamRequest.java
@@ -0,0 +1,124 @@
+package com.ruoyi.im.config;
+
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class CreateTeamRequest {
+ /**
+ * 群主(创建者)的 IM 账号 ID
+ */
+ @NotBlank(message = "群主账号不能为空")
+ @Size(max = 32, message = "群主账号长度不能超过32个字符")
+ private String owner_account_id;
+
+ /**
+ * 群组类型:1:高级群。2:超大群。
+ */
+ @NotNull(message = "群组类型不能为空")
+ private Integer team_type;
+
+ /**
+ * 群组名称,长度上限 64 位字符
+ */
+ @NotBlank(message = "群组名称不能为空")
+ @Size(max = 64, message = "群组名称长度不能超过64个字符")
+ private String name;
+
+ /**
+ * 群组头像的 URL 地址,长度上限 1024 位字符
+ */
+ @Size(max = 1024, message = "群组头像URL长度不能超过1024个字符")
+ private String icon;
+
+ /**
+ * 群组公告,长度上限 1024 位字符
+ */
+ @Size(max = 1024, message = "群组公告长度不能超过1024个字符")
+ private String announcement;
+
+ /**
+ * 群组简介,长度上限 512 位字符
+ */
+ @Size(max = 512, message = "群组简介长度不能超过512个字符")
+ private String intro;
+
+ /**
+ * 群组成员数上限(包含群主),默认为 200
+ */
+ private Integer members_limit = 200;
+
+ /**
+ * 自定义群组扩展字段,建议封装成 JSONObject 格式,长度上限 1024 位字符
+ */
+ @Size(max = 1024, message = "服务器扩展字段长度不能超过1024个字符")
+ private String server_extension;
+
+ /**
+ * 客户端自定义扩展字段,长度上限 1024 位字符
+ */
+ @Size(max = 1024, message = "客户端扩展字段长度不能超过1024个字符")
+ private String customer_extension;
+
+ /**
+ * 创建群组时邀请入群的成员列表
+ */
+ @NotNull(message = "邀请成员列表不能为空")
+ private List<String> invite_account_ids;
+
+ /**
+ * 邀请入群的附言,长度上限 150 位字符
+ */
+ @NotBlank(message = "邀请消息不能为空")
+ @Size(max = 150, message = "邀请消息长度不能超过150个字符")
+ private String invite_msg;
+
+ /**
+ * 自定义扩展字段,JSON 格式,长度上限 512 位字符
+ */
+ @Size(max = 512, message = "扩展字段长度不能超过512个字符")
+ private String extension;
+
+
+
+ /**
+ * 通过 SDK 侧操作申请入群的验证方式
+ * 0(默认):无需验证,直接入群
+ * 1:需要群主或管理员验证通过才能入群
+ * 2:不允许任何人申请入群
+ */
+ @NotNull(message = "入群验证方式不能为空")
+ private Integer join_mode = 0;
+
+ /**
+ * 邀请入群时是否需要被邀请人的同意
+ * 0(默认):需要被邀请人同意才能入群
+ * 1:不需要被邀请人同意,直接入群
+ */
+ private Integer agree_mode = 0;
+
+ /**
+ * 邀请权限,即谁可以邀请他人入群
+ * 0(默认):群主和管理员
+ * 1:所有人
+ */
+ private Integer invite_mode = 0;
+
+ /**
+ * 客户端修改群组信息的权限,即谁可以修改群组信息
+ * 0(默认):群主和管理员
+ * 1:所有人
+ */
+ private Integer update_team_info_mode = 0;
+
+ /**
+ * 客户端修改群自定义扩展信息权限
+ * 0(默认):群主和管理员
+ * 1:所有人
+ */
+ private Integer update_extension_mode = 0;
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/DynamicRequestBodyBuilder.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/DynamicRequestBodyBuilder.java
new file mode 100644
index 0000000..ac47f7f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/DynamicRequestBodyBuilder.java
@@ -0,0 +1,190 @@
+package com.ruoyi.im.config;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.json.JSONObject;
+
+public class DynamicRequestBodyBuilder {
+ private Map<String, Object> bodyData;
+ private Map<String, Object> configurationData;
+
+ public DynamicRequestBodyBuilder() {
+ bodyData = new HashMap<>();
+ configurationData = new HashMap<>();
+ }
+
+ /**
+ * 设置token字段
+ */
+ public DynamicRequestBodyBuilder setToken(String token) {
+ if (token != null) {
+ bodyData.put("token", token);
+ }
+ return this;
+ }
+
+ /**
+ * 设置enabled配置字段
+ */
+ public DynamicRequestBodyBuilder setEnabled(boolean enabled) {
+ configurationData.put("enabled", enabled);
+ return this;
+ }
+
+ /**
+ * 设置p2p_chat_banned配置字段
+ */
+ public DynamicRequestBodyBuilder setP2pChatBanned(boolean banned) {
+ configurationData.put("p2p_chat_banned", banned);
+ return this;
+ }
+
+ /**
+ * 设置team_chat_banned配置字段
+ */
+ public DynamicRequestBodyBuilder setTeamChatBanned(boolean banned) {
+ configurationData.put("team_chat_banned", banned);
+ return this;
+ }
+
+ /**
+ * 设置chatroom_chat_banned配置字段
+ */
+ public DynamicRequestBodyBuilder setChatroomChatBanned(boolean banned) {
+ configurationData.put("chatroom_chat_banned", banned);
+ return this;
+ }
+
+ /**
+ * 设置qchat_chat_banned配置字段
+ */
+ public DynamicRequestBodyBuilder setQchatChatBanned(boolean banned) {
+ configurationData.put("qchat_chat_banned", banned);
+ return this;
+ }
+
+ /**
+ * 设置push_enabled_when_desktop_online配置字段
+ */
+ public DynamicRequestBodyBuilder setPushEnabledWhenDesktopOnline(boolean enabled) {
+ configurationData.put("push_enabled_when_desktop_online", enabled);
+ return this;
+ }
+
+ /**
+ * 设置need_kick字段
+ */
+ public DynamicRequestBodyBuilder setNeedKick(boolean needKick) {
+ bodyData.put("need_kick", needKick);
+ return this;
+ }
+
+ /**
+ * 设置kick_notify_extension字段
+ */
+ public DynamicRequestBodyBuilder setKickNotifyExtension(String extension) {
+ if (extension != null) {
+ bodyData.put("kick_notify_extension", extension);
+ }
+ return this;
+ }
+
+ /**
+ * 批量设置配置项
+ */
+ public DynamicRequestBodyBuilder setConfiguration(Map<String, Object> config) {
+ if (config != null) {
+ configurationData.putAll(config);
+ }
+ return this;
+ }
+
+ /**
+ * 批量设置所有字段
+ */
+ public DynamicRequestBodyBuilder setAllFields(Map<String, Object> fields) {
+ if (fields != null) {
+ // 处理顶级字段
+ if (fields.containsKey("token")) {
+ setToken((String) fields.get("token"));
+ }
+ if (fields.containsKey("need_kick")) {
+ setNeedKick((Boolean) fields.get("need_kick"));
+ }
+ if (fields.containsKey("kick_notify_extension")) {
+ setKickNotifyExtension((String) fields.get("kick_notify_extension"));
+ }
+
+ // 处理配置字段
+ if (fields.containsKey("configuration")) {
+ Map<String, Object> config = (Map<String, Object>) fields.get("configuration");
+ setConfiguration(config);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 构建JSON字符串
+ */
+ public String build() {
+ // 只有在有配置数据时才添加configuration字段
+ if (!configurationData.isEmpty()) {
+ bodyData.put("configuration", new HashMap<>(configurationData));
+ }
+
+ JSONObject jsonObject = new JSONObject(bodyData);
+ return jsonObject.toString();
+ }
+
+ /**
+ * 获取Map形式的数据
+ */
+ public Map<String, Object> getBodyData() {
+ Map<String, Object> result = new HashMap<>(bodyData);
+ if (!configurationData.isEmpty()) {
+ result.put("configuration", new HashMap<>(configurationData));
+ }
+ return result;
+ }
+
+ /**
+ * 清空所有已设置的数据
+ */
+ public void clear() {
+ bodyData.clear();
+ configurationData.clear();
+ }
+
+ /**
+ * 使用示例
+ */
+ public static void main(String[] args) {
+ // 示例1: 只设置部分字段
+ DynamicRequestBodyBuilder builder = new DynamicRequestBodyBuilder();
+ String partialBody = builder
+ .setToken("custom_token_123")
+ .setEnabled(true)
+ .build();
+ System.out.println("部分字段请求体: " + partialBody);
+
+ // 示例2: 使用Map批量设置
+ builder.clear();
+ Map<String, Object> config = new HashMap<>();
+ config.put("enabled", true);
+ config.put("p2p_chat_banned", true);
+
+ Map<String, Object> fields = new HashMap<>();
+ fields.put("token", "batch_token");
+ fields.put("need_kick", true);
+ fields.put("configuration", config);
+
+ String batchBody = builder.setAllFields(fields).build();
+ System.out.println("批量设置请求体: " + batchBody);
+
+ // 示例3: 完全不设置任何字段
+ builder.clear();
+ String emptyBody = builder.build();
+ System.out.println("空请求体: " + emptyBody);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/NeteaseResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/NeteaseResponse.java
new file mode 100644
index 0000000..da544dc
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/NeteaseResponse.java
@@ -0,0 +1,38 @@
+package com.ruoyi.im.config;
+
+public class NeteaseResponse {
+ private int code;
+ private String msg;
+ private Object data;
+
+ public NeteaseResponse() {
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public boolean isSuccess() {
+ return code == 200;
+ }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateAccountRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateAccountRequest.java
new file mode 100644
index 0000000..c10bbb1
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateAccountRequest.java
@@ -0,0 +1,34 @@
+//package com.ruoyi.im.config;
+//
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//
+//public class UpdateAccountRequest {
+// @JsonProperty("props")
+// private String props;
+//
+// @JsonProperty("token")
+// private String token;
+//
+// public UpdateAccountRequest() {}
+//
+// public UpdateAccountRequest(String props, String token) {
+// this.props = props;
+// this.token = token;
+// }
+//
+// public String getProps() {
+// return props;
+// }
+//
+// public void setProps(String props) {
+// this.props = props;
+// }
+//
+// public String getToken() {
+// return token;
+// }
+//
+// public void setToken(String token) {
+// this.token = token;
+// }
+//}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateUserInfoRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateUserInfoRequest.java
new file mode 100644
index 0000000..95c3541
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/UpdateUserInfoRequest.java
@@ -0,0 +1,207 @@
+package com.ruoyi.im.config;
+
+import cn.hutool.json.JSONObject;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class UpdateUserInfoRequest {
+ private Map<String, Object> bodyData;
+
+ public UpdateUserInfoRequest() {
+ bodyData = new HashMap<>();
+ }
+
+ /**
+ * 设置name字段
+ */
+ public UpdateUserInfoRequest setName(String name) {
+ if (name != null) {
+ bodyData.put("name", name);
+ }
+ return this;
+ }
+
+ /**
+ * 设置avatar字段
+ */
+ public UpdateUserInfoRequest setAvatar(String avatar) {
+ if (avatar != null) {
+ bodyData.put("avatar", avatar);
+ }
+ return this;
+ }
+
+ /**
+ * 设置sign字段
+ */
+ public UpdateUserInfoRequest setSign(String sign) {
+ if (sign != null) {
+ bodyData.put("sign", sign);
+ }
+ return this;
+ }
+
+ /**
+ * 设置email字段
+ */
+ public UpdateUserInfoRequest setEmail(String email) {
+ if (email != null) {
+ bodyData.put("email", email);
+ }
+ return this;
+ }
+
+ /**
+ * 设置birthday字段
+ */
+ public UpdateUserInfoRequest setBirthday(String birthday) {
+ if (birthday != null) {
+ bodyData.put("birthday", birthday);
+ }
+ return this;
+ }
+
+ /**
+ * 设置mobile字段
+ */
+ public UpdateUserInfoRequest setMobile(String mobile) {
+ if (mobile != null) {
+ bodyData.put("mobile", mobile);
+ }
+ return this;
+ }
+
+ /**
+ * 设置gender字段
+ */
+ public UpdateUserInfoRequest setGender(String gender) {
+ if (gender != null) {
+ bodyData.put("gender", gender);
+ }
+ return this;
+ }
+
+ /**
+ * 设置extension字段
+ */
+ public UpdateUserInfoRequest setExtension(String extension) {
+ if (extension != null) {
+ bodyData.put("extension", extension);
+ }
+ return this;
+ }
+
+ /**
+ * 批量设置所有字段
+ */
+ public UpdateUserInfoRequest setAllFields(Map<String, Object> fields) {
+ if (fields != null) {
+ for (Map.Entry<String, Object> entry : fields.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ if (value != null) {
+ switch (key) {
+ case "name":
+ setName((String) value);
+ break;
+ case "avatar":
+ setAvatar((String) value);
+ break;
+ case "sign":
+ setSign((String) value);
+ break;
+ case "email":
+ setEmail((String) value);
+ break;
+ case "birthday":
+ setBirthday((String) value);
+ break;
+ case "mobile":
+ setMobile((String) value);
+ break;
+ case "gender":
+ setGender((String) value);
+ break;
+ case "extension":
+ setExtension((String) value);
+ break;
+ default:
+ // 忽略未知字段
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 构建JSON字符串
+ */
+ public String build() {
+ JSONObject jsonObject = new JSONObject(bodyData);
+ return jsonObject.toString();
+ }
+
+ /**
+ * 获取Map形式的数据
+ */
+ public Map<String, Object> getBodyData() {
+ return new HashMap<>(bodyData);
+ }
+
+ /**
+ * 清空所有已设置的数据
+ */
+ public void clear() {
+ bodyData.clear();
+ }
+
+ /**
+ * 使用示例
+ */
+ public static void main(String[] args) {
+ // 示例1: 只设置部分字段
+ UpdateUserInfoRequest builder = new UpdateUserInfoRequest();
+ String partialBody = builder
+ .setName("zhangsan")
+ .setEmail("zhangsan@corp.xx.com")
+ .setMobile("13312345678")
+ .build();
+ System.out.println("部分字段请求体: " + partialBody);
+
+ // 示例2: 使用Map批量设置
+ builder.clear();
+ Map<String, Object> fields = new HashMap<>();
+ fields.put("name", "lisi");
+ fields.put("avatar", "http://xxxx.xx/lisi.png");
+ fields.put("sign", "Hello World");
+ fields.put("gender", "1");
+
+ String batchBody = builder.setAllFields(fields).build();
+ System.out.println("批量设置请求体: " + batchBody);
+
+ // 示例3: 完全不设置任何字段
+ builder.clear();
+ String emptyBody = builder.build();
+ System.out.println("空请求体: " + emptyBody);
+
+ // 示例4: 设置所有字段
+ builder.clear();
+ String fullBody = builder
+ .setName("zhangsan")
+ .setAvatar("http://xxxx.xx/x.png")
+ .setSign("Hello World")
+ .setEmail("zhangsan@corp.xx.com")
+ .setBirthday("1990-01-01")
+ .setMobile("13312345678")
+ .setGender("2")
+ .setExtension("xxxx")
+ .build();
+ System.out.println("完整请求体: " + fullBody);
+ }
+}
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/YunxinClientConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/YunxinClientConfig.java
new file mode 100644
index 0000000..a3bf85a
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/YunxinClientConfig.java
@@ -0,0 +1,26 @@
+package com.ruoyi.im.config;
+
+import com.netease.nim.server.sdk.core.BizName;
+import com.netease.nim.server.sdk.core.YunxinApiHttpClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class YunxinClientConfig {
+
+ @Bean
+ public YunxinApiHttpClient yunxinApiHttpClient() {
+ String appkey = AppAuthConfig.DEFAULT_CONFIG.getAppKey();
+ String appsecret = AppAuthConfig.DEFAULT_CONFIG.getAppSecret();
+ int timeoutMillis = 5000;
+
+ try {
+ return new YunxinApiHttpClient.Builder(BizName.IM, appkey, appsecret)
+ .timeoutMillis(timeoutMillis)
+ .build();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create YunxinApiHttpClient", e);
+ }
+ }
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/dto/CreateGroupDto.java b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/CreateGroupDto.java
new file mode 100644
index 0000000..7558079
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/CreateGroupDto.java
@@ -0,0 +1,102 @@
+package com.ruoyi.im.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+@Data
+public class CreateGroupDto {
+
+ /**
+ * 群名称
+ */
+ @NotBlank(message = "群名称不能为空")
+ @Size(max = 64, message = "群名称长度不能超过64个字符")
+ private String name;
+
+ /**
+ * 群主账号
+ */
+ @NotBlank(message = "群主账号不能为空")
+ private String owner;
+
+ /**
+ * 初始群成员列表
+ */
+ @Size(min = 3, max = 10, message = "初始成员至少3人,最多10人")
+ private List<String> members;
+
+ /**
+ * 群简介/描述
+ */
+ @Size(max = 512, message = "群简介长度不能超过512个字符")
+ private String introduction;
+
+ /**
+ * 群公告
+ */
+ @Size(max = 1024, message = "群公告长度不能超过1024个字符")
+ private String announcement;
+
+ /**
+ * 群头像URL
+ */
+ private String icon;
+
+ /**
+ * 群类型:1-高级群,2-超级群
+ */
+ @NotNull(message = "群类型不能为空")
+ private Integer type;
+
+ /**
+ * 最大群成员数量
+ */
+ @NotNull(message = "最大群成员数量不能为空")
+ private Integer maxMembers;
+
+ /**
+ * 入群验证模式:0-允许任何人加入,1-需要验证,2-不允许加入
+ */
+ @NotNull(message = "验证方式不能为空")
+ private Integer joinMode;
+
+ /**
+ * 邀请权限:0-管理员,1-所有人
+ */
+ @NotNull(message = "邀请权限不能为空")
+ private Integer inviteMode;
+
+ /**
+ * 修改群信息权限:0-管理员,1-所有人
+ */
+ @NotNull(message = "修改群信息权限不能为空")
+ private Integer updateInfoMode;
+
+ /**
+ * 是否允许私聊:0-允许,1-不允许
+ */
+ @NotNull(message = "私聊设置不能为空")
+ private Integer privateChat;
+
+ /**
+ * 群二维码展示:0-展示,1-隐藏
+ */
+ @NotNull(message = "二维码展示设置不能为空")
+ private Integer qrcodeVisible;
+
+ /**
+ * 入群是否需要同意:0-不需要,1-需要
+ */
+ @NotNull(message = "入群同意设置不能为空")
+ private Integer joinApproval;
+
+ /**
+ * 群自定义扩展字段
+ */
+ private String custom;
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
index 0a7c3ae..75486fb 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
@@ -1,5 +1,6 @@
package com.ruoyi.im.dto;
+import io.swagger.models.auth.In;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@@ -14,9 +15,13 @@
@NotEmpty(message = "密码不能为空")
private String password; // 密码
- @NotEmpty(message = "确认密码不能为空")
private String confirmPassword; // 再次确认密码
+ private String name; // 昵称
+
+ private Integer type;//类型 1 批量 2 单一
+
+ private Integer number;//数量
// @NotNull(message = "验证码不能为空")
// private Integer verificationCode; // 验证码
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateGroupDto.java b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateGroupDto.java
new file mode 100644
index 0000000..e7c95ad
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateGroupDto.java
@@ -0,0 +1,94 @@
+package com.ruoyi.im.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+@Data
+public class UpdateGroupDto {
+
+
+ /**
+ * 群名称
+ */
+ @Size(max = 64, message = "群名称长度不能超过64个字符")
+ private String name;
+
+ /**
+ * 群主账号
+ */
+ private String owner;
+
+ /**
+ * 初始群成员列表
+ */
+ @Size(min = 3, max = 10, message = "初始成员至少3人,最多10人")
+ private List<String> members;
+
+ /**
+ * 群简介/描述
+ */
+ @Size(max = 512, message = "群简介长度不能超过512个字符")
+ private String introduction;
+
+ /**
+ * 群公告
+ */
+ @Size(max = 1024, message = "群公告长度不能超过1024个字符")
+ private String announcement;
+
+ /**
+ * 群头像URL
+ */
+ private String icon;
+
+ /**
+ * 群类型:1-高级群,2-超级群
+ */
+ private Integer type;
+
+ /**
+ * 最大群成员数量
+ */
+ private Integer maxMembers;
+
+ /**
+ * 入群验证模式:0-允许任何人加入,1-需要验证,2-不允许加入
+ */
+ private Integer joinMode;
+
+ /**
+ * 邀请权限:0-管理员,1-所有人
+ */
+ private Integer inviteMode;
+
+ /**
+ * 修改群信息权限:0-管理员,1-所有人
+ */
+ private Integer updateInfoMode;
+
+ /**
+ * 是否允许私聊:0-允许,1-不允许
+ */
+ private Integer privateChat;
+
+ /**
+ * 群二维码展示:0-展示,1-隐藏
+ */
+ private Integer qrcodeVisible;
+
+ /**
+ * 入群是否需要同意:0-不需要,1-需要
+ */
+ private Integer joinApproval;
+
+ /**
+ * 群自定义扩展字段
+ */
+ private String custom;
+
+}
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateUserBusinessDto.java b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateUserBusinessDto.java
new file mode 100644
index 0000000..6dc3d2f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/UpdateUserBusinessDto.java
@@ -0,0 +1,26 @@
+package com.ruoyi.im.dto;
+
+import lombok.Data;
+
+@Data
+public class UpdateUserBusinessDto {
+
+ private String name;//用户昵称
+ private String avatar;//用户头像的 URL 地址
+ private String sign;//用户签名
+ private String email;//用户邮箱地址
+ private String birthday;//用户生日
+ private String mobile;//用户手机号码
+ private String gender;//用户性别
+ private String token;//用户账号对应的登录密钥 Token
+ private Boolean enabled;//该账号是否可用。默认可用,若设为 false,则表示禁用该账号。
+ private String p2p_chat_banned;//单聊禁言标记。true:禁言。
+ private String team_chat_banned;//群聊禁言标记。true:禁言。
+ private String chatroom_chat_banned;//聊天室禁言标记。true:禁言。
+ private String qchat_chat_banned;//圈组禁言标记。true:禁言。
+ private String need_kick;//禁用用户(configuration.enabled = false)时可配置是否在禁用的同时踢掉该用户账号。如果设置为 true,则使该账号强制退出登录。
+ private String kick_notify_extension;//禁用用户(configuration.enabled = false)时可设置强制踢下线操作的扩展字段,可透传至客户端 SDK(7.7.0 及以上版本才支持),长度上限 256 位字符。
+
+
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java
index c658f6e..3fe1375 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java
@@ -1,8 +1,19 @@
package com.ruoyi.im.service;
+import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.im.comm.Result;
import com.ruoyi.im.dto.RegisterDto;
+import com.ruoyi.im.dto.UpdateUserBusinessDto;
+import com.ruoyi.system.domain.vo.UserAccountUpdateVo;
+
+import java.util.Map;
public interface ImApiServcie {
Result register(RegisterDto dto);
+
+ Map<String, Object> updateUserAvatar(String accountId, UpdateUserBusinessDto dto);
+
+ AjaxResult updateUserAccount(UserAccountUpdateVo vo);
+
+ Result batchRegister(RegisterDto dto);
}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/NeteaseTeamService.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/NeteaseTeamService.java
new file mode 100644
index 0000000..a486540
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/NeteaseTeamService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.im.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.config.CreateTeamRequest;
+import com.ruoyi.im.dto.CreateGroupDto;
+import com.ruoyi.system.domain.NeteaseTeam;
+import org.springframework.transaction.annotation.Transactional;
+
+public interface NeteaseTeamService extends IService<NeteaseTeam> {
+
+
+ AjaxResult createGroup(CreateTeamRequest request);
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
index 3baa728..f252b5e 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
@@ -2,31 +2,56 @@
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.netease.nim.server.sdk.core.BizName;
import com.netease.nim.server.sdk.core.YunxinApiHttpClient;
import com.netease.nim.server.sdk.core.YunxinApiResponse;
import com.netease.nim.server.sdk.core.exception.YunxinSdkException;
+import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.im.comm.Result;
import com.ruoyi.im.config.AppAuthConfig;
+import com.ruoyi.im.config.DynamicRequestBodyBuilder;
+import com.ruoyi.im.config.NeteaseResponse;
+import com.ruoyi.im.config.UpdateUserInfoRequest;
+import com.ruoyi.im.dto.UpdateUserBusinessDto;
import com.ruoyi.system.domain.UserAccount;
import com.ruoyi.im.service.ImApiServcie;
import com.ruoyi.im.dto.RegisterDto;
+import com.ruoyi.system.domain.vo.UserAccountUpdateVo;
import com.ruoyi.system.service.UserAccountService;
import com.ruoyi.im.util.SymmetricCryptoUtil;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
+import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
import java.time.Instant;
import java.util.*;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
@Service
@Slf4j
@@ -34,8 +59,18 @@
@Autowired
private UserAccountService userAccountService;
- // 使用单例客户端,避免重复初始化
- private YunxinApiHttpClient yunxinClient;
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ private final YunxinApiHttpClient yunxinClient;
+
+ // 使用构造函数注入(推荐)
+ @Autowired
+ public ImApiServcieImpl(YunxinApiHttpClient yunxinClient) {
+ this.yunxinClient = yunxinClient;
+ }
+
// 使用并发安全的集合存储正在生成的账号,防止重复
private final Set<Long> generatingAccounts = Collections.newSetFromMap(new ConcurrentHashMap<>());
@@ -44,17 +79,17 @@
private final AtomicLong lastTimestamp = new AtomicLong(0);
private final AtomicLong sequence = new AtomicLong(0);
- @PostConstruct
- public void init() {
- // 初始化云信客户端,只执行一次
- String appkey = AppAuthConfig.DEFAULT_CONFIG.getAppKey();
- String appsecret = AppAuthConfig.DEFAULT_CONFIG.getAppSecret();
- int timeoutMillis = 5000;
+ private static final String DEFAULT_PASSWORD = "123456";//批量注册密码
+ private static final String ENCRYPTED_PASSWORD = SymmetricCryptoUtil.encryptPassword(DEFAULT_PASSWORD); // 密码加密一次,多次使用
- this.yunxinClient = new YunxinApiHttpClient.Builder(BizName.IM, appkey, appsecret)
- .timeoutMillis(timeoutMillis)
- .build();
- }
+ private static final String YUNXIN_CREATE_PATH = "/user/create.action";
+
+ @Value("${netease.im.api-head-portrait-url}")
+ private String headPortraitUrl;
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+
@Override
@Transactional(rollbackFor = Exception.class) // 添加事务注解确保操作原子性
@@ -76,18 +111,23 @@
userAccount.setPhoneNumber(dto.getAccount());
userAccount.setCloudMessageAccount(dto.getAccount());
userAccount.setPassword(SymmetricCryptoUtil.encryptPassword(dto.getPassword()));
+ userAccount.setCreateTime(new Date());
+ userAccount.setNickname(dto.getAccount());
+
if (!userAccountService.save(userAccount)) {
return Result.error("注册失败,请重试");
}
// 注册云信账号(远程调用)
- String path = "/user/create.action";
Map<String, String> paramMap = new HashMap<>();
paramMap.put("accid", dto.getAccount());
+ if(StringUtils.isNotEmpty(dto.getName())){
+ paramMap.put("name", dto.getName());
+ }
paramMap.put("token", dto.getPassword());
- YunxinApiResponse response = yunxinClient.executeV1Api(path, paramMap);
+ YunxinApiResponse response = yunxinClient.executeV1Api(YUNXIN_CREATE_PATH, paramMap);
// 处理云信响应
String data = response.getData();
@@ -119,6 +159,7 @@
return Result.error("注册失败,请重试");
}
}
+
/**
* 优化的账号生成方法,使用雪花算法变体提高并发性能
@@ -183,4 +224,328 @@
// 组合时间戳和序列号,生成9位ID
return timestamp * 100 + seq;
}
+
+
+
+ /**
+ * 更新用户名片
+ * @param accountId 用户账号
+ * @return 操作结果
+ */
+ public Map<String, Object> updateUserAvatar(String accountId, UpdateUserBusinessDto dto) {
+ Map<String, Object> result = new HashMap<>();
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ // 生成请求参数
+ String nonce = UUID.randomUUID().toString().replace("-", "");
+ String curTime = String.valueOf(System.currentTimeMillis() / 1000);
+ String checkSum = generateCheckSum(nonce, curTime);
+
+ // 构建请求URL
+ String url = headPortraitUrl + "/im/v2/users/" + accountId;
+
+ // 构建请求头
+ HttpPatch httpPatch = new HttpPatch(url);
+ httpPatch.setHeader("Content-Type", "application/json;charset=utf-8");
+ httpPatch.setHeader("AppKey", AppAuthConfig.DEFAULT_CONFIG.getAppKey());
+ httpPatch.setHeader("Nonce", nonce);
+ httpPatch.setHeader("CurTime", curTime);
+ httpPatch.setHeader("CheckSum", checkSum);
+
+ // 构建请求体
+ UpdateUserInfoRequest builder = new UpdateUserInfoRequest();
+ if(StringUtils.isNotEmpty(dto.getMobile())){
+ builder.setMobile(dto.getMobile());
+ }else if(StringUtils.isNotEmpty(dto.getName())){
+ builder.setName(dto.getName());
+ }else if(StringUtils.isNotEmpty(dto.getSign())){
+ builder.setSign(dto.getSign());
+ }else if(StringUtils.isNotEmpty(dto.getAvatar())){
+ builder.setAvatar(dto.getAvatar());
+ }
+ String body = builder.build();
+ String jsonBody = objectMapper.writeValueAsString(body);
+ httpPatch.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
+
+ // 执行请求
+ HttpResponse response = httpClient.execute(httpPatch);
+ String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+
+ // 解析响应
+ NeteaseResponse neteaseResponse = objectMapper.readValue(responseString, NeteaseResponse.class);
+
+ if (neteaseResponse.isSuccess()) {
+ result.put("success", true);
+ result.put("message", "头像更新成功");
+ result.put("data", neteaseResponse.getData());
+ } else {
+ result.put("success", false);
+ result.put("message", "头像更新失败: " + neteaseResponse.getMsg());
+ result.put("errorCode", neteaseResponse.getCode());
+ }
+
+ } catch (Exception e) {
+ result.put("success", false);
+ result.put("message", "请求网易云信API失败: " + e.getMessage());
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * 生成校验和
+ */
+ private String generateCheckSum(String nonce, String curTime) {
+ String content = AppAuthConfig.DEFAULT_CONFIG.getAppSecret() + nonce + curTime;
+ return DigestUtils.sha1Hex(content);
+ }
+
+
+
+ @Override
+ public AjaxResult updateUserAccount(UserAccountUpdateVo vo) {
+ //更新用户名片
+ UpdateUserBusinessDto dto = new UpdateUserBusinessDto();
+ if(StringUtils.isNotEmpty(vo.getPhoneNumber())){
+ dto.setMobile(vo.getPhoneNumber());
+ }else if(StringUtils.isNotEmpty(vo.getNickname())){
+ dto.setName(vo.getNickname());
+ }else if(StringUtils.isNotEmpty(vo.getSignature())){
+ dto.setSign(vo.getSignature());
+ }
+ Map<String, Object> map = updateUserAvatar(vo.getAccountId(), dto);
+ //更新用户属性 状态 密码
+ if ((Boolean) map.get("success")) {
+ AjaxResult ajaxResult = updateAccountProperties(vo.getAccountId(), vo);
+ if(ajaxResult.isSuccess()){
+ UserAccount userAccount = userAccountService.getById(vo.getId());
+ userAccount.setPhoneNumber(vo.getPhoneNumber());
+ userAccount.setAccount(vo.getAccountId());
+ userAccount.setNickname(vo.getNickname());
+ userAccount.setPassword(SymmetricCryptoUtil.encryptPassword(vo.getPassword()));
+ userAccount.setSignature(vo.getSignature());
+ userAccount.setUpdateTime(new Date());
+ userAccountService.updateById(userAccount);
+ }else{
+ return AjaxResult.error("更新用户属性失败!");
+ }
+ } else {
+ return AjaxResult.error("更新用户名片失败!");
+ }
+ return AjaxResult.success("更新成功!");
+ }
+
+
+
+
+ /**
+ * 更新账号属性
+ * @param accountId 用户账号ID
+ * @return 操作结果
+ */
+ public AjaxResult updateAccountProperties(String accountId, UserAccountUpdateVo vo) {
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ // 生成请求参数
+ String nonce = UUID.randomUUID().toString().replace("-", "");
+ String curTime = String.valueOf(System.currentTimeMillis() / 1000);
+ String checkSum = generateCheckSum(nonce, curTime);
+
+ // 构建请求URL
+ String url = headPortraitUrl + "/im/v2/accounts/" + accountId;
+
+ // 构建请求头
+ HttpPatch httpPatch = new HttpPatch(url);
+ httpPatch.setHeader("Content-Type", "application/json;charset=utf-8");
+ httpPatch.setHeader("AppKey", AppAuthConfig.DEFAULT_CONFIG.getAppKey());
+ httpPatch.setHeader("Nonce", nonce);
+ httpPatch.setHeader("CurTime", curTime);
+ httpPatch.setHeader("CheckSum", checkSum);
+
+ // 创建构建器实例
+ DynamicRequestBodyBuilder builder = new DynamicRequestBodyBuilder();
+ if(null != vo.getStatus() && vo.getStatus() == 0){
+ builder.setEnabled(false);
+ builder.setNeedKick(true);
+ }else if(StringUtils.isNotEmpty(vo.getPassword())){
+ builder.setToken(vo.getPassword());
+ }
+ // 只设置需要的字段
+ String body = builder.build();
+ String jsonBody = objectMapper.writeValueAsString(body);
+ httpPatch.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
+
+ // 执行请求
+ HttpResponse response = httpClient.execute(httpPatch);
+ String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+
+ // 解析响应
+ NeteaseResponse neteaseResponse = objectMapper.readValue(responseString, NeteaseResponse.class);
+
+ if (neteaseResponse.isSuccess()) {
+ AjaxResult.success("账号属性更新成功");
+ } else {
+ AjaxResult.error("账号属性更新失败");
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ AjaxResult.error("请求网易云信API失败");
+ }
+
+ return AjaxResult.success();
+ }
+
+
+ /**
+ * 批量注册
+ * @param dto
+ * @return
+ */
+ @Override
+ public Result batchRegister(RegisterDto dto) {
+ if(dto.getType() == 2){
+ return register(dto);
+ }else{
+ return batchRegister(dto.getNumber());
+ }
+ }
+
+ /**
+ * 同步批量注册
+ * 注意:大批量(如超过1000)可能会造成事务过长、数据库连接占用较久,请根据实际情况调整批次大小或考虑异步方式
+ * @param count 要注册的账号数量
+ * @return 批量注册结果
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public Result batchRegister(int count) {
+ if (count <= 0) {
+ return Result.error("注册数量必须大于0");
+ }
+
+ try {
+ // 1. 生成批量账号数据
+ List<UserAccount> accountsToSave = generateBatchAccounts(count);
+ // 2. 批量插入数据库 (使用JDBC Batch,性能远高于MyBatis-Plus的saveBatch)
+ batchInsertAccounts(accountsToSave);
+ // 3. 批量注册云信账号 (并行处理)
+ Result yunxinResult = batchRegisterYunxinAccounts(accountsToSave);
+
+ if (yunxinResult.getCode() != 200) {
+ // 云信注册失败,手动触发回滚
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+ return yunxinResult;
+ }
+
+ return Result.success("成功批量注册 " + count + " 个账号");
+
+ } catch (Exception e) {
+ // 其他异常,触发回滚
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+ log.error("批量注册过程发生异常", e);
+ return Result.error("批量注册失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 生成批量的账号信息
+ * @param count 需要生成的数量
+ * @return 用户账号列表
+ */
+ private List<UserAccount> generateBatchAccounts(int count) {
+ List<UserAccount> accounts = new ArrayList<>(count);
+ Set<String> generatedAccounts = new HashSet<>(count); // 用于内存中去重
+ Random random = new Random();
+
+ for (int i = 0; i < count; i++) {
+ String account;
+ do {
+ // 生成13开头的11位随机手机号作为账号
+ account = "13" + String.format("%09d", random.nextInt(1000000000));
+ } while (generatedAccounts.contains(account)); // 确保本次批量中唯一
+
+ generatedAccounts.add(account);
+
+ UserAccount userAccount = new UserAccount();
+ userAccount.setAccount(account);
+ userAccount.setPhoneNumber(account);
+ userAccount.setCloudMessageAccount(account);
+ userAccount.setPassword(ENCRYPTED_PASSWORD); // 使用预加密的密码
+ userAccount.setCreateTime(new Date());
+ userAccount.setNickname("用户_" + account.substring(7)); // 简单生成昵称
+ userAccount.setAccountType(1); // 设置账号类型为1
+
+ accounts.add(userAccount);
+ }
+ return accounts;
+ }
+
+ /**
+ * 使用JDBC批量插入数据库,最高效的方式
+ * @param accounts 待插入的用户账号列表
+ */
+ private void batchInsertAccounts(List<UserAccount> accounts) {
+ String sql = "INSERT INTO user_account (account, phone_number, cloud_message_account, password, create_time, nickname, account_type) VALUES (?, ?, ?, ?, ?, ?, ?)";
+
+ jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
+ @Override
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
+ UserAccount account = accounts.get(i);
+ ps.setString(1, account.getAccount());
+ ps.setString(2, account.getPhoneNumber());
+ ps.setString(3, account.getCloudMessageAccount());
+ ps.setString(4, account.getPassword());
+ ps.setTimestamp(5, new java.sql.Timestamp(account.getCreateTime().getTime()));
+ ps.setString(6, account.getNickname());
+ ps.setInt(7, account.getAccountType());
+ }
+
+ @Override
+ public int getBatchSize() {
+ return accounts.size();
+ }
+ });
+ }
+
+ /**
+ * 批量注册云信账号(并行调用单个接口)
+ * @param accounts 已存入本地数据库的账号列表
+ * @return 注册结果
+ */
+ private Result batchRegisterYunxinAccounts(List<UserAccount> accounts) {
+ // 使用并行流并行调用云信接口
+ List<CompletableFuture<YunxinApiResponse>> futures = accounts.parallelStream()
+ .map(account -> CompletableFuture.supplyAsync(() -> {
+ Map<String, String> paramMap = new HashMap<>();
+ paramMap.put("accid", account.getAccount());
+ paramMap.put("token", DEFAULT_PASSWORD); // 使用明文密码
+ // 调用云信接口
+ return yunxinClient.executeV1Api(YUNXIN_CREATE_PATH, paramMap);
+ }))
+ .collect(Collectors.toList());
+
+ // 等待所有异步操作完成,并获取结果
+ List<YunxinApiResponse> responses = futures.stream()
+ .map(CompletableFuture::join)
+ .collect(Collectors.toList());
+
+ // 检查所有响应结果
+ for (YunxinApiResponse response : responses) {
+ String data = response.getData();
+ JSONObject json = JSONObject.parseObject(data);
+ int code = json.getIntValue("code");
+ if (code != 200) {
+ // 记录具体的错误信息
+ String errorAccid = json.getString("accid"); // 如果返回了accid
+ log.error("云信账号注册失败,accid: {}, 响应: {}, traceId: {}", errorAccid, data, response.getTraceId());
+ // 返回第一个遇到的错误
+ return Result.error("云信注册失败,错误码: " + code + (errorAccid != null ? ", 账号: " + errorAccid : ""));
+ }
+ }
+
+ return Result.success("所有云信账号注册成功");
+ }
+
}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/NeteaseTeamServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/NeteaseTeamServiceImpl.java
new file mode 100644
index 0000000..454be10
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/NeteaseTeamServiceImpl.java
@@ -0,0 +1,181 @@
+package com.ruoyi.im.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.netease.nim.server.sdk.core.BizName;
+import com.netease.nim.server.sdk.core.YunxinApiHttpClient;
+import com.netease.nim.server.sdk.core.YunxinApiResponse;
+import com.netease.nim.server.sdk.core.exception.YunxinSdkException;
+import com.netease.nim.server.sdk.core.http.HttpMethod;
+import com.netease.nim.server.sdk.im.v2.team.TeamV2UrlContext;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.config.AppAuthConfig;
+import com.ruoyi.im.config.CreateTeamRequest;
+import com.ruoyi.im.dto.CreateGroupDto;
+import com.ruoyi.im.service.NeteaseTeamService;
+import com.ruoyi.system.domain.NeteaseTeam;
+import com.ruoyi.system.mapper.NeteaseTeamMapper;
+import org.aspectj.weaver.loadtime.Aj;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.*;
+
+@Service
+public class NeteaseTeamServiceImpl extends ServiceImpl<NeteaseTeamMapper, NeteaseTeam> implements NeteaseTeamService {
+
+ private final YunxinApiHttpClient yunxinClient;
+
+ // 使用构造函数注入(推荐)
+ @Autowired
+ public NeteaseTeamServiceImpl(YunxinApiHttpClient yunxinClient) {
+ this.yunxinClient = yunxinClient;
+ }
+
+
+
+ @Resource
+ private NeteaseTeamMapper neteaseTeamMapper;
+
+ /**
+ * 创建群组(根据最新参数说明)
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult createGroup(CreateTeamRequest request) {
+ try {
+ // 1. 构建请求参数Map
+ Map<String, Object> paramMap = buildCreateTeamParams(request);
+
+ // 2. 将Map转换为JSON字符串
+ String jsonBody = JSON.toJSONString(paramMap);
+
+ // 3. 调用网易云信API创建群组
+ YunxinApiResponse response = yunxinClient.executeV2Api(
+ HttpMethod.POST,
+ TeamV2UrlContext.CREATE_TEAM,
+ TeamV2UrlContext.CREATE_TEAM,
+ null,
+ jsonBody
+ );
+
+ // 4. 处理响应
+ String data = response.getData();
+ JSONObject json = JSONObject.parseObject(data);
+ int code = json.getIntValue("code");
+
+ if (code != 200) {
+ log.error("网易云信创建群组失败,响应:");
+ return AjaxResult.error("创建群组失败");
+ }
+
+ // 5. 解析响应获取群组ID
+ JSONObject resultData = json.getJSONObject("data");
+ JSONObject teamInfo = resultData.getJSONObject("team_info");
+ String tid = teamInfo.getString("team_id");
+ String ownerAccountId = teamInfo.getString("owner_account_id");
+
+ // 6. 保存群组信息到本地数据库
+ int count = saveTeamToLocal(request,ownerAccountId, tid);
+ if(count > 0){
+ return AjaxResult.success("创建群组成功");
+ }
+ return AjaxResult.error("创建群组失败");
+ } catch (YunxinSdkException e) {
+ // 云信调用异常时回滚事务
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+ e.printStackTrace();
+ log.error("网易云信服务调用异常 traceId:");
+ return AjaxResult.error("创建群组失败,云信服务异常");
+ } catch (Exception e) {
+ // 其他异常同样回滚
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+ e.printStackTrace();
+ log.error("创建群组过程发生未知异常", e);
+ return AjaxResult.error("创建群组失败,系统异常");
+ }
+ }
+
+ /**
+ * 构建创建群组的参数Map
+ */
+ private Map<String, Object> buildCreateTeamParams(CreateTeamRequest request) {
+ Map<String, Object> paramMap = new HashMap<>();
+
+ // 必填参数
+ paramMap.put("owner_account_id", request.getOwner_account_id());
+ paramMap.put("team_type", request.getTeam_type());
+ paramMap.put("name", request.getName());
+ paramMap.put("invite_account_ids", request.getInvite_account_ids());
+ paramMap.put("invite_msg", request.getInvite_msg());
+
+ // 群组配置
+ Map<String, Object> configMap = new HashMap<>();
+ configMap.put("join_mode", request.getJoin_mode());
+ configMap.put("agree_mode", request.getAgree_mode());
+ configMap.put("invite_mode", request.getInvite_mode());
+ configMap.put("update_team_info_mode", request.getUpdate_team_info_mode());
+ configMap.put("update_extension_mode", request.getUpdate_extension_mode());
+ paramMap.put("configuration", configMap);
+
+ // 选填参数
+ if (request.getIcon() != null) {
+ paramMap.put("icon", request.getIcon());
+ }
+ if (request.getAnnouncement() != null) {
+ paramMap.put("announcement", request.getAnnouncement());
+ }
+ if (request.getIntro() != null) {
+ paramMap.put("intro", request.getIntro());
+ }
+ if (request.getMembers_limit() != null) {
+ paramMap.put("members_limit", request.getMembers_limit());
+ }
+ if (request.getServer_extension() != null) {
+ paramMap.put("server_extension", request.getServer_extension());
+ }
+ if (request.getCustomer_extension() != null) {
+ paramMap.put("customer_extension", request.getCustomer_extension());
+ }
+ if (request.getExtension() != null) {
+ paramMap.put("extension", request.getExtension());
+ }
+
+ return paramMap;
+ }
+
+ /**
+ * 保存群组信息到本地数据库
+ */
+ private int saveTeamToLocal(CreateTeamRequest request,String ownerAccountId, String tid) {
+ NeteaseTeam team = new NeteaseTeam();
+ BeanUtils.copyProperties(request, team);
+ team.setTid(tid);
+ team.setCreateTime(new Date());
+ team.setUpdateTime(new Date());
+ team.setStatus(0); // 正常状态
+ team.setOwnerAccountId(ownerAccountId);
+ team.setTeamType(request.getTeam_type());
+ team.setName(request.getName());
+ team.setMembersLimit(request.getMembers_limit());
+
+ // 保存配置信息
+ team.setJoinMode(request.getJoin_mode());
+ team.setAgreeMode(request.getAgree_mode());
+ team.setInviteMode(request.getInvite_mode());
+ team.setUpdateTeamInfoMode(request.getUpdate_team_info_mode());
+ team.setUpdateExtensionMode(request.getUpdate_extension_mode());
+
+ // 保存到数据库
+ int insert = neteaseTeamMapper.insert(team);
+
+ return insert;
+ }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/group/ImGroupController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/group/ImGroupController.java
new file mode 100644
index 0000000..972d236
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/group/ImGroupController.java
@@ -0,0 +1,33 @@
+package com.ruoyi.web.controller.group;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.im.config.CreateTeamRequest;
+import com.ruoyi.im.dto.CreateGroupDto;
+import com.ruoyi.im.service.NeteaseTeamService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/im/group")
+@Slf4j
+public class ImGroupController extends BaseController {
+
+
+ @Autowired
+ NeteaseTeamService neteaseGroupService;
+
+ /**
+ * 创建群组
+ */
+ @PostMapping("/create")
+ public AjaxResult createGroup(@Validated @RequestBody CreateTeamRequest request) {
+ return neteaseGroupService.createGroup(request);
+ }
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java
index 83fdc8f..095fe20 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java
@@ -7,13 +7,19 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.dto.RegisterDto;
+import com.ruoyi.im.service.ImApiServcie;
import com.ruoyi.system.domain.UserAccount;
import com.ruoyi.system.domain.vo.UserAccountUpdateVo;
import com.ruoyi.system.domain.vo.UserAccountVo;
import com.ruoyi.system.service.UserAccountService;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -22,10 +28,14 @@
@RestController
@RequestMapping("/im/user")
+@Slf4j
public class UserController extends BaseController {
@Autowired
UserAccountService userAccountService;
+
+ @Autowired
+ private ImApiServcie imApiServcie;
/**
* 获取会员列表
@@ -34,19 +44,32 @@
@GetMapping("/list")
public TableDataInfo list(UserAccountVo vo)
{
- startPage();
- List<UserAccount> list = userAccountService.list(new LambdaQueryWrapper<UserAccount>()
- .eq(UserAccount::getId, vo.getKeyword())
- .or()
- .eq(UserAccount::getPhoneNumber, vo.getKeyword())
- .or()
- .eq(UserAccount::getAccount, vo.getKeyword())
- .or()
- .eq(UserAccount::getNickname, vo.getKeyword())
- .eq(ObjectUtil.isNotEmpty(vo.getAccountType()),UserAccount::getAccountType,vo.getAccountType())
- .eq(ObjectUtil.isNotEmpty(vo.getStatus()),UserAccount::getStatus,vo.getStatus())
- .between(UserAccount::getCreateTime,vo.getStartTime(),vo.getEndTime())
- );
+ // 创建查询条件包装器
+ LambdaQueryWrapper<UserAccount> queryWrapper = new LambdaQueryWrapper<>();
+
+ // 只有当 keyword 不为空时才添加 OR 条件
+ if (ObjectUtil.isNotEmpty(vo.getKeyword())) {
+ queryWrapper.and(wrapper -> wrapper
+ .eq(UserAccount::getId, vo.getKeyword())
+ .or()
+ .eq(UserAccount::getPhoneNumber, vo.getKeyword())
+ .or()
+ .eq(UserAccount::getAccount, vo.getKeyword())
+ .or()
+ .eq(UserAccount::getNickname, vo.getKeyword())
+ );
+ }
+
+ // 添加其他条件
+ queryWrapper
+ .eq(ObjectUtil.isNotEmpty(vo.getAccountType()), UserAccount::getAccountType, vo.getAccountType())
+ .eq(ObjectUtil.isNotEmpty(vo.getStatus()), UserAccount::getStatus, vo.getStatus())
+ .between(ObjectUtil.isAllNotEmpty(vo.getStartTime(), vo.getEndTime()),
+ UserAccount::getCreateTime, vo.getStartTime(), vo.getEndTime());
+
+ // 默认按创建时间倒序
+ queryWrapper.orderByDesc(UserAccount::getCreateTime);
+ List<UserAccount> list = userAccountService.list(queryWrapper);
return getDataTable(list);
}
@@ -54,11 +77,15 @@
* 修改会员
*/
@PreAuthorize("@ss.hasPermi('im:user:updateUserAccount')")
- @GetMapping("/updateUserAccount")
+ @PostMapping("/updateUserAccount")
public AjaxResult updateUserAccount(UserAccountUpdateVo vo) {
try {
UserAccount userAccount = userAccountService.getById(vo.getId());
+ if(ObjectUtil.isEmpty(userAccount)){
+ return AjaxResult.error("会员不存在!");
+ }
+ return imApiServcie.updateUserAccount(vo);
}catch (Exception e){
e.printStackTrace();
logger.error("修改会员失败!");
@@ -66,4 +93,18 @@
return AjaxResult.success();
}
+ /**
+ * 批量注册
+ */
+ @PostMapping("/batchRegister")
+ public Result batchRegister(@Validated RegisterDto dto){
+ try {
+ return imApiServcie.batchRegister(dto);
+ }catch (Exception e){
+ log.error("批量注册报错:",e);
+ return Result.error("注册失败,请稍后再试!");
+ }
+ }
+
+
}
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index db8c314..45a5a3e 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -30,7 +30,9 @@
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
-
+ multipart:
+ max-file-size: 10MB
+ max-request-size: 10MB
# 日志配置
logging:
level:
@@ -140,4 +142,14 @@
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
- urlPatterns: /system/*,/monitor/*,/tool/*
\ No newline at end of file
+ urlPatterns: /system/*,/monitor/*,/tool/*
+
+
+file:
+ upload-dir: /www/wwwroot/img
+ upload-prefix: https://img.freessl.help
+
+# 网易云信配置
+netease:
+ im:
+ api-head-portrait-url: https://open.yunxinapi.com
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index 222ba3f..d27572d 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -116,6 +116,8 @@
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/im/api/**").permitAll()
+ .antMatchers("/im/user/**").permitAll()
+ .antMatchers("/im/group/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/NeteaseTeam.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NeteaseTeam.java
new file mode 100644
index 0000000..ada3659
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NeteaseTeam.java
@@ -0,0 +1,138 @@
+package com.ruoyi.system.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("netease_team")
+public class NeteaseTeam {
+ /**
+ * 主键ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 网易云信群ID
+ */
+ private String tid;
+
+ /**
+ * 群名称
+ */
+ private String name;
+
+ /**
+ * 群主账号
+ */
+ private String ownerAccountId;
+
+ /**
+ * 群类型:1-高级群,2-超大群
+ */
+ private Integer teamType;
+
+ /**
+ * 群头像URL
+ */
+ private String icon;
+
+ /**
+ * 群公告
+ */
+ private String announcement;
+
+ /**
+ * 群简介
+ */
+ private String intro;
+
+ /**
+ * 最大群成员数量
+ */
+ private Integer membersLimit;
+
+ /**
+ * 服务器扩展字段
+ */
+ private String serverExtension;
+
+ /**
+ * 客户端扩展字段
+ */
+ private String customerExtension;
+
+ /**
+ * 扩展字段
+ */
+ private String extension;
+
+ /**
+ * 入群验证模式:0-无需验证,直接入群,1-需要验证,2-不允许任何人申请入群
+ */
+ private Integer joinMode;
+
+ /**
+ * 同意模式:0-需要被邀请人同意,1-不需要被邀请人同意
+ */
+ private Integer agreeMode;
+
+ /**
+ * 邀请权限:0-群主和管理员,1-所有人
+ */
+ private Integer inviteMode;
+
+ /**
+ * 修改群信息权限:0-群主和管理员,1-所有人
+ */
+ private Integer updateTeamInfoMode;
+
+ /**
+ * 修改扩展字段权限:0-群主和管理员,1-所有人
+ */
+ private Integer updateExtensionMode;
+
+ /**
+ * 是否开启安全通
+ */
+ private Boolean antispamEnabled;
+
+ /**
+ * 安全通业务配置(JSON格式存储)
+ */
+ private String antispamBusinessConfig;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 状态:0-正常,1-解散
+ */
+ private Integer status;
+
+ /**
+ * 群成员数量(需要定期同步更新)
+ */
+ private Integer memberCount;
+
+ /**
+ * 群管理员列表(JSON格式存储)
+ */
+ private String managers;
+
+ /**
+ * 自定义字段,用于存储其他信息
+ */
+ private String customData;
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserAccountUpdateVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserAccountUpdateVo.java
index 4acaeb1..e6dac8f 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserAccountUpdateVo.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserAccountUpdateVo.java
@@ -23,6 +23,9 @@
@TableId(type = IdType.AUTO) // 主键自增
private Integer id;
+ // 云信账号
+ private String accountId;
+
// 手机号(唯一)
private String phoneNumber;
@@ -30,34 +33,34 @@
private String password;
// 账号类型: 0:真实 1:虚拟
- private Integer accountType = 0;
+ private Integer accountType;
// 昵称
private String nickname;
- // 账号状态: 0:正常 1:禁用
- private Integer status = 0;
+ // 账号状态: 1:正常 0:禁用
+ private Integer status;
// 个性签名
private String signature;
- // 是否支持昵称搜索
- private Boolean supportNicknameSearch = true;
+// // 是否支持昵称搜索
+// private Boolean supportNicknameSearch = true;
+//
+// // 是否支持手机搜索
+// private Boolean supportPhoneSearch = true;
+//
+// // 是否支持ID搜索
+// private Boolean supportIdSearch = true;
+//
+// // 是否支持用户名搜索
+// private Boolean supportUsernameSearch = true;
+//
+// // 是否申请删除账号
+// private Boolean requestDelete = true;
- // 是否支持手机搜索
- private Boolean supportPhoneSearch = true;
-
- // 是否支持ID搜索
- private Boolean supportIdSearch = true;
-
- // 是否支持用户名搜索
- private Boolean supportUsernameSearch = true;
-
- // 是否申请删除账号
- private Boolean requestDelete = true;
-
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @TableField(fill = FieldFill.INSERT_UPDATE)
- private Date updateTime;
+// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+// @TableField(fill = FieldFill.INSERT_UPDATE)
+// private Date updateTime;
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NeteaseTeamMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NeteaseTeamMapper.java
new file mode 100644
index 0000000..873d5d7
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NeteaseTeamMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.system.domain.NeteaseTeam;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface NeteaseTeamMapper extends BaseMapper<NeteaseTeam> {
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java
index d469d22..f1a4db4 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java
@@ -1,7 +1,9 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.UserAccount;
+import com.ruoyi.system.domain.vo.UserAccountUpdateVo;
public interface UserAccountService extends IService<UserAccount> {
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java
index 8ba90d0..a5aca1d 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java
@@ -1,11 +1,26 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.netease.nim.server.sdk.core.YunxinApiHttpClient;
+import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.UserAccount;
+import com.ruoyi.system.domain.vo.UserAccountUpdateVo;
import com.ruoyi.system.mapper.UserAccountMapper;
import com.ruoyi.system.service.UserAccountService;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
/**
* @program: ruoyiim
* @description:
--
Gitblit v1.9.3