Browse Source

获取用户状态使用批量指令,减少io开销

master
xsx 2 years ago
parent
commit
9a2d1a2def
  1. 14
      im-client/src/main/java/com/bx/imclient/IMClient.java
  2. 29
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  3. 11
      im-platform/src/main/java/com/bx/implatform/controller/UserController.java
  4. 2
      im-platform/src/main/java/com/bx/implatform/service/IUserService.java
  5. 48
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  6. 49
      im-ui/src/components/friend/AddFriend.vue
  7. 2
      im-ui/src/view/Home.vue

14
im-client/src/main/java/com/bx/imclient/IMClient.java

@ -6,7 +6,9 @@ import com.bx.imcommon.model.IMPrivateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@Configuration
public class IMClient {
@ -23,6 +25,16 @@ public class IMClient {
return imSender.isOnline(userId);
}
/**
* 判断多个用户是否在线
*
* @param userIds 用户id列表
* @return 在线的用户列表
*/
public List<Long> isOnline(List<Long> userIds){
return imSender.isOnline(userIds);
}
/**
* 发送私聊消息发送结果通过MessageListener接收
*

29
im-client/src/main/java/com/bx/imclient/sender/IMSender.java

@ -1,5 +1,6 @@
package com.bx.imclient.sender;
import cn.hutool.core.collection.CollectionUtil;
import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
@ -13,7 +14,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Service
public class IMSender {
@ -153,4 +154,30 @@ public class IMSender {
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), "*");
return !redisTemplate.keys(key).isEmpty();
}
public List<Long> isOnline(List<Long> userIds){
if(CollectionUtil.isEmpty(userIds)){
return Collections.emptyList();
}
// 把所有用户的key都存起来
Map<String,Long> keyMap = new HashMap<>();
for(Long id:userIds){
for (Integer terminal : IMTerminalType.codes()) {
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, id.toString(), terminal.toString());
keyMap.put(key,id);
}
}
// 批量拉取
List<Object> serverIds = redisTemplate.opsForValue().multiGet(keyMap.keySet());
int idx = 0;
List<Long> onlineIds = new LinkedList<>();
for (Map.Entry<String,Long> entry : keyMap.entrySet()) {
// serverid有值表示用户在线
if(serverIds.get(idx++) != null){
onlineIds.add(entry.getValue());
}
}
// 去重并返回
return onlineIds.stream().distinct().collect(Collectors.toList());
}
}

11
im-platform/src/main/java/com/bx/implatform/controller/UserController.java

@ -1,6 +1,5 @@
package com.bx.implatform.controller;
import com.bx.implatform.dto.ModifyPwdDTO;
import com.bx.implatform.entity.User;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
@ -60,17 +59,9 @@ public class UserController {
return ResultUtils.success();
}
@GetMapping("/findByNickName")
@ApiOperation(value = "查找用户",notes="根据昵称查找用户")
public Result<List<UserVO>> findByNickName(@NotEmpty(message = "用户昵称不可为空") @RequestParam("nickName") String nickName){
return ResultUtils.success( userService.findUserByNickName(nickName));
}
@GetMapping("/findByName")
@ApiOperation(value = "查找用户",notes="根据用户名或昵称查找用户")
public Result<List<UserVO>> findByName(@NotEmpty(message = "用户名称不可为空") @RequestParam("name") String name){
public Result<List<UserVO>> findByName(@RequestParam("name") String name){
return ResultUtils.success( userService.findUserByName(name));
}
}

2
im-platform/src/main/java/com/bx/implatform/service/IUserService.java

@ -25,8 +25,6 @@ public interface IUserService extends IService<User> {
void update(UserVO vo);
List<UserVO> findUserByNickName(String nickname);
List<UserVO> findUserByName(String name);
List<Long> checkOnline(String userIds);

48
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -6,8 +6,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.util.JwtUtil;
import com.bx.implatform.config.JwtProperties;
import com.bx.implatform.dto.LoginDTO;
import com.bx.implatform.dto.ModifyPwdDTO;
import com.bx.implatform.dto.RegisterDTO;
import com.bx.implatform.entity.Friend;
import com.bx.implatform.entity.GroupMember;
import com.bx.implatform.entity.User;
@ -20,9 +23,6 @@ import com.bx.implatform.service.IUserService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.imcommon.util.JwtUtil;
import com.bx.implatform.dto.LoginDTO;
import com.bx.implatform.dto.RegisterDTO;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
@ -32,7 +32,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@ -62,7 +62,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
* 用户登录
*
* @param dto 登录dto
* @return
* @return 登录token
*/
@Override
@ -96,7 +96,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
* 用refreshToken换取新 token
*
* @param refreshToken
* @return
* @return 登录token
*/
@Override
public LoginVO refreshToken(String refreshToken) {
@ -206,24 +206,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
}
/**
* 根据用户昵称查询用户最多返回20条数据
*
* @param nickname 用户昵称
* @return
*/
@Override
public List<UserVO> findUserByNickName(String nickname) {
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.like(User::getNickName,nickname).last("limit 20");
List<User> users = this.list(queryWrapper);
return users.stream().map(u-> {
UserVO vo = BeanUtils.copyProperties(u,UserVO.class);
vo.setOnline(imClient.isOnline(u.getId()));
return vo;
}).collect(Collectors.toList());
}
/**
* 根据用户昵称查询用户最多返回20条数据
*
@ -238,9 +220,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
.like(User::getNickName,name)
.last("limit 20");
List<User> users = this.list(queryWrapper);
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Long> onlineUserIds = imClient.isOnline(userIds);
return users.stream().map(u-> {
UserVO vo = BeanUtils.copyProperties(u,UserVO.class);
vo.setOnline(imClient.isOnline(u.getId()));
vo.setOnline(onlineUserIds.contains(u.getId()));
return vo;
}).collect(Collectors.toList());
}
@ -249,19 +233,13 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
* 判断用户是否在线返回在线的用户id列表
*
* @param userIds 用户id多个用,分割
* @return
* @return 在线用户id列表
*/
@Override
public List<Long> checkOnline(String userIds) {
String[] idArr = userIds.split(",");
List<Long> onlineIds = new LinkedList<>();
for(String userId:idArr){
if(imClient.isOnline(Long.parseLong(userId))){
onlineIds.add(Long.parseLong(userId));
List<Long> userIdList = Arrays.stream(userIds.split(","))
.map(Long::parseLong).collect(Collectors.toList());
return imClient.isOnline(userIdList);
}
}
return onlineIds;
}
}

49
im-ui/src/components/friend/AddFriend.vue

@ -1,6 +1,6 @@
<template>
<el-dialog title="添加好友" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
<el-input placeholder="输入好友昵称,最多展示10条" class="input-with-select" v-model="searchText" @keyup.enter.native="handleSearch()">
<el-input placeholder="输入用户名或昵称,最多展示20条" class="input-with-select" v-model="searchText" @keyup.enter.native="handleSearch()">
<el-button slot="append" icon="el-icon-search" @click="handleSearch()"></el-button>
</el-input>
<el-scrollbar style="height:400px">
@ -10,11 +10,16 @@
<head-image :url="user.headImage"></head-image>
</div>
<div class="add-friend-text">
<div>{{user.nickName}}</div>
<div class="text-user-name">
<div>{{user.userName}}</div>
<div :class="user.online ? 'online-status online':'online-status'">{{ user.online?"[在线]":"[离线]"}}</div>
</div>
<el-button type="success" v-show="!isFriend(user.id)" plain @click="handleAddFriend(user)">添加</el-button>
<el-button type="info" v-show="isFriend(user.id)" plain disabled>已添加</el-button>
<div class="text-nick-name">
<div>昵称:{{user.nickName}}</div>
</div>
</div>
<el-button type="success" size="small" v-show="!isFriend(user.id)" plain @click="handleAddFriend(user)">添加</el-button>
<el-button type="info" size="small" v-show="isFriend(user.id)" plain disabled>已添加</el-button>
</div>
</div>
</el-scrollbar>
@ -45,10 +50,10 @@
},
handleSearch() {
this.$http({
url: "/user/findByNickName",
url: "/user/findByName",
method: "get",
params: {
nickName: this.searchText
name: this.searchText
}
}).then((data) => {
this.users = data;
@ -77,18 +82,16 @@
let friend = friends.find((f)=> f.id==userId);
return friend != undefined;
}
},
mounted() {
this.handleSearch();
}
}
</script>
<style scoped lang="scss">
<style lang="scss">
.el-dialog {
min-width: 400px;
}
.item {
height: 80px;
height: 65px;
display: flex;
position: relative;
padding-left: 15px;
@ -97,14 +100,19 @@
.add-friend-text {
margin-left: 15px;
line-height: 80px;
flex: 3;
display: flex;
flex-direction: row;
height: 100%;
flex-direction: column;
flex-shrink: 0;
overflow: hidden;
.text-user-name{
display: flex;
flex-direction: row;
font-weight: 600;
font-size: 16px;
line-height: 25px;
.online-status{
font-size: 12px;
font-weight: 600;
@ -113,5 +121,14 @@
}
}
}
.text-nick-name{
display: flex;
flex-direction: row;
font-size: 12px;
line-height: 20px;
}
}
}
</style>

2
im-ui/src/view/Home.vue

@ -27,7 +27,7 @@
<el-menu-item title="设置" @click="showSetting()">
<span class="el-icon-setting"></span>
</el-menu-item>
<el-menu-item title="长得帅的都已经star啦">
<el-menu-item title="小伙子这么帅,点点star吧~">
<a href="https://gitee.com/bluexsx/box-im" target="_blank">
<el-image style="width: 30px; height: 30px" src="https://gitee.com/favicon.ico" fit="fit">
</el-image>

Loading…
Cancel
Save