ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -62,7 +62,7 @@ /** * 获取参数配置列表 * 注册 */ @PostMapping("/register") public Result register(@Validated RegisterDto dto){ @@ -78,7 +78,7 @@ * 获取ip地址并判断是否在黑名单 */ @GetMapping("/blacklist") public Result blacklist(HttpServletRequest request,@PathVariable("account") String account){ public Result blacklist(HttpServletRequest request,@RequestParam("account") String account){ try { String clientIp = IpUtils.getClientIp(request); if(StringUtils.isEmpty(clientIp)){ ruoyi-admin/src/main/java/com/ruoyi/im/config/AddTeamMembersRequest.java
@@ -21,6 +21,8 @@ private String groupId;//群id private Integer number;//拉人数量 拉人为自动拉虚拟注册的用户;如果填写了云信账号数量无效(最大支持99人) /** * 邀请入群的成员账号ID列表,必填 */ ruoyi-admin/src/main/java/com/ruoyi/im/config/CreateTeamRequest.java
@@ -10,7 +10,6 @@ @Data public class CreateTeamRequest { @NotBlank(message = "群主id为空") private String groupId; ruoyi-admin/src/main/java/com/ruoyi/im/out/UserAccountOut.java
@@ -13,6 +13,8 @@ @Data public class UserAccountOut { private Integer id; // 账号(唯一) private String account; ruoyi-admin/src/main/java/com/ruoyi/im/service/NeteaseTeamService.java
@@ -18,5 +18,5 @@ AjaxResult assignment(AssignmentRequest request); AjaxResult dismiss(Long id); AjaxResult dismiss(Integer id); } ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/NeteaseTeamServiceImpl.java
@@ -4,7 +4,9 @@ import com.alibaba.druid.support.json.JSONUtils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.databind.ObjectMapper; 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; @@ -17,8 +19,10 @@ import com.ruoyi.im.config.CreateTeamRequest; import com.ruoyi.im.config.AssignmentRequest; import com.ruoyi.im.service.NeteaseTeamService; import com.ruoyi.system.domain.GroupWelcomeConfig; import com.ruoyi.system.domain.NeteaseTeam; import com.ruoyi.system.mapper.NeteaseTeamMapper; import com.ruoyi.system.service.GroupWelcomeConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,7 +43,8 @@ this.yunxinClient = yunxinClient; } @Autowired GroupWelcomeConfigService groupWelcomeConfigService; @Resource private NeteaseTeamMapper neteaseTeamMapper; @@ -114,7 +119,7 @@ String jsonBody = JSON.toJSONString(paramMap); NeteaseTeam neteaseTeam = neteaseTeamMapper.selectById(request.getGroupId()); if(ObjectUtil.isNotEmpty(neteaseTeam)){ if(ObjectUtil.isEmpty(neteaseTeam)){ return AjaxResult.error("群不存在!"); } @@ -258,18 +263,23 @@ try { NeteaseTeam neteaseTeam = neteaseTeamMapper.selectById(request.getGroupId()); if(ObjectUtil.isNotEmpty(neteaseTeam)){ if(ObjectUtil.isEmpty(neteaseTeam)){ return AjaxResult.error("群不存在!"); } GroupWelcomeConfig groupWelcomeConfig = groupWelcomeConfigService.getOne(new LambdaQueryWrapper<>(GroupWelcomeConfig.class) .eq(GroupWelcomeConfig::getConfigurationName, "IM-BASICS").last(" limit 1")); AddTeamMembersRequest build = AddTeamMembersRequest.builder() .inviteAccountIds(request.getInviteAccountIds()) .operatorId(request.getOperatorId()) .operatorId(groupWelcomeConfig.getUserAccid()) .msg(request.getMsg()) .teamId(Long.getLong(neteaseTeam.getTid())) .teamId(Long.valueOf(neteaseTeam.getTid())) .teamType(neteaseTeam.getTeamType()) .build(); String jsonBody = JSONUtils.toJSONString(build); // 方法1:使用 Jackson ObjectMapper ObjectMapper objectMapper = new ObjectMapper(); String jsonBody = objectMapper.writeValueAsString(build); YunxinApiResponse response = yunxinClient.executeV2Api( HttpMethod.POST, @@ -307,16 +317,18 @@ try { NeteaseTeam neteaseTeam = neteaseTeamMapper.selectById(request.getGroupId()); if(ObjectUtil.isNotEmpty(neteaseTeam)){ if(ObjectUtil.isEmpty(neteaseTeam)){ return AjaxResult.error("群不存在!"); } AssignmentRequest build = AssignmentRequest.builder() .teamId(Long.getLong(neteaseTeam.getTid())) .teamId(Long.valueOf(neteaseTeam.getTid())) .team_type(neteaseTeam.getTeamType()) .leave(2) .new_owner_account_id(request.getNew_owner_account_id()) .leave(request.getLeave()) .build(); String requestBody = JSONUtils.toJSONString(build); ObjectMapper objectMapper = new ObjectMapper(); String requestBody = objectMapper.writeValueAsString(build); String path = TeamV2UrlContext.TRANSFER_OWNER.replace("{team_id}",neteaseTeam.getTid().toString()); @@ -352,10 +364,10 @@ } @Override public AjaxResult dismiss(Long id) { public AjaxResult dismiss(Integer id) { try { NeteaseTeam neteaseTeam = neteaseTeamMapper.selectById(id); if(ObjectUtil.isNotEmpty(neteaseTeam)){ if(ObjectUtil.isEmpty(neteaseTeam)){ return AjaxResult.error("群不存在!"); } Map<String, String> queryParams = new HashMap<>(); @@ -380,6 +392,8 @@ log.error("网易云信解散群失败,响应:"); return AjaxResult.error("解散群失败"); } neteaseTeam.setStatus(1); neteaseTeamMapper.updateById(neteaseTeam); return AjaxResult.success(); } catch (YunxinSdkException e) { // 云信调用异常时回滚事务 ruoyi-admin/src/main/java/com/ruoyi/web/controller/group/ImGroupController.java
@@ -5,20 +5,34 @@ import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.im.comm.Result; import com.ruoyi.im.config.AddTeamMembersRequest; import com.ruoyi.im.config.CreateTeamRequest; import com.ruoyi.im.config.AssignmentRequest; import com.ruoyi.im.dto.UpdateUserBusinessDto; import com.ruoyi.im.service.NeteaseTeamService; import com.ruoyi.system.domain.NeteaseTeam; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.domain.vo.UserAccountVo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; 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.List; import java.util.Map; import java.util.Set; import java.util.UUID; @RestController @RequestMapping("/im/group") @@ -29,12 +43,18 @@ @Autowired NeteaseTeamService neteaseGroupService; @Value("${file.upload-dir}") private String uploadDir; @Value("${file.upload-prefix}") private String prefix; /** * 获取会员列表 * 获取群组列表 */ @PreAuthorize("@ss.hasPermi('im:group:list')") // @PreAuthorize("@ss.hasPermi('im:group:list')") @GetMapping("/list") public TableDataInfo list(@PathVariable("keyword") String keyword) public TableDataInfo list(@PathVariable(value = "keyword", required = false) String keyword) { // 创建查询条件包装器 LambdaQueryWrapper<NeteaseTeam> queryWrapper = new LambdaQueryWrapper<>(); @@ -52,6 +72,7 @@ // 默认按创建时间倒序 queryWrapper.orderByDesc(NeteaseTeam::getCreateTime); startPage(); List<NeteaseTeam> list = neteaseGroupService.list(queryWrapper); return getDataTable(list); } @@ -61,7 +82,7 @@ * 创建群组 */ @PostMapping("/create") public AjaxResult createGroup(@Validated @RequestBody CreateTeamRequest request) { public AjaxResult createGroup(CreateTeamRequest request) { return neteaseGroupService.createGroup(request); } @@ -69,7 +90,7 @@ * 更新群组 */ @PostMapping("/updateCreate") public AjaxResult updateCreate(@Validated @RequestBody CreateTeamRequest request) { public AjaxResult updateCreate(CreateTeamRequest request) { return neteaseGroupService.updateCreate(request); } @@ -77,7 +98,7 @@ * 拉人进去 */ @PostMapping("/inviteTeamMembers") public AjaxResult inviteTeamMembers(@Validated @RequestBody AddTeamMembersRequest request) { public AjaxResult inviteTeamMembers( AddTeamMembersRequest request) { return neteaseGroupService.inviteTeamMembers(request); } @@ -85,16 +106,89 @@ * 转让群主 */ @PostMapping("/assignment") public AjaxResult assignment(@Validated @RequestBody AssignmentRequest request) { public AjaxResult assignment(AssignmentRequest request) { return neteaseGroupService.assignment(request); } /** * 解散 */ @PostMapping("/dismiss") public AjaxResult dismiss(@PathVariable("id") Long id) { return neteaseGroupService.dismiss(id); @GetMapping("/dismiss") public AjaxResult dismiss(@RequestParam(value = "groupId") Integer groupId) { return neteaseGroupService.dismiss(groupId); } @PostMapping("/upload") public Result uploadFile(@RequestParam("file") MultipartFile file) { 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; return Result.success("文件上传成功",fileName); } 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); } } } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/BasicSetupController.java
@@ -2,10 +2,13 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.im.config.CreateTeamRequest; import com.ruoyi.system.domain.GroupWelcomeConfig; import com.ruoyi.system.domain.IpBlacklist; import com.ruoyi.system.domain.NeteaseTeam; import com.ruoyi.system.service.GroupWelcomeConfigService; import com.ruoyi.system.service.IpBlacklistService; import org.aspectj.weaver.loadtime.Aj; @@ -23,7 +26,7 @@ **/ @RestController @RequestMapping("/system/basic") public class BasicSetupController { public class BasicSetupController extends BaseController { @Autowired GroupWelcomeConfigService groupWelcomeConfigService; @@ -33,10 +36,10 @@ /** * 创建群组 * 修改基础信息 */ @PostMapping("/update") public AjaxResult createGroup(@Validated @RequestBody GroupWelcomeConfig vo) { public AjaxResult update(GroupWelcomeConfig vo) { GroupWelcomeConfig configServiceById = groupWelcomeConfigService.getById(vo.getId()); if(ObjectUtil.isEmpty(configServiceById)){ return AjaxResult.error("请联系管理员初始化配置"); @@ -53,10 +56,44 @@ } /** * 查询基础信息 */ @GetMapping("/get") public AjaxResult get() { GroupWelcomeConfig groupWelcomeConfig = groupWelcomeConfigService.getOne(new LambdaQueryWrapper<>(GroupWelcomeConfig.class) .eq(GroupWelcomeConfig::getConfigurationName, "IM-BASICS").last(" limit 1")); return AjaxResult.success(groupWelcomeConfig); } /** * ip黑名单列表 */ // @PreAuthorize("@ss.hasPermi('im:group:list')") @GetMapping("/list") public TableDataInfo list(@RequestParam(value = "keyword", required = false) String keyword) { // 创建查询条件包装器 LambdaQueryWrapper<IpBlacklist> queryWrapper = new LambdaQueryWrapper<>(); // 只有当 keyword 不为空时才添加 OR 条件 if (ObjectUtil.isNotEmpty(keyword)) { queryWrapper.and(wrapper -> wrapper.eq(IpBlacklist::getIpAddress,keyword) ); } // 默认按创建时间倒序 queryWrapper.orderByDesc(IpBlacklist::getCreateTime); startPage(); List<IpBlacklist> list = ipBlacklistService.list(queryWrapper); return getDataTable(list); } /** * 新增ip黑名单 */ @PostMapping("/addIp") public AjaxResult addIp(@PathVariable("ip") String ip) { public AjaxResult addIp(@RequestParam("ip") String ip) { long count = ipBlacklistService.count(new LambdaQueryWrapper<>(IpBlacklist.class).eq(IpBlacklist::getIpAddress, ip)); if(count > 0){ @@ -70,17 +107,17 @@ } /** * 新增ip黑名单 * 删除ip黑名单 */ @PostMapping("/deleteIp") public AjaxResult addIp(@PathVariable("id") Integer id) { public AjaxResult addIp(@RequestParam("id") Integer id) { IpBlacklist ipBlacklist = ipBlacklistService.getById(id); if(ObjectUtil.isEmpty(ipBlacklist)){ return AjaxResult.error("ip地址不存在!"); } ipBlacklistService.removeById(id); return AjaxResult.success("添加成功"); return AjaxResult.success("删除成功"); } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java
@@ -43,7 +43,7 @@ /** * 获取会员列表 */ @PreAuthorize("@ss.hasPermi('im:user:list')") // @PreAuthorize("@ss.hasPermi('im:user:list')") @GetMapping("/list") public TableDataInfo list(UserAccountVo vo) { @@ -73,6 +73,7 @@ // 默认按创建时间倒序 queryWrapper.orderByDesc(UserAccount::getCreateTime); startPage(); List<UserAccount> list = userAccountService.list(queryWrapper); List<UserAccountOut> toList = ConverterUtil.convertToList(list, UserAccountOut.class); return getDataTable(toList); ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -118,6 +118,7 @@ .antMatchers("/im/api/**").permitAll() .antMatchers("/im/user/**").permitAll() .antMatchers("/im/group/**").permitAll() .antMatchers("/system/basic/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated(); })