Browse Source

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

xsx 2 years ago
parent
commit
9a2d1a2def

+ 13 - 1
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接收)
      *

+ 28 - 1
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());
+    }
 }

+ 1 - 10
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));
     }
 }

+ 0 - 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);

+ 13 - 35
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));
-            }
-        }
-        return onlineIds;
+        List<Long> userIdList = Arrays.stream(userIds.split(","))
+            .map(Long::parseLong).collect(Collectors.toList());
+        return imClient.isOnline(userIdList);
     }
 
-
 }

+ 38 - 21
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="user.online ? 'online-status  online':'online-status'">{{ user.online?"[在线]":"[离线]"}}</div>
+						<div class="text-user-name">
+							<div>{{user.userName}}</div>
+							<div :class="user.online ? 'online-status  online':'online-status'">{{ user.online?"[在线]":"[离线]"}}</div>
+						</div>
+						<div class="text-nick-name">
+							<div>昵称:{{user.nickName}}</div>
+						</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>
+					 <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,21 +100,35 @@
 		
 		.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;
 		
-			.online-status{
-				font-size: 12px;
+			.text-user-name{
+				display: flex;
+				flex-direction: row;
 				font-weight: 600;
-				&.online{
-					color: #5fb878;
+				font-size: 16px;
+				line-height: 25px;
+		
+				.online-status{
+					font-size: 12px;
+					font-weight: 600;
+					&.online{
+						color: #5fb878;
+					}
 				}
 			}
+			
+			.text-nick-name{
+				display: flex;
+				flex-direction: row;
+				font-size: 12px;
+				line-height: 20px;
+			}
+			
 		}
 	}
 </style>

+ 1 - 1
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>