diff --git a/pom.xml b/pom.xml index c073769..ba855e2 100644 --- a/pom.xml +++ b/pom.xml @@ -21,11 +21,6 @@ 4.9.0 - - org.springframework.boot - spring-boot-starter-websocket - - commons-fileupload commons-fileupload @@ -63,11 +58,7 @@ 3.0.2 - - org.bytedeco - javacv-platform - 1.5.9 - + mysql mysql-connector-java diff --git a/src/main/java/com/realtime/config/ClientVoiceHandler.java b/src/main/java/com/realtime/config/ClientVoiceHandler.java index 97380d1..af21d22 100644 --- a/src/main/java/com/realtime/config/ClientVoiceHandler.java +++ b/src/main/java/com/realtime/config/ClientVoiceHandler.java @@ -1,90 +1,90 @@ -package com.realtime.config; - -import com.realtime.service.DoubaoVoiceService; -import lombok.extern.slf4j.Slf4j; -import org.bytedeco.javacv.FFmpegFrameGrabber; -import org.bytedeco.javacv.FFmpegFrameRecorder; -import org.bytedeco.javacv.Frame; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.BinaryMessage; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.BinaryWebSocketHandler; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -@Slf4j -@Component -public class ClientVoiceHandler extends BinaryWebSocketHandler { - private final DoubaoVoiceService doubaoVoiceService; - - public ClientVoiceHandler(DoubaoVoiceService doubaoVoiceService) { - this.doubaoVoiceService = doubaoVoiceService; - } - - @Override - public void afterConnectionEstablished(WebSocketSession session) throws Exception { - // 客户端会话 - // 建立与豆包 API 的连接 - - System.out.println(session); - doubaoVoiceService.connect(session); - } - - @Override - protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { - System.out.println("2"); - // 接收客户端的音频数据(如 PCM 片段) - byte[] audioData = message.getPayload().array(); - // 预处理:确保格式符合豆包 API 要求(如采样率、位深) - byte[] processedData = preprocessAudio(audioData); - // 发送给豆包 API - doubaoVoiceService.sendAudio(processedData); - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { - // 关闭与豆包 API 的连接 - doubaoVoiceService.close(); - } - - private byte[] preprocessAudio(byte[] audioData) { - if (audioData == null || audioData.length == 0) { - log.warn("接收到空的音频数据,跳过处理"); - return new byte[0]; // 返回空数组,避免后续错误 - } - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(audioData)); - FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, 1)) { - - grabber.start(); - // 打印输入音频信息(调试用) - log.debug("输入音频格式:{},采样率:{},声道:{}", - grabber.getFormat(), grabber.getSampleRate(), grabber.getAudioChannels()); - - recorder.setFormat("s16le"); - recorder.setSampleRate(16000); - recorder.setAudioChannels(1); - recorder.start(); - - Frame frame; - int frameCount = 0; - while ((frame = grabber.grab()) != null) { - recorder.record(frame); - frameCount++; - } - log.debug("成功处理 {} 帧音频", frameCount); - - recorder.stop(); - grabber.stop(); - return outputStream.toByteArray(); - - } catch (Exception e) { - log.error("音频预处理失败", e); - return new byte[0]; - } - } -} +//package com.realtime.config; +// +//import com.realtime.service.DoubaoVoiceService; +//import lombok.extern.slf4j.Slf4j; +//import org.bytedeco.javacv.FFmpegFrameGrabber; +//import org.bytedeco.javacv.FFmpegFrameRecorder; +//import org.bytedeco.javacv.Frame; +//import org.springframework.stereotype.Component; +//import org.springframework.web.socket.BinaryMessage; +//import org.springframework.web.socket.CloseStatus; +//import org.springframework.web.socket.WebSocketSession; +//import org.springframework.web.socket.handler.BinaryWebSocketHandler; +// +//import java.io.ByteArrayInputStream; +//import java.io.ByteArrayOutputStream; +//import java.io.EOFException; +//@Slf4j +//@Component +//public class ClientVoiceHandler extends BinaryWebSocketHandler { +// private final DoubaoVoiceService doubaoVoiceService; +// +// public ClientVoiceHandler(DoubaoVoiceService doubaoVoiceService) { +// this.doubaoVoiceService = doubaoVoiceService; +// } +// +// @Override +// public void afterConnectionEstablished(WebSocketSession session) throws Exception { +// // 客户端会话 +// // 建立与豆包 API 的连接 +// +// System.out.println(session); +// doubaoVoiceService.connect(session); +// } +// +// @Override +// protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { +// System.out.println("2"); +// // 接收客户端的音频数据(如 PCM 片段) +// byte[] audioData = message.getPayload().array(); +// // 预处理:确保格式符合豆包 API 要求(如采样率、位深) +// byte[] processedData = preprocessAudio(audioData); +// // 发送给豆包 API +// doubaoVoiceService.sendAudio(processedData); +// } +// +// @Override +// public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { +// // 关闭与豆包 API 的连接 +// doubaoVoiceService.close(); +// } +// +// private byte[] preprocessAudio(byte[] audioData) { +// if (audioData == null || audioData.length == 0) { +// log.warn("接收到空的音频数据,跳过处理"); +// return new byte[0]; // 返回空数组,避免后续错误 +// } +// +// ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +// +// try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(audioData)); +// FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, 1)) { +// +// grabber.start(); +// // 打印输入音频信息(调试用) +// log.debug("输入音频格式:{},采样率:{},声道:{}", +// grabber.getFormat(), grabber.getSampleRate(), grabber.getAudioChannels()); +// +// recorder.setFormat("s16le"); +// recorder.setSampleRate(16000); +// recorder.setAudioChannels(1); +// recorder.start(); +// +// Frame frame; +// int frameCount = 0; +// while ((frame = grabber.grab()) != null) { +// recorder.record(frame); +// frameCount++; +// } +// log.debug("成功处理 {} 帧音频", frameCount); +// +// recorder.stop(); +// grabber.stop(); +// return outputStream.toByteArray(); +// +// } catch (Exception e) { +// log.error("音频预处理失败", e); +// return new byte[0]; +// } +// } +//} diff --git a/src/main/java/com/realtime/config/CloudApiConfig.java b/src/main/java/com/realtime/config/CloudApiConfig.java new file mode 100644 index 0000000..595de8e --- /dev/null +++ b/src/main/java/com/realtime/config/CloudApiConfig.java @@ -0,0 +1,18 @@ +package com.realtime.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 云端API配置 + */ +@Data +@Configuration +public class CloudApiConfig { + + /** + * 云端API基础URL + */ + private String baseUrl = "http://www.yuxindazhineng.com:3001/cloud_api"; +} diff --git a/src/main/java/com/realtime/config/RestTemplateConfig.java b/src/main/java/com/realtime/config/RestTemplateConfig.java new file mode 100644 index 0000000..e5698da --- /dev/null +++ b/src/main/java/com/realtime/config/RestTemplateConfig.java @@ -0,0 +1,21 @@ +package com.realtime.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * RestTemplate配置 + */ +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(10000); // 连接超时10秒 + factory.setReadTimeout(30000); // 读取超时30秒 + return new RestTemplate(factory); + } +} diff --git a/src/main/java/com/realtime/controller/CloudFileController.java b/src/main/java/com/realtime/controller/CloudFileController.java new file mode 100644 index 0000000..6d960b8 --- /dev/null +++ b/src/main/java/com/realtime/controller/CloudFileController.java @@ -0,0 +1,83 @@ +package com.realtime.controller; + +import com.realtime.model.query.*; +import com.realtime.service.CloudFileService; +import com.realtime.sysconst.Result; +import com.realtime.vo.CloudFileDownloadVo; +import com.realtime.vo.CloudFileListVo; +import com.realtime.vo.CloudFileOperationVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 云端文件管理控制器 + */ +@Slf4j +@RestController +@RequestMapping("/cloud_api/file") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CloudFileController { + + private final CloudFileService cloudFileService; + + /** + * 批量上传云端文件 + */ + @PostMapping("/upload") + public Result batchUpload( + @RequestPart("files") List files, + @RequestParam("path") String path, + @RequestParam("type") String type, + @RequestParam(value = "team_id", required = false) String teamId) { + + CloudFileUploadReq request = new CloudFileUploadReq(); + request.setPath(path); + request.setType(type); + request.setTeamId(teamId); + + // TODO: 从Session或Token获取当前用户ID + String userId = "current_user_id"; + + return Result.success(cloudFileService.batchUpload(files, request, userId)); + } + + /** + * 通过URL批量上传云端文件 + */ + @PostMapping("/upload/url") + public Result batchUploadByUrl(@RequestBody CloudFileUploadByUrlReq request) { + // TODO: 从Session或Token获取当前用户ID + String userId = "current_user_id"; + + return Result.success(cloudFileService.batchUploadByUrl(request, userId)); + } + + /** + * 下载文件 + */ + @PostMapping("/download") + public Result downloadFile(@RequestBody CloudFileDownloadReq request) { + return Result.success(cloudFileService.downloadFile(request)); + } + + /** + * 查看文件列表 + */ + @PostMapping("/list") + public Result listFiles(@RequestBody CloudFileListReq request) { + return Result.success(cloudFileService.listFiles(request)); + } + + /** + * 删除文件 + */ + @PostMapping("/delete") + public Result deleteFile(@RequestBody CloudFileDeleteReq request) { + return Result.success(cloudFileService.deleteFile(request)); + } +} diff --git a/src/main/java/com/realtime/controller/GroupController.java b/src/main/java/com/realtime/controller/GroupController.java index ce420d0..f164612 100644 --- a/src/main/java/com/realtime/controller/GroupController.java +++ b/src/main/java/com/realtime/controller/GroupController.java @@ -72,8 +72,8 @@ public class GroupController { } @PostMapping("/saveInvent") - Result saveInvent(@RequestBody List groupMembers,@RequestParam("launchContactId")String launchContactId) { - return groupMemberService.saveInvent(groupMembers,launchContactId); + Result saveInvent(@RequestBody List groupMembers, @RequestParam("launchContactId") String launchContactId) { + return groupMemberService.saveInvent(groupMembers, launchContactId); } @PostMapping("/disband") @@ -85,4 +85,6 @@ public class GroupController { Result quit(@RequestBody RemoveGroupReq removeGroupReq) { return groupMemberService.quit(removeGroupReq); } + + } diff --git a/src/main/java/com/realtime/controller/TeamController.java b/src/main/java/com/realtime/controller/TeamController.java new file mode 100644 index 0000000..c2aae8d --- /dev/null +++ b/src/main/java/com/realtime/controller/TeamController.java @@ -0,0 +1,112 @@ +package com.realtime.controller; + +import com.realtime.model.query.*; +import com.realtime.service.TeamService; +import com.realtime.sysconst.Result; +import com.realtime.vo.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 团队管理控制器 + */ +@Slf4j +@RestController +@RequestMapping("/cloud_api/team") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class TeamController { + + private final TeamService teamService; + + /** + * 获取用户可访问的团队列表 + * @param request 团队列表查询请求 + * @return 团队列表 + */ + @PostMapping("/list") + public Result getTeamList(@RequestBody TeamListReq request) { + return Result.success(teamService.getTeamList(request)); + } + + /** + * 创建团队 + * @param request 创建团队请求 + * @return 创建结果 + */ + @PostMapping("/create") + public Result createTeam(@RequestBody TeamCreateReq request) { + return Result.success(teamService.createTeam(request)); + } + + /** + * 删除团队 + * @param request 删除团队请求 + * @return 删除结果 + */ + @PostMapping("/delete") + public Result deleteTeam(@RequestBody TeamDeleteReq request) { + return Result.success(teamService.deleteTeam(request)); + } + + /** + * 修改团队信息 + * @param request 修改团队请求 + * @return 修改结果 + */ + @PostMapping("/update") + public Result updateTeam(@RequestBody TeamUpdateReq request) { + return Result.success(teamService.updateTeam(request)); + } + + /** + * 查看全部成员 + * @param request 查看成员请求 + * @return 成员列表 + */ + @PostMapping("/get_all_member") + public Result getAllMembers(@RequestBody TeamGetAllMemberReq request) { + return Result.success(teamService.getAllMembers(request)); + } + + /** + * 批量添加团队成员 + * @param request 添加成员请求 + * @return 添加结果 + */ + @PostMapping("/add_member") + public Result addMembers(@RequestBody TeamAddMemberReq request) { + return Result.success(teamService.addMembers(request)); + } + + /** + * 批量删除团队成员 + * @param request 删除成员请求 + * @return 删除结果 + */ + @PostMapping("/remove_member") + public Result removeMembers(@RequestBody TeamRemoveMemberReq request) { + return Result.success(teamService.removeMembers(request)); + } + + /** + * 调整团队成员身份 + * @param request 调整身份请求 + * @return 调整结果 + */ + @PostMapping("/adjust_member_role") + public Result adjustMemberRole(@RequestBody TeamAdjustMemberRoleReq request) { + return Result.success(teamService.adjustMemberRole(request)); + } + + /** + * 获取部门结构 + * @param request 获取部门结构请求 + * @return 部门结构 + */ + @PostMapping("/get_department") + public Result getDepartment(@RequestBody TeamGetDepartmentReq request) { + return Result.success(teamService.getDepartment(request)); + } +} diff --git a/src/main/java/com/realtime/model/baseModel/BaseQueryModel.java b/src/main/java/com/realtime/model/baseModel/BaseQueryModel.java index d6ae6e1..9b36df4 100644 --- a/src/main/java/com/realtime/model/baseModel/BaseQueryModel.java +++ b/src/main/java/com/realtime/model/baseModel/BaseQueryModel.java @@ -10,4 +10,7 @@ import lombok.EqualsAndHashCode; public class BaseQueryModel extends Page { private Long id; private String contactId; + private String accessToken; + private String teamType; + private String path; } diff --git a/src/main/java/com/realtime/model/pojo/GroupList.java b/src/main/java/com/realtime/model/pojo/GroupList.java index d09d3ce..60bb15a 100644 --- a/src/main/java/com/realtime/model/pojo/GroupList.java +++ b/src/main/java/com/realtime/model/pojo/GroupList.java @@ -37,6 +37,11 @@ public class GroupList implements Serializable { */ private Integer type; + /** + * 团队ID + */ + private String teamId; + /** * 是否需要邀请才能加入 1 = 是 0 = 否 */ diff --git a/src/main/java/com/realtime/model/query/BaseTeamReq.java b/src/main/java/com/realtime/model/query/BaseTeamReq.java new file mode 100644 index 0000000..fb84dcd --- /dev/null +++ b/src/main/java/com/realtime/model/query/BaseTeamReq.java @@ -0,0 +1,19 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 团队请求基类 + */ +@Data +public class BaseTeamReq implements Serializable { + + /** + * 访问令牌 + */ + @JsonProperty("access_token") + private String accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjhlNzY0ZjBmOTU0ODg3MWMyMmY5M2I4IiwibGFzdExvZ2luIjoiMTc2NTQzNDMyNy41MjA5ODYifQ.Vj0ovpCsziFzeR2qroYnN-3KevR78krx-rDFQ9BKlgU"; +} diff --git a/src/main/java/com/realtime/model/query/CloudFileDeleteReq.java b/src/main/java/com/realtime/model/query/CloudFileDeleteReq.java new file mode 100644 index 0000000..4a03c05 --- /dev/null +++ b/src/main/java/com/realtime/model/query/CloudFileDeleteReq.java @@ -0,0 +1,18 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 云端文件删除请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CloudFileDeleteReq extends BaseTeamReq implements Serializable { + + @JsonProperty("file_id") + private String fileId; +} diff --git a/src/main/java/com/realtime/model/query/CloudFileDownloadReq.java b/src/main/java/com/realtime/model/query/CloudFileDownloadReq.java new file mode 100644 index 0000000..9ed18b3 --- /dev/null +++ b/src/main/java/com/realtime/model/query/CloudFileDownloadReq.java @@ -0,0 +1,18 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 云端文件下载请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CloudFileDownloadReq extends BaseTeamReq implements Serializable { + + @JsonProperty("file_id") + private String fileId; +} diff --git a/src/main/java/com/realtime/model/query/CloudFileListReq.java b/src/main/java/com/realtime/model/query/CloudFileListReq.java new file mode 100644 index 0000000..65c12c5 --- /dev/null +++ b/src/main/java/com/realtime/model/query/CloudFileListReq.java @@ -0,0 +1,31 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 云端文件列表查询请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CloudFileListReq extends BaseTeamReq implements Serializable { + + /** + * 查询路径,默认根目录 / + */ + private String path = "/"; + + /** + * 文件类型:user/team + */ + private String type; + + /** + * 团队ID(type为team时必填) + */ + @JsonProperty("team_id") + private String teamId; +} diff --git a/src/main/java/com/realtime/model/query/CloudFileUploadByUrlReq.java b/src/main/java/com/realtime/model/query/CloudFileUploadByUrlReq.java new file mode 100644 index 0000000..ef5cca4 --- /dev/null +++ b/src/main/java/com/realtime/model/query/CloudFileUploadByUrlReq.java @@ -0,0 +1,47 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.List; + +/** + * 通过URL批量上传云端文件请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CloudFileUploadByUrlReq extends BaseTeamReq implements Serializable { + + /** + * 上传路径 + */ + private String path; + + /** + * 上传类型:user/team + */ + private String type; + + /** + * 团队ID(type为team时必填) + */ + @JsonProperty("team_id") + private String teamId; + + /** + * 文件URL列表 + */ + private List urls; + + /** + * 文件名列表 + */ + private List filename; + + /** + * 文件大小列表 + */ + private List size; +} diff --git a/src/main/java/com/realtime/model/query/CloudFileUploadReq.java b/src/main/java/com/realtime/model/query/CloudFileUploadReq.java new file mode 100644 index 0000000..e1d0cb3 --- /dev/null +++ b/src/main/java/com/realtime/model/query/CloudFileUploadReq.java @@ -0,0 +1,29 @@ +package com.realtime.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 云端文件上传请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CloudFileUploadReq extends BaseTeamReq implements Serializable { + + /** + * 上传路径 + */ + private String path; + + /** + * 上传类型:user/team + */ + private String type; + + /** + * 团队ID(type为team时必填) + */ + private String teamId; +} diff --git a/src/main/java/com/realtime/model/query/GroupDetailQueryReq.java b/src/main/java/com/realtime/model/query/GroupDetailQueryReq.java index 9dcea0e..29469be 100644 --- a/src/main/java/com/realtime/model/query/GroupDetailQueryReq.java +++ b/src/main/java/com/realtime/model/query/GroupDetailQueryReq.java @@ -9,4 +9,5 @@ import lombok.EqualsAndHashCode; @Data public class GroupDetailQueryReq extends BaseQueryModel { private Long groupId; + } diff --git a/src/main/java/com/realtime/model/query/TeamAddMemberReq.java b/src/main/java/com/realtime/model/query/TeamAddMemberReq.java new file mode 100644 index 0000000..a397529 --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamAddMemberReq.java @@ -0,0 +1,34 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.List; + +/** + * 批量添加团队成员请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamAddMemberReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; + + /** + * 用户ID列表 + */ + @JsonProperty("user_ids") + private List userIds; + + /** + * 角色:admin、member + */ + @JsonProperty("role") + private String role; +} diff --git a/src/main/java/com/realtime/model/query/TeamAdjustMemberRoleReq.java b/src/main/java/com/realtime/model/query/TeamAdjustMemberRoleReq.java new file mode 100644 index 0000000..ee3010e --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamAdjustMemberRoleReq.java @@ -0,0 +1,33 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 调整团队成员身份请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamAdjustMemberRoleReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; + + /** + * 用户ID + */ + @JsonProperty("user_id") + private String userId; + + /** + * 角色:admin、member + */ + @JsonProperty("role") + private String role; +} diff --git a/src/main/java/com/realtime/model/query/TeamCreateReq.java b/src/main/java/com/realtime/model/query/TeamCreateReq.java new file mode 100644 index 0000000..688e615 --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamCreateReq.java @@ -0,0 +1,46 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 创建团队请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamCreateReq extends BaseTeamReq { + + /** + * 团队名称 + */ + @JsonProperty("team_name") + private String teamName; + + /** + * 团队描述 + */ + @JsonProperty("team_description") + private String teamDescription; + + /** + * 团队类型 + */ + @JsonProperty("team_type") + private String teamType; + + /** + * 父团队ID(可选) + */ + @JsonProperty("parent_id") + private String parentId; + + /** + * 群ID + */ + @JsonProperty("group") + private String group; + +} diff --git a/src/main/java/com/realtime/model/query/TeamDeleteReq.java b/src/main/java/com/realtime/model/query/TeamDeleteReq.java new file mode 100644 index 0000000..47a5bfd --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamDeleteReq.java @@ -0,0 +1,21 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 删除团队请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamDeleteReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; +} diff --git a/src/main/java/com/realtime/model/query/TeamGetAllMemberReq.java b/src/main/java/com/realtime/model/query/TeamGetAllMemberReq.java new file mode 100644 index 0000000..e7068af --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamGetAllMemberReq.java @@ -0,0 +1,21 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 查看全部成员请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamGetAllMemberReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; +} diff --git a/src/main/java/com/realtime/model/query/TeamGetDepartmentReq.java b/src/main/java/com/realtime/model/query/TeamGetDepartmentReq.java new file mode 100644 index 0000000..018de8c --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamGetDepartmentReq.java @@ -0,0 +1,15 @@ +package com.realtime.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 获取部门结构请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamGetDepartmentReq extends BaseTeamReq { + // 空请求体 +} diff --git a/src/main/java/com/realtime/model/query/TeamListReq.java b/src/main/java/com/realtime/model/query/TeamListReq.java new file mode 100644 index 0000000..ca460e4 --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamListReq.java @@ -0,0 +1,21 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 团队列表查询请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamListReq extends BaseTeamReq { + + /** + * 团队名称(可选,用于搜索) + */ + @JsonProperty("team_name") + private String teamName; +} diff --git a/src/main/java/com/realtime/model/query/TeamRemoveMemberReq.java b/src/main/java/com/realtime/model/query/TeamRemoveMemberReq.java new file mode 100644 index 0000000..3593a3c --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamRemoveMemberReq.java @@ -0,0 +1,28 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.List; + +/** + * 批量删除团队成员请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamRemoveMemberReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; + + /** + * 用户ID列表 + */ + @JsonProperty("user_ids") + private List userIds; +} diff --git a/src/main/java/com/realtime/model/query/TeamUpdateReq.java b/src/main/java/com/realtime/model/query/TeamUpdateReq.java new file mode 100644 index 0000000..12f7eb5 --- /dev/null +++ b/src/main/java/com/realtime/model/query/TeamUpdateReq.java @@ -0,0 +1,45 @@ +package com.realtime.model.query; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 修改团队信息请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamUpdateReq extends BaseTeamReq { + + /** + * 团队ID + */ + @JsonProperty("team_id") + private String teamId; + + /** + * 团队名称 + */ + @JsonProperty("team_name") + private String teamName; + + /** + * 团队描述 + */ + @JsonProperty("team_description") + private String teamDescription; + + /** + * 团队类型 + */ + @JsonProperty("team_type") + private String teamType; + + /** + * 父团队ID(可选) + */ + @JsonProperty("parent_id") + private String parentId; +} diff --git a/src/main/java/com/realtime/packets/AutoSoftwarePacket.java b/src/main/java/com/realtime/packets/AutoSoftwarePacket.java index 723faf5..97bd353 100644 --- a/src/main/java/com/realtime/packets/AutoSoftwarePacket.java +++ b/src/main/java/com/realtime/packets/AutoSoftwarePacket.java @@ -11,11 +11,7 @@ import java.util.List; @EqualsAndHashCode(callSuper = true) @Data public class AutoSoftwarePacket extends BasePackets { - private String url; - private String body; - private String method; - private String commandLine; - + private String merMessage; @Override public Byte getCommand() { return Command.SOFT_CALL_MSG; diff --git a/src/main/java/com/realtime/packets/GroupPacket.java b/src/main/java/com/realtime/packets/GroupPacket.java index 3e45a15..f56a38c 100644 --- a/src/main/java/com/realtime/packets/GroupPacket.java +++ b/src/main/java/com/realtime/packets/GroupPacket.java @@ -1,6 +1,7 @@ package com.realtime.packets; +import com.realtime.model.query.TeamCreateReq; import com.realtime.packets.basePackets.BasePackets; import com.realtime.packets.command.Command; import lombok.Data; @@ -14,6 +15,7 @@ public class GroupPacket extends BasePackets { private List userIds; private Integer type = 2; private String groupName; + private TeamCreateReq teamCreateReq; @Override public Byte getCommand() { diff --git a/src/main/java/com/realtime/packets/server/handler/ExceptionHandler.java b/src/main/java/com/realtime/packets/server/handler/ExceptionHandler.java index 59f629d..af18489 100644 --- a/src/main/java/com/realtime/packets/server/handler/ExceptionHandler.java +++ b/src/main/java/com/realtime/packets/server/handler/ExceptionHandler.java @@ -1,5 +1,6 @@ package com.realtime.packets.server.handler; +import com.realtime.utils.SessionUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandler.Sharable; @@ -7,6 +8,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import lombok.extern.slf4j.Slf4j; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -22,12 +24,29 @@ public class ExceptionHandler extends ChannelDuplexHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // 处理连接重置异常(客户端断开连接) + if (cause instanceof IOException) { + log.warn("连接异常: {}, 通道: {}", cause.getMessage(), ctx.channel().id().asShortText()); + // 清理会话信息 + SessionUtils.unbind(ctx.channel()); + // 关闭连接 + ctx.channel().close(); + return; + } + + // 处理运行时异常 if (cause instanceof RuntimeException) { + log.error("运行时异常: ", cause); ByteBuf byteBuf = ctx.alloc().buffer(); map.put("errorCode",-10000); map.put("errorMessage",cause.getMessage()); byteBuf.writeBytes(map.toString().getBytes(StandardCharsets.UTF_8)); ctx.channel().writeAndFlush(new TextWebSocketFrame(byteBuf)); + } else { + // 其他异常 + log.error("未处理的异常: ", cause); + SessionUtils.unbind(ctx.channel()); + ctx.channel().close(); } } } diff --git a/src/main/java/com/realtime/packets/server/handler/GroupMessageHandler.java b/src/main/java/com/realtime/packets/server/handler/GroupMessageHandler.java index 8c51390..f4f9914 100644 --- a/src/main/java/com/realtime/packets/server/handler/GroupMessageHandler.java +++ b/src/main/java/com/realtime/packets/server/handler/GroupMessageHandler.java @@ -4,6 +4,7 @@ package com.realtime.packets.server.handler; import com.alibaba.fastjson2.JSONObject; +import com.realtime.exception.BusinessException; import com.realtime.model.pojo.GroupMessage; import com.realtime.packets.GroupSendPacket; import com.realtime.service.GroupMessageService; @@ -33,8 +34,11 @@ public class GroupMessageHandler extends SimpleChannelInboundHandler { private final GroupListService groupListService; + private final TeamService teamService; + @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, GroupPacket groupPacket) throws Exception { ChannelGroup channels = new DefaultChannelGroup(channelHandlerContext.executor()); @@ -52,7 +57,14 @@ public class JoinGroupHandler extends SimpleChannelInboundHandler { groupPacket.setGroupId(groupId); byte[] bytes = JSONObject.toJSONString(groupPacket).getBytes(StandardCharsets.UTF_8); byteBuf.writeBytes(bytes); - groupListService.saveGroupList(groupPacket, groupId); + TeamCreateReq teamCreateReq = new TeamCreateReq(); + teamCreateReq.setTeamName(groupPacket.getGroupName()+System.currentTimeMillis()+"_的团队"); + teamCreateReq.setTeamDescription("默认介绍"); + teamCreateReq.setTeamType("company"); + teamCreateReq.setParentId(""); + teamCreateReq.setGroup(groupId.toString()); + TeamOperationVo team = teamService.createTeam(teamCreateReq); + groupListService.saveGroupList(groupPacket, groupId,team); return byteBuf; } } diff --git a/src/main/java/com/realtime/packets/server/handler/MessageSocketHandler.java b/src/main/java/com/realtime/packets/server/handler/MessageSocketHandler.java index b6d5f23..9358b0e 100644 --- a/src/main/java/com/realtime/packets/server/handler/MessageSocketHandler.java +++ b/src/main/java/com/realtime/packets/server/handler/MessageSocketHandler.java @@ -6,6 +6,7 @@ import com.alibaba.fastjson2.JSONObject; import com.realtime.config.NettyConfig; import com.realtime.packets.basePackets.BasePackets; import com.realtime.packets.strategy.PacketService; +import com.realtime.utils.SessionUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -16,6 +17,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.IOException; import java.nio.charset.StandardCharsets; @Slf4j @@ -62,6 +64,10 @@ public class MessageSocketHandler extends SimpleChannelInboundHandler files, CloudFileUploadReq request, String userId); + + /** + * 通过URL批量上传云端文件 + */ + CloudFileOperationVo batchUploadByUrl(CloudFileUploadByUrlReq request, String userId); + + /** + * 下载文件 + */ + CloudFileDownloadVo downloadFile(CloudFileDownloadReq request); + + /** + * 查看文件列表 + */ + CloudFileListVo listFiles(CloudFileListReq request); + + /** + * 删除文件 + */ + CloudFileOperationVo deleteFile(CloudFileDeleteReq request); +} diff --git a/src/main/java/com/realtime/service/GroupListService.java b/src/main/java/com/realtime/service/GroupListService.java index 45083fe..5d70869 100644 --- a/src/main/java/com/realtime/service/GroupListService.java +++ b/src/main/java/com/realtime/service/GroupListService.java @@ -13,13 +13,14 @@ import com.realtime.packets.GroupPacket; import com.realtime.sysconst.Result; import com.realtime.vo.GroupDetailVo; import com.realtime.vo.GroupListVo; +import com.realtime.vo.TeamOperationVo; import java.io.IOException; import java.util.List; public interface GroupListService extends IService { - void saveGroupList(GroupPacket groupPacket, Long groupId); + void saveGroupList(GroupPacket groupPacket, Long groupId, TeamOperationVo teamOperationVo); Result> getGroup(GroupListQueryReq groupDetailQueryReq); diff --git a/src/main/java/com/realtime/service/TeamService.java b/src/main/java/com/realtime/service/TeamService.java new file mode 100644 index 0000000..68964a3 --- /dev/null +++ b/src/main/java/com/realtime/service/TeamService.java @@ -0,0 +1,73 @@ +package com.realtime.service; + +import com.realtime.model.query.*; +import com.realtime.vo.*; + +/** + * 团队管理服务接口 + */ +public interface TeamService { + + /** + * 获取用户可访问的团队列表 + * @param request 团队列表查询请求 + * @return 团队列表 + */ + TeamListVo getTeamList(TeamListReq request); + + /** + * 创建团队 + * @param request 创建团队请求 + * @return 创建结果 + */ + TeamOperationVo createTeam(TeamCreateReq request); + + /** + * 删除团队 + * @param request 删除团队请求 + * @return 删除结果 + */ + TeamOperationVo deleteTeam(TeamDeleteReq request); + + /** + * 修改团队信息 + * @param request 修改团队请求 + * @return 修改结果 + */ + TeamOperationVo updateTeam(TeamUpdateReq request); + + /** + * 查看全部成员 + * @param request 查看成员请求 + * @return 成员列表 + */ + TeamMemberListVo getAllMembers(TeamGetAllMemberReq request); + + /** + * 批量添加团队成员 + * @param request 添加成员请求 + * @return 添加结果 + */ + TeamOperationVo addMembers(TeamAddMemberReq request); + + /** + * 批量删除团队成员 + * @param request 删除成员请求 + * @return 删除结果 + */ + TeamOperationVo removeMembers(TeamRemoveMemberReq request); + + /** + * 调整团队成员身份 + * @param request 调整身份请求 + * @return 调整结果 + */ + TeamOperationVo adjustMemberRole(TeamAdjustMemberRoleReq request); + + /** + * 获取部门结构 + * @param request 获取部门结构请求 + * @return 部门结构 + */ + TeamDepartmentVo getDepartment(TeamGetDepartmentReq request); +} diff --git a/src/main/java/com/realtime/service/impl/CloudFileServiceImpl.java b/src/main/java/com/realtime/service/impl/CloudFileServiceImpl.java new file mode 100644 index 0000000..e213d64 --- /dev/null +++ b/src/main/java/com/realtime/service/impl/CloudFileServiceImpl.java @@ -0,0 +1,167 @@ +package com.realtime.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.realtime.config.CloudApiConfig; +import com.realtime.exception.BusinessException; +import com.realtime.model.query.*; +import com.realtime.service.CloudFileService; +import com.realtime.vo.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * 云端文件服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CloudFileServiceImpl implements CloudFileService { + + private final CloudApiConfig cloudApiConfig; + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + + @Override + public CloudFileOperationVo batchUpload(List files, CloudFileUploadReq request, String userId) { + if (files == null || files.isEmpty()) { + throw new BusinessException("文件列表不能为空"); + } + + try { + String url = cloudApiConfig.getBaseUrl() + "/file/upload"; + + // 构建multipart请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + MultiValueMap body = new LinkedMultiValueMap<>(); + + // 添加文件 + for (MultipartFile file : files) { + body.add("files", file.getResource()); + } + + // 添加其他参数 + body.add("path", request.getPath()); + body.add("type", request.getType()); + if (request.getTeamId() != null) { + body.add("team_id", request.getTeamId()); + } + + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, CloudFileOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("批量上传文件失败", e); + throw new BusinessException("批量上传文件失败: " + e.getMessage()); + } + } + + @Override + public CloudFileOperationVo batchUploadByUrl(CloudFileUploadByUrlReq request, String userId) { + if (request.getUrls() == null || request.getUrls().isEmpty()) { + throw new BusinessException("URL列表不能为空"); + } + + try { + String url = cloudApiConfig.getBaseUrl() + "/file/upload/url"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, CloudFileOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("批量URL上传文件失败", e); + throw new BusinessException("批量URL上传文件失败: " + e.getMessage()); + } + } + + @Override + public CloudFileDownloadVo downloadFile(CloudFileDownloadReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/file/download"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, CloudFileDownloadVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("生成下载链接失败", e); + throw new BusinessException("生成下载链接失败: " + e.getMessage()); + } + } + + @Override + public CloudFileListVo listFiles(CloudFileListReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/file/list"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, CloudFileListVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("查询文件列表失败", e); + throw new BusinessException("查询文件列表失败: " + e.getMessage()); + } + } + + @Override + public CloudFileOperationVo deleteFile(CloudFileDeleteReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/file/delete"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, CloudFileOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("删除文件失败", e); + throw new BusinessException("删除文件失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/realtime/service/impl/FileServiceImpl.java b/src/main/java/com/realtime/service/impl/FileServiceImpl.java index 46bfb80..a5ee7e9 100644 --- a/src/main/java/com/realtime/service/impl/FileServiceImpl.java +++ b/src/main/java/com/realtime/service/impl/FileServiceImpl.java @@ -25,6 +25,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -51,39 +52,38 @@ public class FileServiceImpl implements FileService { throw new BusinessException(ResultEnum.FILE_FORMAT_ERROR); } - UploadVo vo = generateFileName(file,fileSuffix); + UploadVo vo = generateFileName(file, fileSuffix); InputStream inputStream = file.getInputStream(); minioClient.putObject(PutObjectArgs.builder() - .bucket(minioConfig.getBucketName()) - .object(vo.getNewFileName()) - .stream(inputStream, file.getSize(), -1) - .contentType(file.getContentType()) - .build() - ); + .bucket(minioConfig.getBucketName()) + .object(vo.getNewFileName()) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build() + ); return Result.success(vo); } - private UploadVo generateFileName(MultipartFile file,String fileSuffix) { + private UploadVo generateFileName(MultipartFile file, String fileSuffix) { String originalFilename = file.getOriginalFilename(); assert originalFilename != null; UploadVo uploadVo = new UploadVo(); - String storeFileName = UUID.randomUUID() + "_" + originalFilename; + String storeFileName = originalFilename + Instant.now().getEpochSecond(); String url = generateFileUrl(storeFileName); uploadVo.setName(originalFilename); uploadVo.setUrl(url); uploadVo.setExtendName(fileSuffix); uploadVo.setNewFileName(storeFileName); - uploadVo.setFileSize(BigDecimal.valueOf(file.getSize()).divide(BigDecimal.valueOf(1048576)).setScale(3, RoundingMode.HALF_UP)); // MB + uploadVo.setFileSize(BigDecimal.valueOf(file.getSize()).divide(BigDecimal.valueOf(1048576), 3, RoundingMode.HALF_UP)); // MB uploadVo.setFileNameCode(Base64.encode(originalFilename)); return uploadVo; } - private String generateFileUrl(String fileName) { - return - minioConfig.getEndpoint()+"/real/" + fileName; + + String generateFileUrl(String fileName) { + return new StringBuilder().append(minioConfig.getEndpoint()).append("/").append(minioConfig.getBucketName()).append("/").append(fileName).toString(); } - } diff --git a/src/main/java/com/realtime/service/impl/GroupListServiceImpl.java b/src/main/java/com/realtime/service/impl/GroupListServiceImpl.java index 7f5d183..f3e885f 100644 --- a/src/main/java/com/realtime/service/impl/GroupListServiceImpl.java +++ b/src/main/java/com/realtime/service/impl/GroupListServiceImpl.java @@ -1,6 +1,8 @@ package com.realtime.service.impl; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.zxing.BarcodeFormat; @@ -12,17 +14,22 @@ import com.realtime.exception.BusinessException; import com.realtime.mappers.GroupListMapper; import com.realtime.model.pojo.GroupList; import com.realtime.model.pojo.GroupMember; +import com.realtime.model.query.CloudFileListReq; import com.realtime.model.query.GroupDetailQueryReq; import com.realtime.model.query.GroupListQueryReq; import com.realtime.model.remove.DisbandGroupReq; import com.realtime.model.update.GroupInventUpdateReq; import com.realtime.packets.GroupPacket; +import com.realtime.service.CloudFileService; import com.realtime.service.GroupListService; import com.realtime.service.GroupMemberService; +import com.realtime.service.TeamService; import com.realtime.sysconst.Result; import com.realtime.utils.SessionUtils; +import com.realtime.vo.CloudFileListVo; import com.realtime.vo.GroupDetailVo; import com.realtime.vo.GroupListVo; +import com.realtime.vo.TeamOperationVo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -33,10 +40,7 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.Objects; +import java.util.*; @Slf4j @Service @@ -46,13 +50,21 @@ public class GroupListServiceImpl extends ServiceImpl teamData = (Map) teamOperationVo.getTeam().get("team"); + + groupList.setTeamId(teamData.get("id").toString()); groupList.setName(groupPacket.getGroupName()); save(groupList); @@ -78,7 +90,14 @@ public class GroupListServiceImpl extends ServiceImpl requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamListVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("获取团队列表失败", e); + throw new BusinessException("获取团队列表失败: " + e.getMessage()); + } + } + + /** + * 创建团队 + * + * @param request 创建团队请求 + * @return 创建结果 + */ + @Override + public TeamOperationVo createTeam(TeamCreateReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/create"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("创建团队失败", e); + throw new BusinessException("创建团队失败: " + e.getMessage()); + } + } + + /** + * 删除团队 + * + * @param request 删除团队请求 + * @return 删除结果 + */ + @Override + public TeamOperationVo deleteTeam(TeamDeleteReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/delete"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("删除团队失败", e); + throw new BusinessException("删除团队失败: " + e.getMessage()); + } + } + + /** + * 修改团队信息 + * + * @param request 修改团队请求 + * @return 修改结果 + */ + @Override + public TeamOperationVo updateTeam(TeamUpdateReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/update"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("修改团队信息失败", e); + throw new BusinessException("修改团队信息失败: " + e.getMessage()); + } + } + + /** + * 查看全部成员 + * + * @param request 查看成员请求 + * @return 成员列表 + */ + @Override + public TeamMemberListVo getAllMembers(TeamGetAllMemberReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/get_all_member"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamMemberListVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("查看团队成员失败", e); + throw new BusinessException("查看团队成员失败: " + e.getMessage()); + } + } + + /** + * 批量添加团队成员 + * + * @param request 添加成员请求 + * @return 添加结果 + */ + @Override + public TeamOperationVo addMembers(TeamAddMemberReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/add_member"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("添加团队成员失败", e); + throw new BusinessException("添加团队成员失败: " + e.getMessage()); + } + } + + /** + * 批量删除团队成员 + * + * @param request 删除成员请求 + * @return 删除结果 + */ + @Override + public TeamOperationVo removeMembers(TeamRemoveMemberReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/remove_member"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("删除团队成员失败", e); + throw new BusinessException("删除团队成员失败: " + e.getMessage()); + } + } + + /** + * 调整团队成员身份 + * + * @param request 调整身份请求 + * @return 调整结果 + */ + @Override + public TeamOperationVo adjustMemberRole(TeamAdjustMemberRoleReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/adjust_member_role"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamOperationVo.class); + + return response.getBody(); + } catch (Exception e) { + + log.error("调整团队成员身份失败", e); + throw new BusinessException("调整团队成员身份失败: " + e.getMessage()); + } + } + + /** + * 获取部门结构 + * + * @param request 获取部门结构请求 + * @return 部门结构 + */ + @Override + public TeamDepartmentVo getDepartment(TeamGetDepartmentReq request) { + try { + String url = cloudApiConfig.getBaseUrl() + "/team/get_department"; + + // 构建请求 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity requestEntity = new HttpEntity<>(request, headers); + + // 调用外部API + ResponseEntity response = restTemplate.postForEntity( + url, requestEntity, TeamDepartmentVo.class); + + return response.getBody(); + } catch (Exception e) { + log.error("获取部门结构失败", e); + throw new BusinessException("获取部门结构失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/realtime/utils/SessionUtils.java b/src/main/java/com/realtime/utils/SessionUtils.java index a6782d7..b6710a4 100644 --- a/src/main/java/com/realtime/utils/SessionUtils.java +++ b/src/main/java/com/realtime/utils/SessionUtils.java @@ -3,11 +3,13 @@ package com.realtime.utils; import com.realtime.packets.basePackets.BasePackets; import io.netty.channel.Channel; import io.netty.channel.group.ChannelGroup; +import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@Slf4j public class SessionUtils { public static final Map userIdChannelMap = new ConcurrentHashMap<>(); @@ -27,7 +29,22 @@ public class SessionUtils { public static void unbind(Channel channel) { if (hasLogin(channel)) { - userIdChannelMap.remove(getUser(channel).getSender()); + BasePackets user = getUser(channel); + if (user != null && user.getSender() != null) { + log.info("清理用户会话: userId={}, channel={}", user.getSender(), channel.id().asShortText()); + // 从用户-通道映射中移除 + userIdChannelMap.remove(user.getSender()); + // 从会话用户映射中移除 + sessionUser.remove(user.getSender()); + // 从所有群组中移除该通道 + groupIdChannelGroupMap.values().forEach(channelGroup -> { + if (channelGroup != null && channelGroup.contains(channel)) { + channelGroup.remove(channel); + log.debug("从群组中移除通道: {}", channel.id().asShortText()); + } + }); + } + // 清除通道属性 channel.attr(Attributes.SESSION).set(null); } } diff --git a/src/main/java/com/realtime/vo/CloudFileDownloadVo.java b/src/main/java/com/realtime/vo/CloudFileDownloadVo.java new file mode 100644 index 0000000..3fa82ee --- /dev/null +++ b/src/main/java/com/realtime/vo/CloudFileDownloadVo.java @@ -0,0 +1,29 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 云端文件下载响应VO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CloudFileDownloadVo implements Serializable { + + private Boolean success; + + private DownloadData data; + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class DownloadData implements Serializable { + @JsonProperty("download_url") + private String downloadUrl; + } +} diff --git a/src/main/java/com/realtime/vo/CloudFileListVo.java b/src/main/java/com/realtime/vo/CloudFileListVo.java new file mode 100644 index 0000000..3da2ad8 --- /dev/null +++ b/src/main/java/com/realtime/vo/CloudFileListVo.java @@ -0,0 +1,24 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 云端文件列表响应VO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CloudFileListVo implements Serializable { + + private Boolean success; + + private List data; + + private Integer count; +} diff --git a/src/main/java/com/realtime/vo/CloudFileOperationVo.java b/src/main/java/com/realtime/vo/CloudFileOperationVo.java new file mode 100644 index 0000000..bb88362 --- /dev/null +++ b/src/main/java/com/realtime/vo/CloudFileOperationVo.java @@ -0,0 +1,22 @@ +package com.realtime.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 云端文件操作响应VO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CloudFileOperationVo implements Serializable { + + private Boolean success; + + private String message; + + private String error; +} diff --git a/src/main/java/com/realtime/vo/CloudFileVo.java b/src/main/java/com/realtime/vo/CloudFileVo.java new file mode 100644 index 0000000..ce0e0d2 --- /dev/null +++ b/src/main/java/com/realtime/vo/CloudFileVo.java @@ -0,0 +1,49 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * 云端文件VO + */ +@Data +public class CloudFileVo implements Serializable { + + private String id; + + private String name; + + private String path; + + @JsonProperty("full_path") + private String fullPath; + + private Long size; + + private String type; + + @JsonProperty("owner_id") + private String ownerId; + + @JsonProperty("owner_type") + private String ownerType; + + @JsonProperty("updated_id") + private String updatedId; + + @JsonProperty("created_at") + private LocalDateTime createdAt; + + @JsonProperty("updated_at") + private LocalDateTime updatedAt; + + /** + * 子文件/文件夹列表 + */ + private List children = new ArrayList<>(); +} diff --git a/src/main/java/com/realtime/vo/GroupDetailVo.java b/src/main/java/com/realtime/vo/GroupDetailVo.java index 2aa7040..866db30 100644 --- a/src/main/java/com/realtime/vo/GroupDetailVo.java +++ b/src/main/java/com/realtime/vo/GroupDetailVo.java @@ -2,6 +2,8 @@ package com.realtime.vo; import lombok.Data; +import java.util.Map; + @Data public class GroupDetailVo { private Long id; @@ -10,4 +12,7 @@ public class GroupDetailVo { private Integer isInvent; private String creator; private String qrCode; + private String teamId; + private CloudFileListVo teamFileData; + private String teamInfo; } diff --git a/src/main/java/com/realtime/vo/TeamDepartmentVo.java b/src/main/java/com/realtime/vo/TeamDepartmentVo.java new file mode 100644 index 0000000..3764527 --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamDepartmentVo.java @@ -0,0 +1,20 @@ +package com.realtime.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 部门结构响应VO + */ +@Data +public class TeamDepartmentVo implements Serializable { + + private Boolean success; + + /** + * 部门结构数据 + */ + private Map data; +} diff --git a/src/main/java/com/realtime/vo/TeamListVo.java b/src/main/java/com/realtime/vo/TeamListVo.java new file mode 100644 index 0000000..356983d --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamListVo.java @@ -0,0 +1,21 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 团队列表响应VO + */ +@Data +public class TeamListVo implements Serializable { + + private Boolean success; + + private List teams; + + @JsonProperty("total_count") + private Integer totalCount; +} diff --git a/src/main/java/com/realtime/vo/TeamMemberListVo.java b/src/main/java/com/realtime/vo/TeamMemberListVo.java new file mode 100644 index 0000000..17e0b32 --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamMemberListVo.java @@ -0,0 +1,21 @@ +package com.realtime.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 团队成员列表响应VO + */ +@Data +public class TeamMemberListVo implements Serializable { + + private Boolean success; + + /** + * 嵌套的团队成员数据 + */ + private Map team; +} diff --git a/src/main/java/com/realtime/vo/TeamMemberUserVo.java b/src/main/java/com/realtime/vo/TeamMemberUserVo.java new file mode 100644 index 0000000..85a8844 --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamMemberUserVo.java @@ -0,0 +1,34 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 团队成员用户信息VO + */ +@Data +public class TeamMemberUserVo implements Serializable { + + @JsonProperty("_id") + private String id; + + private String username; + + private String email; + + @JsonProperty("is_beta") + private Boolean isBeta; + + @JsonProperty("trial_end_date") + private String trialEndDate; + + @JsonProperty("trial_start_date") + private String trialStartDate; + + @JsonProperty("lastLogin") + private String lastLogin; + + private String role; +} diff --git a/src/main/java/com/realtime/vo/TeamMemberVo.java b/src/main/java/com/realtime/vo/TeamMemberVo.java new file mode 100644 index 0000000..bf0b2fe --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamMemberVo.java @@ -0,0 +1,38 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 团队成员VO + */ +@Data +public class TeamMemberVo implements Serializable { + + @JsonProperty("_id") + private String id; + + @JsonProperty("team_id") + private String teamId; + + @JsonProperty("user_id") + private String userId; + + /** + * 角色:admin、member + */ + private String role; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("updated_at") + private String updatedAt; + + /** + * 用户信息 + */ + private TeamMemberUserVo user; +} diff --git a/src/main/java/com/realtime/vo/TeamOperationVo.java b/src/main/java/com/realtime/vo/TeamOperationVo.java new file mode 100644 index 0000000..e0fd25c --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamOperationVo.java @@ -0,0 +1,22 @@ +package com.realtime.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 团队操作响应VO + */ +@Data +public class TeamOperationVo implements Serializable { + + private Boolean success; + + private String message; + + /** + * 嵌套的团队数据 + */ + private Map team; +} diff --git a/src/main/java/com/realtime/vo/TeamVo.java b/src/main/java/com/realtime/vo/TeamVo.java new file mode 100644 index 0000000..bc63047 --- /dev/null +++ b/src/main/java/com/realtime/vo/TeamVo.java @@ -0,0 +1,31 @@ +package com.realtime.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 团队VO + */ +@Data +public class TeamVo implements Serializable { + + @JsonProperty("_id") + private String id; + + private String name; + + private String description; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("updated_at") + private String updatedAt; + + /** + * 用户在该团队的角色:admin、member + */ + private String role; +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 17518e4..2f3b92e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,7 +6,7 @@ minio: endpoint: https://database.yuxindazhineng.com access-key: yuxinda_admin secret-key: yuxinda_admin01 - bucket-name: real + bucket-name: team-bucket file: picMaxSize: 104857600 # 100MB picMaxCount: 1 @@ -22,7 +22,7 @@ spring: url: jdbc:mysql://8.134.75.237:3309/real_time?serverTimezone=Asia/Shanghai username: root password: zhengfei_2024 - type: com.alibaba.druid.pool.DruidDataSource + type: com.zaxxer.hikari.HikariDataSource mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl diff --git a/src/main/resources/mappers/GroupListMapper.xml b/src/main/resources/mappers/GroupListMapper.xml index 05687a4..574b10c 100644 --- a/src/main/resources/mappers/GroupListMapper.xml +++ b/src/main/resources/mappers/GroupListMapper.xml @@ -9,7 +9,7 @@ id ,name,creator, created_time,picture,type, - is_invent + is_invent,team_id