diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java index 22b35be..68c76f5 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -58,7 +59,8 @@ public class ImFacePackItemManagerController { @Operation(summary = "批量删除表情") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:delete')") - public CommonResult deleteFacePackItemList(@RequestParam("ids") List ids) { + public CommonResult deleteFacePackItemList(@RequestParam("ids") + @Size(max = 100, message = "批量删除最多 100 条") List ids) { facePackItemService.deleteFacePackItemList(ids); return success(true); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java index 0179243..ad2f8ff 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -58,7 +59,8 @@ public class ImFacePackManagerController { @Operation(summary = "批量删除表情包") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('im:manager:face-pack:delete')") - public CommonResult deleteFacePackList(@RequestParam("ids") List ids) { + public CommonResult deleteFacePackList(@RequestParam("ids") + @Size(max = 100, message = "批量删除最多 100 条") List ids) { facePackService.deleteFacePackList(ids); return success(true); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java index a2b2a1c..eefbbed 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,6 +19,7 @@ public class ImFacePackItemPageReqVO extends PageParam { private String name; @Schema(description = "状态", example = "0") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类 } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java index 3231bb8..de6842b 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; @@ -47,6 +49,7 @@ public class ImFacePackItemSaveReqVO { @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用) } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java index e7320fe..e0220af 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -19,6 +21,7 @@ public class ImFacePackPageReqVO extends PageParam { private String name; @Schema(description = "状态", example = "0") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类 @Schema(description = "创建时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java index eaed5e9..86be43d 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -28,6 +30,7 @@ public class ImFacePackSaveReqVO { @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用) } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java index 629880c..d69248d 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java @@ -18,6 +18,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -67,7 +68,9 @@ public class ImSensitiveWordManagerController { @Operation(summary = "批量删除敏感词") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:delete')") - public CommonResult deleteSensitiveWordList(@RequestParam("ids") List ids) { + public CommonResult deleteSensitiveWordList( + @RequestParam("ids") + @Size(max = 100, message = "批量删除最多 100 条") List ids) { sensitiveWordService.deleteSensitiveWordList(ids); return success(true); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java index 302e002..4fc21f2 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -19,6 +21,7 @@ public class ImSensitiveWordPageReqVO extends PageParam { private String word; @Schema(description = "状态", example = "0") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用) @Schema(description = "创建时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java index d88d826..5d42140 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -20,6 +22,7 @@ public class ImSensitiveWordSaveReqVO { @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用) } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java index 78296ee..4bc719e 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -40,8 +41,9 @@ public class ImGroupMessageController { @Operation(summary = "拉取群聊消息(增量)") @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0") @Parameter(name = "size", description = "拉取数量", required = true, example = "100") - public CommonResult> pullGroupMessageList(@RequestParam("minId") Long minId, - @RequestParam("size") Integer size) { + public CommonResult> pullGroupMessageList( + @RequestParam("minId") Long minId, + @RequestParam("size") @Min(value = 1, message = "拉取数量最小值为 1") Integer size) { List messages = groupMessageService.pullGroupMessageList(getLoginUserId(), minId, size); return success(BeanUtils.toBean(messages, ImGroupMessageRespVO.class)); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java index 206d074..d375ec7 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -41,8 +42,9 @@ public class ImPrivateMessageController { @Operation(summary = "拉取私聊消息(增量)") @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0") @Parameter(name = "size", description = "拉取数量", required = true, example = "100") - public CommonResult> pullPrivateMessageList(@RequestParam("minId") Long minId, - @RequestParam("size") Integer size) { + public CommonResult> pullPrivateMessageList( + @RequestParam("minId") Long minId, + @RequestParam("size") @Min(value = 1, message = "拉取数量最小值为 1") Integer size) { List messages = privateMessageService.pullPrivateMessageList(getLoginUserId(), minId, size); return success(BeanUtils.toBean(messages, ImPrivateMessageRespVO.class)); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java index 548b610..622a319 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java @@ -20,7 +20,7 @@ public interface ImFacePackItemMapper extends BaseMapperX { default List selectListByPackIdsAndStatus(Collection packIds, Integer status) { return selectList(new LambdaQueryWrapperX() - .inIfPresent(ImFacePackItemDO::getPackId, packIds) + .in(ImFacePackItemDO::getPackId, packIds) .eq(ImFacePackItemDO::getStatus, status) .orderByAsc(ImFacePackItemDO::getPackId) .orderByAsc(ImFacePackItemDO::getSort) diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java index 1bbd1ce..a85fc27 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java @@ -73,7 +73,7 @@ public interface ImStatisticsManagerMapper { // ==================== 群 ==================== /** - * 群总数(im_group) + * 当前有效群总数 */ @Select("SELECT COUNT(*) FROM im_group WHERE deleted = 0 AND status = 0") Long selectTotalGroupCount(); diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java index afcebbe..9aea183 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java @@ -90,7 +90,7 @@ public class LiveKitClient { .setNotBefore(new Date(nowSec * 1000)) .setExpiresAt(new Date((nowSec + ttlSec) * 1000)) .setPayload("video", video) - .setSigner(JWTSignerUtil.hs256(cfg.getApiSecret().getBytes())); + .setSigner(JWTSignerUtil.hs256(cfg.getApiSecret().getBytes(StandardCharsets.UTF_8))); if (StrUtil.isNotEmpty(displayName)) { jwt.setPayload("name", displayName); } @@ -219,7 +219,7 @@ public class LiveKitClient { .setNotBefore(new Date(nowSec * 1000)) .setExpiresAt(new Date((nowSec + ADMIN_TOKEN_TTL.getSeconds()) * 1000)) .setPayload("video", MapUtil.of("roomAdmin", true)) - .setSigner(JWTSignerUtil.hs256(cfg.getApiSecret().getBytes())) + .setSigner(JWTSignerUtil.hs256(cfg.getApiSecret().getBytes(StandardCharsets.UTF_8))) .sign(); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java index e000c8e..b21d0fe 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java @@ -1,4 +1,4 @@ /** - * trade 模块的 web 配置 + * IM 模块的 web 配置 */ package cn.iocoder.yudao.module.im.framework.web; diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java index c304841..5d16b26 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.im.service.friend; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -28,6 +29,7 @@ import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.Collection; +import java.util.Collections; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -100,6 +102,9 @@ public class ImFriendServiceImpl implements ImFriendService { @Override public List getActiveFriendList(Long userId, Collection friendUserIds) { + if (CollUtil.isEmpty(friendUserIds)) { + return Collections.emptyList(); + } return friendMapper.selectListByUserIdAndFriendUserIdsAndStatus(userId, friendUserIds, CommonStatusEnum.ENABLE.getStatus()); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java index 469c6c0..dc8045b 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java @@ -669,6 +669,10 @@ public class ImGroupServiceImpl implements ImGroupService { * 三档分层禁言权限校验 */ private void validateMutePermission(ImGroupMemberDO operator, ImGroupMemberDO target) { + // 普通成员不能禁言任何人 + if (!ImGroupMemberRoleEnum.isOwnerOrAdmin(operator.getRole())) { + throw exception(GROUP_NOT_OWNER_OR_ADMIN); + } // 群主不可被禁言 if (ImGroupMemberRoleEnum.isOwner(target.getRole())) { throw exception(GROUP_MUTE_OWNER_DENIED); @@ -677,10 +681,6 @@ public class ImGroupServiceImpl implements ImGroupService { if (ImGroupMemberRoleEnum.isAdmin(target.getRole()) && !ImGroupMemberRoleEnum.isOwner(operator.getRole())) { throw exception(GROUP_MUTE_ADMIN_DENIED); } - // 普通成员不能禁言任何人 - if (!ImGroupMemberRoleEnum.isOwnerOrAdmin(operator.getRole())) { - throw exception(GROUP_NOT_OWNER_OR_ADMIN); - } } private ImGroupServiceImpl getSelf() { diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java index da4e3fb..5251641 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java @@ -71,7 +71,7 @@ public class ImStatisticsManagerServiceImpl implements ImStatisticsManagerServic List> rows = statisticsMapper.selectGroupSizeDistribution(); return convertMap(rows, row -> (String) row.get("range"), - row -> (Long) row.get("count")); + row -> Convert.toLong(row.get("count"))); } // ==================== 消息 ==================== @@ -102,16 +102,16 @@ public class ImStatisticsManagerServiceImpl implements ImStatisticsManagerServic public Map getMessageTypeCountMap(LocalDateTime beginTime, LocalDateTime endTime) { List> rows = statisticsMapper.selectMessageTypeDistribution(beginTime, endTime); return convertMap(rows, - row -> (Integer) row.get("type"), - row -> (Long) row.get("count")); + row -> Convert.toInt(row.get("type")), + row -> Convert.toLong(row.get("count"))); } @Override public Map getTopSenderCountMap(LocalDateTime beginTime, LocalDateTime endTime, int limit) { List> rows = statisticsMapper.selectTopSenders(beginTime, endTime, limit); return convertMap(rows, - row -> (Long) row.get("userId"), - row -> (Long) row.get("messageCount")); + row -> Convert.toLong(row.get("userId")), + row -> Convert.toLong(row.get("messageCount"))); } /** @@ -120,7 +120,7 @@ public class ImStatisticsManagerServiceImpl implements ImStatisticsManagerServic private static Map toDailyCountMap(List> rows) { return convertMap(rows, row -> Convert.convert(LocalDate.class, row.get("date")).atStartOfDay(), - row -> (Long) row.get("count")); + row -> Convert.toLong(row.get("count"))); } } diff --git a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java index a644705..df1edff 100644 --- a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java +++ b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java @@ -17,6 +17,7 @@ import org.mockito.Mock; import org.springframework.dao.DuplicateKeyException; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_NOT_FRIEND; @@ -213,4 +214,12 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { assertEquals(2, result.size()); } + @Test + public void testGetActiveFriendList_emptySkip() { + List result = friendService.getActiveFriendList(1L, Collections.emptyList()); + + assertTrue(result.isEmpty()); + verify(imFriendMapper, never()).selectListByUserIdAndFriendUserIdsAndStatus(anyLong(), anyCollection(), anyInt()); + } + } diff --git a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java index 33810da..55ee5eb 100644 --- a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java +++ b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java @@ -7,10 +7,12 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupAdminAddReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupAdminRemoveReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupCreateReqVO; +import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupMuteMemberReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupTransferOwnerReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO; +import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO; import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; @@ -932,6 +934,76 @@ public class ImGroupServiceImplTest extends BaseMockitoUnitTest { } } + // ========== banGroup ========== + + @Test + public void testBanGroup_dissolved() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupServiceImpl.class))) + .thenReturn(groupService); + + ImGroupDO group = ImGroupDO.builder().id(10L).status(CommonStatusEnum.DISABLE.getStatus()).build(); + when(groupMapper.selectById(10L)).thenReturn(group); + + ImGroupManagerBanReqVO reqVO = new ImGroupManagerBanReqVO(); + reqVO.setId(10L).setReason("违规"); + + ServiceException exception = assertThrows(ServiceException.class, + () -> groupService.banGroup(1L, reqVO)); + assertEquals(GROUP_DISSOLVED.getCode(), exception.getCode()); + verify(groupMapper, never()).updateById(any(ImGroupDO.class)); + verify(groupMessageService, never()).sendGroupMessage(anyLong(), any(ImGroupMessageSendDTO.class)); + } + } + + @Test + public void testBanGroup_alreadyBannedSkip() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupServiceImpl.class))) + .thenReturn(groupService); + + ImGroupDO group = ImGroupDO.builder().id(10L).banned(true) + .status(CommonStatusEnum.ENABLE.getStatus()).build(); + when(groupMapper.selectById(10L)).thenReturn(group); + + ImGroupManagerBanReqVO reqVO = new ImGroupManagerBanReqVO(); + reqVO.setId(10L).setReason("违规"); + + groupService.banGroup(1L, reqVO); + + verify(groupMapper, never()).updateById(any(ImGroupDO.class)); + verify(groupMessageService, never()).sendGroupMessage(anyLong(), any(ImGroupMessageSendDTO.class)); + } + } + + // ========== muteMember ========== + + @Test + public void testMuteMember_normalCannotMuteAdmin() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupServiceImpl.class))) + .thenReturn(groupService); + + ImGroupDO group = ImGroupDO.builder().id(10L).status(CommonStatusEnum.ENABLE.getStatus()).build(); + when(groupMapper.selectById(10L)).thenReturn(group); + when(groupMemberService.validateMemberInGroup(10L, 1L)).thenReturn( + ImGroupMemberDO.builder().groupId(10L).userId(1L) + .role(ImGroupMemberRoleEnum.NORMAL.getRole()).build()); + when(groupMemberService.validateMemberInGroup(10L, 2L)).thenReturn( + ImGroupMemberDO.builder().groupId(10L).userId(2L) + .role(ImGroupMemberRoleEnum.ADMIN.getRole()).build()); + + ImGroupMuteMemberReqVO reqVO = new ImGroupMuteMemberReqVO(); + reqVO.setGroupId(10L).setUserId(2L).setMutedSeconds(60); + + ServiceException exception = assertThrows(ServiceException.class, + () -> groupService.muteMember(1L, reqVO)); + assertEquals(GROUP_NOT_OWNER_OR_ADMIN.getCode(), exception.getCode()); + verify(groupMemberService, never()).updateGroupMemberMuteEndTime(anyLong(), anyLong(), any()); + verify(groupMessageService, never()).sendGroupMessage(anyLong(), any(ImGroupMessageSendDTO.class)); + } + } + // ========== validateGroupExists ========== @Test diff --git a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java index 6c3c9bf..f5b5786 100644 --- a/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java +++ b/yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; @@ -75,10 +76,10 @@ public class ImStatisticsManagerServiceImplTest extends BaseMockitoUnitTest { @Test public void testGetNewUserDailyCountMap_dateConvert() { - // 准备:date 字段以 SQL Date / String 混入,count 为 Long + // 准备:date 字段以 SQL Date / String 混入,count 混入多种 Number when(statisticsMapper.selectNewUserDailyCount(eq(BEGIN), eq(END))).thenReturn(Arrays.asList( - row("date", java.sql.Date.valueOf("2026-01-10"), "count", 5L), - row("date", "2026-01-11", "count", 8L))); + row("date", java.sql.Date.valueOf("2026-01-10"), "count", BigInteger.valueOf(5)), + row("date", "2026-01-11", "count", 8))); // 调用 Map result = service.getNewUserDailyCountMap(BEGIN, END); @@ -119,8 +120,8 @@ public class ImStatisticsManagerServiceImplTest extends BaseMockitoUnitTest { @Test public void testGetGroupSizeCountMap() { when(statisticsMapper.selectGroupSizeDistribution()).thenReturn(Arrays.asList( - row("range", "1-9 人", "count", 3L), - row("range", "10-49 人", "count", 2L))); + row("range", "1-9 人", "count", BigInteger.valueOf(3)), + row("range", "10-49 人", "count", 2))); Map result = service.getGroupSizeCountMap(); assertEquals(3L, result.get("1-9 人")); @@ -130,8 +131,8 @@ public class ImStatisticsManagerServiceImplTest extends BaseMockitoUnitTest { @Test public void testGetMessageTypeCountMap() { when(statisticsMapper.selectMessageTypeDistribution(BEGIN, END)).thenReturn(Arrays.asList( - row("type", 101, "count", 8L), - row("type", 102, "count", 1L))); + row("type", 101L, "count", BigInteger.valueOf(8)), + row("type", 102, "count", 1))); Map result = service.getMessageTypeCountMap(BEGIN, END); assertEquals(8L, result.get(101)); @@ -141,8 +142,8 @@ public class ImStatisticsManagerServiceImplTest extends BaseMockitoUnitTest { @Test public void testGetTopSenderCountMap_passesLimit() { when(statisticsMapper.selectTopSenders(BEGIN, END, 3)).thenReturn(Arrays.asList( - row("userId", 1L, "messageCount", 10L), - row("userId", 2L, "messageCount", 5L))); + row("userId", 1, "messageCount", BigInteger.valueOf(10)), + row("userId", 2L, "messageCount", 5))); Map result = service.getTopSenderCountMap(BEGIN, END, 3); assertEquals(10L, result.get(1L));