Browse Source

群优化(实时更新群信息)

master
xsx 1 year ago
parent
commit
b2e26ca7a8
  1. 6
      im-platform/src/main/java/com/bx/implatform/controller/GroupController.java
  2. 4
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  3. 55
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java
  4. 96
      im-uniapp/App.vue
  5. 2
      im-uniapp/common/enums.js
  6. 1
      im-uniapp/store/chatStore.js
  7. 8
      im-uniapp/store/friendStore.js
  8. 24
      im-uniapp/store/groupStore.js
  9. 2
      im-web/src/api/enums.js
  10. 1
      im-web/src/store/chatStore.js
  11. 23
      im-web/src/store/friendStore.js
  12. 24
      im-web/src/store/groupStore.js
  13. 4
      im-web/src/view/Friend.vue
  14. 51
      im-web/src/view/Home.vue

6
im-platform/src/main/java/com/bx/implatform/controller/GroupController.java

@ -31,12 +31,14 @@ public class GroupController {
return ResultUtils.success(groupService.createGroup(vo));
}
@RepeatSubmit
@Operation(summary = "修改群聊信息", description = "修改群聊信息")
@PutMapping("/modify")
public Result<GroupVO> modifyGroup(@Valid @RequestBody GroupVO vo) {
return ResultUtils.success(groupService.modifyGroup(vo));
}
@RepeatSubmit
@Operation(summary = "解散群聊", description = "解散群聊")
@DeleteMapping("/delete/{groupId}")
public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
@ -44,6 +46,7 @@ public class GroupController {
return ResultUtils.success();
}
@Operation(summary = "查询群聊", description = "查询单个群聊信息")
@GetMapping("/find/{groupId}")
public Result<GroupVO> findGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
@ -56,6 +59,7 @@ public class GroupController {
return ResultUtils.success(groupService.findGroups());
}
@RepeatSubmit
@Operation(summary = "邀请进群", description = "邀请好友进群")
@PostMapping("/invite")
public Result invite(@Valid @RequestBody GroupInviteVO vo) {
@ -70,6 +74,7 @@ public class GroupController {
return ResultUtils.success(groupService.findGroupMembers(groupId));
}
@RepeatSubmit
@Operation(summary = "退出群聊", description = "退出群聊")
@DeleteMapping("/quit/{groupId}")
public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
@ -77,6 +82,7 @@ public class GroupController {
return ResultUtils.success();
}
@RepeatSubmit
@Operation(summary = "踢出群聊", description = "将用户踢出群聊")
@DeleteMapping("/kick/{groupId}")
public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId,

4
im-platform/src/main/java/com/bx/implatform/enums/MessageType.java

@ -10,6 +10,8 @@ import lombok.AllArgsConstructor;
* 30-39: UI交互类消息: 显示加载状态等
* 40-49: 操作交互类消息: 语音通话视频通话消息等
* 50-60: 后台操作类消息: 用户封禁群组封禁等
* 80-89: 好友变化消息
* 90-99: 群聊变化消息
* 100-199: 单人语音通话rtc信令
* 200-299: 多人语音通话rtc信令
*
@ -35,6 +37,8 @@ public enum MessageType {
GROUP_UNBAN(52,"群聊解封"),
FRIEND_NEW(80, "新增好友"),
FRIEND_DEL(81, "删除好友"),
GROUP_NEW(90, "新增群聊"),
GROUP_DEL(91, "删除群聊"),
RTC_CALL_VOICE(100, "语音呼叫"),
RTC_CALL_VIDEO(101, "视频呼叫"),
RTC_ACCEPT(102, "接受"),

55
im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java

@ -2,6 +2,7 @@ package com.bx.implatform.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -30,6 +31,7 @@ import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.vo.GroupVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
@ -69,9 +71,12 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
member.setRemarkNickName(vo.getRemarkNickName());
member.setRemarkGroupName(vo.getRemarkGroupName());
groupMemberService.save(member);
GroupVO groupVo = findById(group.getId());
// 推送同步消息给自己的其他终端
sendAddGroupMessage(groupVo, Lists.newArrayList(), true);
// 返回
log.info("创建群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
return findById(group.getId());
return groupVo;
}
@CacheEvict(key = "#vo.getId()")
@ -95,7 +100,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
this.updateById(group);
}
log.info("修改群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
return convert(group,member);
return convert(group, member);
}
@Transactional(rollbackFor = Exception.class)
@ -119,6 +124,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
redisTemplate.delete(key);
// 推送解散群聊提示
this.sendTipMessage(groupId, userIds, String.format("'%s'解散了群聊", session.getNickName()));
// 推送同步消息
this.sendDelGroupMessage(groupId, userIds, false);
log.info("删除群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
}
@ -136,6 +143,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
redisTemplate.opsForHash().delete(key, userId.toString());
// 推送退出群聊提示
this.sendTipMessage(groupId, List.of(userId), "您已退出群聊");
// 推送同步消息
this.sendDelGroupMessage(groupId, Lists.newArrayList(), true);
log.info("退出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
}
@ -156,6 +165,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
redisTemplate.opsForHash().delete(key, userId.toString());
// 推送踢出群聊提示
this.sendTipMessage(groupId, List.of(userId), "您已被移出群聊");
// 推送同步消息
this.sendDelGroupMessage(groupId, List.of(userId), false);
log.info("踢出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
}
@ -170,7 +181,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
if (Objects.isNull(member)) {
throw new GlobalException("您未加入群聊");
}
return convert(group,member);
return convert(group, member);
}
@Cacheable(key = "#groupId")
@ -247,6 +258,11 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
if (!groupMembers.isEmpty()) {
groupMemberService.saveOrUpdateBatch(group.getId(), groupMembers);
}
// 推送同步消息给被邀请人
for (GroupMember m : groupMembers) {
GroupVO groupVo = convert(group, m);
sendAddGroupMessage(groupVo, List.of(m.getUserId()), false);
}
// 推送进入群聊消息
List<Long> userIds = groupMemberService.findUserIdsByGroupId(vo.getGroupId());
String memberNames = groupMembers.stream().map(GroupMember::getShowNickName).collect(Collectors.joining(","));
@ -310,4 +326,37 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
vo.setQuit(member.getQuit());
return vo;
}
private void sendAddGroupMessage(GroupVO group, List<Long> recvIds, Boolean sendToSelf) {
UserSession session = SessionContext.getSession();
GroupMessageVO msgInfo = new GroupMessageVO();
msgInfo.setContent(JSON.toJSONString(group));
msgInfo.setType(MessageType.GROUP_NEW.code());
msgInfo.setSendTime(new Date());
msgInfo.setGroupId(group.getId());
msgInfo.setSendId(session.getUserId());
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvIds(recvIds);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
sendMessage.setSendToSelf(sendToSelf);
imClient.sendGroupMessage(sendMessage);
}
private void sendDelGroupMessage(Long groupId, List<Long> recvIds, Boolean sendToSelf) {
UserSession session = SessionContext.getSession();
GroupMessageVO msgInfo = new GroupMessageVO();
msgInfo.setType(MessageType.GROUP_DEL.code());
msgInfo.setSendTime(new Date());
msgInfo.setGroupId(groupId);
msgInfo.setSendId(session.getUserId());
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvIds(recvIds);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
sendMessage.setSendToSelf(sendToSelf);
imClient.sendGroupMessage(sendMessage);
}
}

96
im-uniapp/App.vue

@ -139,12 +139,12 @@ export default {
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_NEW) {
if (msg.type == enums.MESSAGE_TYPE.FRIEND_NEW) {
this.friendStore.addFriend(JSON.parse(msg.content));
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_DEL) {
if (msg.type == enums.MESSAGE_TYPE.FRIEND_DEL) {
this.friendStore.removeFriend(friendId);
return;
}
@ -179,18 +179,22 @@ export default {
}, delayTime)
return;
}
let chatInfo = {
type: 'PRIVATE',
targetId: friend.id,
showName: friend.nickName,
headImage: friend.headImage
};
//
this.chatStore.openChat(chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
//
this.playAudioTip();
if (msgType.isNormal(msg.type) || msgType.isTip(msg.type) || msgType.isAction(msg.type)) {
let chatInfo = {
type: 'PRIVATE',
targetId: friend.id,
showName: friend.nickName,
headImage: friend.headImage
};
//
this.chatStore.openChat(chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
//
this.playAudioTip();
}
},
handleGroupMessage(msg) {
@ -224,14 +228,24 @@ export default {
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.RECALL) {
if (msg.type == enums.MESSAGE_TYPE.RECALL) {
this.chatStore.recallMessage(msg, chatInfo)
return;
}
this.loadGroupInfo(msg.groupId, (group) => {
//
this.insertGroupMessage(group, msg);
})
//
if (msg.type == enums.MESSAGE_TYPE.GROUP_NEW) {
this.groupStore.addGroup(JSON.parse(msg.content));
return;
}
//
if (msg.type == enums.MESSAGE_TYPE.GROUP_DEL) {
this.groupStore.removeGroup(msg.groupId);
return;
}
//
let group = this.loadGroupInfo(msg.groupId);
this.insertGroupMessage(group, msg);
},
handleSystemMessage(msg) {
if (msg.type == enums.MESSAGE_TYPE.USER_BANNED) {
@ -273,19 +287,22 @@ export default {
}, delayTime)
return;
}
let chatInfo = {
type: 'GROUP',
targetId: group.id,
showName: group.showGroupName,
headImage: group.headImageThumb
};
//
this.chatStore.openChat(chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
//
this.playAudioTip();
if (msgType.isNormal(msg.type) || msgType.isTip(msg.type) || msgType.isAction(msg.type)) {
let chatInfo = {
type: 'GROUP',
targetId: group.id,
showName: group.showGroupName,
headImage: group.headImageThumb
};
//
this.chatStore.openChat(chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
//
this.playAudioTip();
}
},
loadFriendInfo(id, callback) {
let friend = this.friendStore.findFriend(id);
@ -299,19 +316,16 @@ export default {
}
return friend;
},
loadGroupInfo(id, callback) {
loadGroupInfo(id) {
let group = this.groupStore.findGroup(id);
if (group) {
callback(group);
} else {
http({
url: `/group/find/${id}`,
method: 'GET'
}).then((group) => {
this.groupStore.addGroup(group);
callback(group)
})
if (!group) {
group = {
id: id,
showGroupName: "未知群聊",
headImageThumb: ""
}
}
return group;
},
exit() {
console.log("exit");

2
im-uniapp/common/enums.js

@ -16,6 +16,8 @@ const MESSAGE_TYPE = {
USER_BANNED: 50,
FRIEND_NEW: 80,
FRIEND_DEL: 81,
GROUP_NEW: 90,
GROUP_DEL: 91,
RTC_CALL_VOICE: 100,
RTC_CALL_VIDEO: 101,
RTC_ACCEPT: 102,

1
im-uniapp/store/chatStore.js

@ -90,6 +90,7 @@ export default defineStore('chatStore', {
},
readedMessage(pos) {
let chat = this.findChatByFriend(pos.friendId);
if (!chat) return;
chat.messages.forEach((m) => {
if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) {
// pos.maxId为空表示整个会话已读

8
im-uniapp/store/friendStore.js

@ -27,14 +27,10 @@ export default defineStore('friendStore', {
f.onlineApp = copy.onlineApp;
},
removeFriend(id) {
this.friends.forEach((f, idx) => {
if (f.id == id) {
this.friends[idx].deleted = true;
}
})
this.friends.filter(f => f.id == id).forEach(f => f.deleted = true);
},
addFriend(friend) {
if (this.friends.find((f) => f.id == friend.id)) {
if (this.friends.some((f) => f.id == friend.id)) {
this.updateFriend(friend)
} else {
this.friends.unshift(friend);

24
im-uniapp/store/groupStore.js

@ -4,29 +4,22 @@ import http from '@/common/request';
export default defineStore('groupStore', {
state: () => {
return {
groups: [],
activeIndex: -1
groups: []
}
},
actions: {
setGroups(groups) {
this.groups = groups;
},
activeGroup(index) {
this.activeIndex = index;
},
addGroup(group) {
this.groups.unshift(group);
if (this.groups.some((g) => g.id == group.id)) {
this.updateGroup(group);
} else {
this.groups.unshift(group);
}
},
removeGroup(groupId) {
this.groups.forEach((g, index) => {
if (g.id == groupId) {
this.groups.splice(index, 1);
if (this.activeIndex >= this.groups.length) {
this.activeIndex = this.groups.length - 1;
}
}
})
removeGroup(id) {
this.groups.filter(g => g.id == id).forEach(g => g.quit = true);
},
updateGroup(group) {
let g = this.findGroup(group.id);
@ -34,7 +27,6 @@ export default defineStore('groupStore', {
},
clear() {
this.groups = [];
this.activeGroup = -1;
},
loadGroup() {
return new Promise((resolve, reject) => {

2
im-web/src/api/enums.js

@ -15,6 +15,8 @@ const MESSAGE_TYPE = {
USER_BANNED: 50,
FRIEND_NEW: 80,
FRIEND_DEL: 81,
GROUP_NEW: 90,
GROUP_DEL: 91,
RTC_CALL_VOICE: 100,
RTC_CALL_VIDEO: 101,
RTC_ACCEPT: 102,

1
im-web/src/store/chatStore.js

@ -82,6 +82,7 @@ export default {
},
readedMessage(state, pos) {
let chat = this.getters.findChatByFriend(pos.friendId);
if (!chat) return;
chat.messages.forEach((m) => {
if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) {
// pos.maxId为空表示整个会话已读

23
im-web/src/store/friendStore.js

@ -28,25 +28,16 @@ export default {
})
},
activeFriend(state, idx) {
if (idx < 0) {
state.activeFriend = null;
} else {
state.activeFriend = state.friends[idx];
}
state.activeFriend = idx > 0 ? state.friends[idx] : null;
},
removeFriend(state, id) {
for (let idx in state.friends) {
if (id && state.friends[idx].id == id) {
state.friends[idx].deleted = true;
if (state.friends[idx] == state.activeFriend) {
state.activeFriend = null;
}
return;
}
state.friends.filter(f => f.id == id).forEach(f => f.deleted = true);
if (state.activeFriend && id == state.activeFriend.id) {
state.activeFriend = null;
}
},
addFriend(state, friend) {
if (state.friends.find((f) => f.id == friend.id)) {
if (state.friends.some((f) => f.id == friend.id)) {
this.commit("updateFriend", friend)
} else {
state.friends.unshift(friend);
@ -119,10 +110,10 @@ export default {
},
getters: {
isFriend: (state) => (userId) => {
return state.friends.filter((f)=>!f.deleted).some((f)=>f.id == userId);
return state.friends.filter((f) => !f.deleted).some((f) => f.id == userId);
},
findFriend: (state) => (userId) => {
return state.friends.find((f)=>f.id == userId);
return state.friends.find((f) => f.id == userId);
}
}
}

24
im-web/src/store/groupStore.js

@ -1,7 +1,6 @@
import http from '../api/httpRequest.js'
export default {
state: {
groups: [],
activeGroup: null,
@ -11,18 +10,18 @@ export default {
state.groups = groups;
},
activeGroup(state, idx) {
state.activeGroup = state.groups[idx];
state.activeGroup = idx > 0 ? state.groups[idx] : null;
},
addGroup(state, group) {
state.groups.unshift(group);
if (state.groups.some((g) => g.id == group.id)) {
this.commit("updateGroup", group)
} else {
state.groups.unshift(group);
}
},
removeGroup(state, groupId) {
state.groups.forEach((g, idx) => {
if (g.id == groupId) {
state.groups.splice(idx, 1);
}
})
if (state.activeGroup && state.activeGroup.id == groupId) {
removeGroup(state, id) {
state.groups.filter(g => g.id == id).forEach(g => g.quit = true);
if (state.activeGroup && id == state.activeGroup.id) {
state.activeGroup = null;
}
},
@ -53,5 +52,10 @@ export default {
})
});
}
},
getters: {
findGroup: (state) => (id) => {
return state.groups.find((g) => g.id == id);
}
}
}

4
im-web/src/view/Friend.vue

@ -47,8 +47,6 @@
</div>
</div>
</div>
<!-- <el-divider content-position="center"></el-divider>-->
</div>
</el-container>
</el-container>
@ -166,7 +164,7 @@ export default {
return this.$store.state.friendStore;
},
isFriend() {
return this.friendStore.friends.find((f) => f.id == this.userInfo.id);
return this.$store.getters.isFriend(this.userInfo.id);
}
}
}

51
im-web/src/view/Home.vue

@ -204,10 +204,11 @@ export default {
this.$refs.rtcPrivateVideo.onRTCMessage(msg)
return;
}
// id
let friend = this.loadFriendInfo(friendId);
this.insertPrivateMessage(friend, msg);
//
if (this.$msgType.isNormal(msg.type) || this.$msgType.isTip(msg.type) || this.$msgType.isAction(msg.type)) {
let friend = this.loadFriendInfo(friendId);
this.insertPrivateMessage(friend, msg);
}
},
insertPrivateMessage(friend, msg) {
let chatInfo = {
@ -261,6 +262,17 @@ export default {
this.$store.commit("recallMessage", [msg, chatInfo])
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.GROUP_NEW) {
this.$store.commit("addGroup", JSON.parse(msg.content));
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.GROUP_DEL) {
console.log("this.$enums.MESSAGE_TYPE.GROUP_DE")
this.$store.commit("removeGroup", msg.groupId);
return;
}
//
if (this.$msgType.isRtcGroup(msg.type)) {
this.$nextTick(() => {
@ -268,10 +280,11 @@ export default {
})
return;
}
this.loadGroupInfo(msg.groupId).then((group) => {
//
//
if (this.$msgType.isNormal(msg.type) || this.$msgType.isTip(msg.type) || this.$msgType.isAction(msg.type)) {
let group = this.loadGroupInfo(msg.groupId);
this.insertGroupMessage(group, msg);
})
}
},
insertGroupMessage(group, msg) {
let chatInfo = {
@ -292,7 +305,6 @@ export default {
},
handleSystemMessage(msg) {
//
if (msg.type == this.$enums.MESSAGE_TYPE.USER_BANNED) {
this.$wsApi.close(3000);
this.$alert("您的账号已被管理员封禁,原因:" + msg.content, "账号被封禁", {
@ -331,7 +343,7 @@ export default {
this.showSettingDialog = false;
},
loadFriendInfo(id) {
let friend = this.$store.state.friendStore.friends.find((f) => f.id == id);
let friend = this.$store.getters.findFriend(id);
if (!friend) {
friend = {
id: id,
@ -342,20 +354,15 @@ export default {
return friend;
},
loadGroupInfo(id) {
return new Promise((resolve, reject) => {
let group = this.$store.state.groupStore.groups.find((g) => g.id == id);
if (group) {
resolve(group);
} else {
this.$http({
url: `/group/find/${id}`,
method: 'get'
}).then((group) => {
resolve(group)
this.$store.commit("addGroup", group);
})
let group = this.$store.getters.findGroup(id);
if (!group) {
group = {
id: id,
showGroupName: "未知群聊",
headImageThumb: ""
}
});
}
return group;
}
},
computed: {

Loading…
Cancel
Save