diff --git a/db/im-platform.sql b/db/im-platform.sql index a32c6a2..575610f 100644 --- a/db/im-platform.sql +++ b/db/im-platform.sql @@ -22,6 +22,7 @@ create table `im_friend`( `friend_id` bigint not null comment '好友id', `friend_nick_name` varchar(255) not null comment '好友昵称', `friend_head_image` varchar(255) default '' comment '好友头像', + `is_dnd` tinyint comment '免打扰标识(Do Not Disturb) 0:关闭 1:开启', `deleted` tinyint comment '删除标识 0:正常 1:已删除', `created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间', key `idx_user_id` (`user_id`), @@ -62,6 +63,7 @@ create table `im_group_member`( `remark_nick_name` varchar(255) DEFAULT '' comment '显示昵称备注', `head_image` varchar(255) DEFAULT '' comment '用户头像', `remark_group_name` varchar(255) DEFAULT '' comment '显示群名备注', + `is_dnd` tinyint comment '免打扰标识(Do Not Disturb) 0:关闭 1:开启', `quit` tinyint(1) DEFAULT 0 comment '是否已退出', `quit_time` datetime DEFAULT NULL comment '退出时间', `created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间', diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java index fae3c61..3b54fa0 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java @@ -1,12 +1,14 @@ package com.bx.implatform.controller; import com.bx.implatform.annotation.RepeatSubmit; +import com.bx.implatform.dto.FriendDndDTO; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.FriendService; import com.bx.implatform.vo.FriendVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -50,5 +52,12 @@ public class FriendController { return ResultUtils.success(); } + @PutMapping("/dnd") + @Operation(summary = "开启/关闭免打扰状态", description = "开启/关闭免打扰状态") + public Result setFriendDnd(@Valid @RequestBody FriendDndDTO dto) { + friendService.setDnd(dto); + return ResultUtils.success(); + } + } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java index a95b692..af25a5a 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java @@ -1,6 +1,7 @@ package com.bx.implatform.controller; import com.bx.implatform.annotation.RepeatSubmit; +import com.bx.implatform.dto.GroupDndDTO; import com.bx.implatform.dto.GroupInviteDTO; import com.bx.implatform.dto.GroupMemberRemoveDTO; import com.bx.implatform.result.Result; @@ -101,5 +102,12 @@ public class GroupController { return ResultUtils.success(); } + @Operation(summary = "开启/关闭免打扰", description = "开启/关闭免打扰") + @PutMapping("/dnd") + public Result setGroupDnd(@Valid @RequestBody GroupDndDTO dto) { + groupService.setDnd(dto); + return ResultUtils.success(); + } + } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/FriendDndDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/FriendDndDTO.java new file mode 100644 index 0000000..12d1721 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/FriendDndDTO.java @@ -0,0 +1,23 @@ +package com.bx.implatform.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * @author Blue + * @version 1.0 + */ +@Data +@Schema(description = "好友免打扰") +public class FriendDndDTO { + + @NotNull(message = "好友id不可为空") + @Schema(description = "好友用户id") + private Long friendId; + + @NotNull(message = "消息免打扰状态不可为空") + @Schema(description = "消息免打扰状态") + private Boolean isDnd; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupDndDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupDndDTO.java new file mode 100644 index 0000000..1358b82 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupDndDTO.java @@ -0,0 +1,23 @@ +package com.bx.implatform.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * @author Blue + * @version 1.0 + * @date 2025-02-23 + */ +@Data +@Schema(description = "群聊免打扰") +public class GroupDndDTO { + + @NotNull(message = "群id不可为空") + @Schema(description = "群组id") + private Long groupId; + + @NotNull(message = "免打扰状态不可为空") + @Schema(description = "免打扰状态") + private Boolean isDnd; +} diff --git a/im-platform/src/main/java/com/bx/implatform/entity/Friend.java b/im-platform/src/main/java/com/bx/implatform/entity/Friend.java index c7451f9..e3f96f7 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/Friend.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/Friend.java @@ -45,6 +45,11 @@ public class Friend{ */ private String friendHeadImage; + /** + * 是否开启免打扰 + */ + private Boolean isDnd; + /** * 是否已删除 */ diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java index 415eb83..8aec5cc 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java @@ -58,6 +58,12 @@ public class GroupMember extends Model { */ private String remarkGroupName; + /** + * 是否免打扰 + */ + private Boolean isDnd; + + /** * 是否已退出 */ diff --git a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java index 6d12868..b044a44 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java @@ -37,8 +37,10 @@ public enum MessageType { GROUP_UNBAN(52,"群聊解封"), FRIEND_NEW(80, "新增好友"), FRIEND_DEL(81, "删除好友"), + FRIEND_DND(82, "好友免打扰"), GROUP_NEW(90, "新增群聊"), GROUP_DEL(91, "删除群聊"), + GROUP_DND(92, "群聊免打扰"), RTC_CALL_VOICE(100, "语音呼叫"), RTC_CALL_VIDEO(101, "视频呼叫"), RTC_ACCEPT(102, "接受"), diff --git a/im-platform/src/main/java/com/bx/implatform/service/FriendService.java b/im-platform/src/main/java/com/bx/implatform/service/FriendService.java index 70088a4..282a090 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/FriendService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/FriendService.java @@ -1,6 +1,7 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.bx.implatform.dto.FriendDndDTO; import com.bx.implatform.entity.Friend; import com.bx.implatform.vo.FriendVO; @@ -70,4 +71,10 @@ public interface FriendService extends IService { */ void bindFriend(Long userId, Long friendId); + /** + * 设置好友免打扰状态 + * @param dto + */ + void setDnd(FriendDndDTO dto); + } \ No newline at end of file diff --git a/im-platform/src/main/java/com/bx/implatform/service/GroupMemberService.java b/im-platform/src/main/java/com/bx/implatform/service/GroupMemberService.java index dd0ae42..770943b 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/GroupMemberService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/GroupMemberService.java @@ -1,6 +1,7 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.bx.implatform.dto.GroupDndDTO; import com.bx.implatform.entity.GroupMember; import java.util.List; @@ -90,4 +91,12 @@ public interface GroupMemberService extends IService { * @param userIds 用户id */ Boolean isInGroup(Long groupId,List userIds); + + /** + * 设置免打扰状态 + * @param groupId 群id + * @param userId 用户id + * @param isDnd 是否开启免打扰 + */ + void setDnd(Long groupId, Long userId, Boolean isDnd); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/GroupService.java b/im-platform/src/main/java/com/bx/implatform/service/GroupService.java index e471b7d..a43320a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/GroupService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/GroupService.java @@ -1,6 +1,7 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.bx.implatform.dto.GroupDndDTO; import com.bx.implatform.dto.GroupInviteDTO; import com.bx.implatform.dto.GroupMemberRemoveDTO; import com.bx.implatform.entity.Group; @@ -92,4 +93,10 @@ public interface GroupService extends IService { * @return List **/ List findGroupMembers(Long groupId); + + /** + * 开启/关闭免打扰 + * @param dto + */ + void setDnd(GroupDndDTO dto); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java index a302596..bbe6204 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java @@ -11,6 +11,7 @@ import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.model.IMPrivateMessage; import com.bx.imcommon.model.IMUserInfo; import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.dto.FriendDndDTO; import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.entity.User; @@ -138,6 +139,18 @@ public class FriendServiceImpl extends ServiceImpl impleme sendAddFriendMessage(userId, friendId, friend); } + @Override + public void setDnd(FriendDndDTO dto) { + UserSession session = SessionContext.getSession(); + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); + wrapper.eq(Friend::getUserId, session.getUserId()); + wrapper.eq(Friend::getFriendId, dto.getFriendId()); + wrapper.set(Friend::getIsDnd, dto.getIsDnd()); + this.update(wrapper); + // 推送同步消息 + sendSyncDndMessage(dto.getFriendId(), dto.getIsDnd()); + } + /** * 单向解除好友关系 * @@ -175,6 +188,7 @@ public class FriendServiceImpl extends ServiceImpl impleme vo.setHeadImage(f.getFriendHeadImage()); vo.setNickName(f.getFriendNickName()); vo.setDeleted(f.getDeleted()); + vo.setIsDnd(f.getIsDnd()); return vo; } @@ -254,4 +268,21 @@ public class FriendServiceImpl extends ServiceImpl impleme sendMessage.setData(messageInfo); imClient.sendPrivateMessage(sendMessage); } + + void sendSyncDndMessage(Long friendId, Boolean isDnd) { + // 同步免打扰状态到其他终端 + UserSession session = SessionContext.getSession(); + PrivateMessageVO msgInfo = new PrivateMessageVO(); + msgInfo.setSendId(session.getUserId()); + msgInfo.setRecvId(friendId); + msgInfo.setSendTime(new Date()); + msgInfo.setType(MessageType.FRIEND_DND.code()); + msgInfo.setContent(isDnd.toString()); + IMPrivateMessage sendMessage = new IMPrivateMessage<>(); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); + sendMessage.setData(msgInfo); + sendMessage.setSendToSelf(true); + imClient.sendPrivateMessage(sendMessage); + } + } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java index 6a8e23f..9302c61 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java @@ -120,4 +120,12 @@ public class GroupMemberServiceImpl extends ServiceImpl wrapper = Wrappers.lambdaUpdate(); + wrapper.eq(GroupMember::getGroupId, groupId); + wrapper.eq(GroupMember::getUserId, userId); + wrapper.set(GroupMember::getIsDnd, isDnd); + this.update(wrapper); + } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index 6564679..f5ab43d 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -12,6 +12,7 @@ import com.bx.imcommon.model.IMUserInfo; import com.bx.imcommon.util.CommaTextUtils; import com.bx.implatform.contant.Constant; import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.dto.GroupDndDTO; import com.bx.implatform.dto.GroupInviteDTO; import com.bx.implatform.dto.GroupMemberRemoveDTO; import com.bx.implatform.entity.*; @@ -314,6 +315,15 @@ public class GroupServiceImpl extends ServiceImpl implements }).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList()); } + @Override + public void setDnd(GroupDndDTO dto) { + UserSession session = SessionContext.getSession(); + groupMemberService.setDnd(dto.getGroupId(), session.getUserId(), dto.getIsDnd()); + // 推送同步消息 + sendSyncDndMessage(dto.getGroupId(), dto.getIsDnd()); + } + + private void sendTipMessage(Long groupId, List recvIds, String content, Boolean sendToAll) { UserSession session = SessionContext.getSession(); // 消息入库 @@ -351,6 +361,7 @@ public class GroupServiceImpl extends ServiceImpl implements vo.setShowNickName(member.getShowNickName()); vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName())); vo.setQuit(member.getQuit()); + vo.setIsDnd(member.getIsDnd()); return vo; } @@ -386,4 +397,20 @@ public class GroupServiceImpl extends ServiceImpl implements sendMessage.setSendToSelf(false); imClient.sendGroupMessage(sendMessage); } + + private void sendSyncDndMessage(Long groupId, Boolean isDnd) { + UserSession session = SessionContext.getSession(); + GroupMessageVO msgInfo = new GroupMessageVO(); + msgInfo.setType(MessageType.GROUP_DND.code()); + msgInfo.setSendTime(new Date()); + msgInfo.setGroupId(groupId); + msgInfo.setSendId(session.getUserId()); + msgInfo.setContent(isDnd.toString()); + IMGroupMessage sendMessage = new IMGroupMessage<>(); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); + sendMessage.setData(msgInfo); + sendMessage.setSendResult(false); + imClient.sendGroupMessage(sendMessage); + } + } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java b/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java index e36e6e3..cf65b05 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java @@ -20,6 +20,10 @@ public class FriendVO { @Schema(description = "好友头像") private String headImage; + @Schema(description = "是否开启免打扰") + private Boolean isDnd; + + @Schema(description = "是否已删除") private Boolean deleted; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java index 75abfa8..1673ee2 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java @@ -56,5 +56,8 @@ public class GroupVO { @Schema(description = "被封禁原因") private String reason; + @Schema(description = "是否开启免打扰") + private Boolean isDnd; + } diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index 9910879..e9264ca 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -146,6 +146,12 @@ export default { this.friendStore.removeFriend(friendId); return; } + // 对好友设置免打扰 + if (msg.type == enums.MESSAGE_TYPE.FRIEND_DND) { + this.friendStore.setDnd(friendId, JSON.parse(msg.content)); + this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); + return; + } // 消息插入 let friend = this.loadFriendInfo(friendId); this.insertPrivateMessage(friend, msg); @@ -183,14 +189,20 @@ export default { type: 'PRIVATE', targetId: friend.id, showName: friend.nickName, - headImage: friend.headImage + headImage: friend.headImage, + isDnd: friend.isDnd }; // 打开会话 this.chatStore.openChat(chatInfo); // 插入消息 this.chatStore.insertMessage(msg, chatInfo); // 播放提示音 - this.playAudioTip(); + this.chatStore.insertMessage(msg, chatInfo); + if (!friend.isDnd && !this.chatStore.isLoading() && + !msg.selfSend && msgType.isNormal(msg.type) && + msg.status != enums.MESSAGE_STATUS.READED) { + this.playAudioTip(); + } } @@ -240,6 +252,12 @@ export default { this.groupStore.removeGroup(msg.groupId); return; } + // 对群设置免打扰 + if (msg.type == enums.MESSAGE_TYPE.GROUP_DND) { + this.groupStore.setDnd(msg.groupId, JSON.parse(msg.content)); + this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); + return; + } // 插入消息 let group = this.loadGroupInfo(msg.groupId); this.insertGroupMessage(group, msg); @@ -291,14 +309,19 @@ export default { type: 'GROUP', targetId: group.id, showName: group.showGroupName, - headImage: group.headImageThumb + headImage: group.headImageThumb, + isDnd: group.isDnd }; // 打开会话 this.chatStore.openChat(chatInfo); // 插入消息 this.chatStore.insertMessage(msg, chatInfo); // 播放提示音 - this.playAudioTip(); + if (!group.isDnd && !this.chatStore.isLoading() && + !msg.selfSend && msgType.isNormal(msg.type) && + msg.status != enums.MESSAGE_STATUS.READED) { + this.playAudioTip(); + } } }, diff --git a/im-uniapp/common/enums.js b/im-uniapp/common/enums.js index fbdb7a3..3ddd09c 100644 --- a/im-uniapp/common/enums.js +++ b/im-uniapp/common/enums.js @@ -16,8 +16,10 @@ const MESSAGE_TYPE = { USER_BANNED: 50, FRIEND_NEW: 80, FRIEND_DEL: 81, + FRIEND_DND: 82, GROUP_NEW: 90, - GROUP_DEL: 91, + GROUP_DEL: 91, + GROUP_DND: 92, RTC_CALL_VOICE: 100, RTC_CALL_VIDEO: 101, RTC_ACCEPT: 102, diff --git a/im-uniapp/components/chat-item/chat-item.vue b/im-uniapp/components/chat-item/chat-item.vue index d0ed604..d7c8f7f 100644 --- a/im-uniapp/components/chat-item/chat-item.vue +++ b/im-uniapp/components/chat-item/chat-item.vue @@ -15,8 +15,10 @@ {{ atText }} {{ chat.sendNickName + ': ' }} - + + @@ -43,7 +45,7 @@ export default { methods: { showChatBox() { // 初始化期间进入会话会导致消息不刷新 - if(!getApp().$vm.isInit || this.chatStore.isLoading()){ + if (!getApp().$vm.isInit || this.chatStore.isLoading()) { uni.showToast({ title: "正在初始化页面,请稍后...", icon: 'none' @@ -153,8 +155,8 @@ export default { font-size: $im-font-size-smaller; color: $im-text-color-lighter; padding-top: 8rpx; - align-items: center; - + align-items: center; + .chat-at-text { color: $im-color-danger; } diff --git a/im-uniapp/components/head-image/head-image.vue b/im-uniapp/components/head-image/head-image.vue index 4bc68bf..3e4210a 100644 --- a/im-uniapp/components/head-image/head-image.vue +++ b/im-uniapp/components/head-image/head-image.vue @@ -1,5 +1,5 @@