u
This commit is contained in:
1 parent
63f1d4d1dc
commit
655581b217
17 files changed
+188
-146
No files matched your search
+17
-13
@@ -1,21 +1,12 @@
|
|||||||
package com.link.im.controller;
|
package com.link.im.controller;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.link.api.util.DruidDataSourceManager;
|
|
||||||
import com.link.api.util.PaginationUtils;
|
|
||||||
import com.link.api.util.Result;
|
import com.link.api.util.Result;
|
||||||
import com.link.api.util.UserContext;
|
import com.link.api.util.UserContext;
|
||||||
import com.link.im.manager.UserChannelManager;
|
|
||||||
import com.link.im.utils.DbUtils;
|
import com.link.im.utils.DbUtils;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@@ -29,15 +20,28 @@ public class CompanyController {
|
|||||||
|
|
||||||
List<Map<String, Object>> maps = DbUtils.queryForList(orgId, sql);
|
List<Map<String, Object>> maps = DbUtils.queryForList(orgId, sql);
|
||||||
|
|
||||||
for (Map<String, Object> map : maps) {
|
return Result.ok("成功",maps);
|
||||||
String bId = map.get("b_id").toString();
|
} catch (Exception e) {
|
||||||
|
return Result.fail("公司列表查询异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
map.put("_online",UserChannelManager.isOnline(bId));
|
@GetMapping("/api/loadFriendList")
|
||||||
}
|
public Result<?> loadFriendList(){
|
||||||
|
try {
|
||||||
|
String orgId = UserContext.getOrgId();
|
||||||
|
String sql = "select b_id,b_name from b_user where b_canuse = '1' ";
|
||||||
|
|
||||||
|
List<Map<String, Object>> maps = DbUtils.queryForList(orgId, sql);
|
||||||
|
|
||||||
return Result.ok("成功",maps);
|
return Result.ok("成功",maps);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return Result.fail("公司列表查询异常:" + e.getMessage());
|
return Result.fail("公司列表查询异常:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/api/loadGroupList")
|
||||||
|
public Result<?> loadGroupList(){
|
||||||
|
return Result.ok("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.link.im.handler;
|
package com.link.im.handler;
|
||||||
|
|
||||||
import com.link.api.util.UserContext;
|
|
||||||
import com.link.im.manager.UserChannelManager;
|
import com.link.im.manager.UserChannelManager;
|
||||||
import com.link.im.manager.WriteManager;
|
import com.link.im.manager.WriteManager;
|
||||||
import com.link.im.model.ImRequest;
|
import com.link.im.model.ImRequest;
|
||||||
@@ -9,7 +8,6 @@ import com.link.im.utils.CommonUtils;
|
|||||||
import com.link.im.utils.DbUtils;
|
import com.link.im.utils.DbUtils;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,35 +36,68 @@ public class ImMessageProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handlePing(ChannelHandlerContext ctx) {
|
private void handlePing(ChannelHandlerContext ctx) {
|
||||||
WriteManager.writeToChannel(ctx.channel(), ImResponse.success("pong",null));
|
WriteManager.writeToChannel(ctx.channel(), ImResponse.success("pong",null,null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleLogin(ChannelHandlerContext ctx,ImRequest request){
|
private void handleLogin(ChannelHandlerContext ctx,ImRequest request){
|
||||||
String fromUserid = request.getFrom();
|
|
||||||
UserChannelManager.addUser(fromUserid,ctx.channel());
|
|
||||||
String orgId = request.getOrgId();
|
String orgId = request.getOrgId();
|
||||||
|
|
||||||
System.out.println(orgId);
|
String fromUserid = request.getFrom();
|
||||||
|
// 单个上线
|
||||||
|
UserChannelManager.addUser(fromUserid,ctx.channel());
|
||||||
|
// 群组上线
|
||||||
|
|
||||||
// 1.通知给好友
|
// 1.通知给好友
|
||||||
String sql1 = "select b_frienduser_Id from IM_Friends where b_user_id = '" + fromUserid +"'";
|
|
||||||
try {
|
try {
|
||||||
List<Map<String, Object>> maps = DbUtils.queryForList(orgId, sql1);
|
String friendSql = "select b_frienduser_Id from IM_Friends where b_user_id = '" + fromUserid +"'";
|
||||||
|
|
||||||
|
List<Map<String, Object>> maps = DbUtils.queryForList(orgId, friendSql);
|
||||||
|
|
||||||
|
ImResponse response = ImResponse.success();
|
||||||
|
response.setType("online");
|
||||||
|
response.setFrom(fromUserid);
|
||||||
|
|
||||||
for (Map<String, Object> map : maps) {
|
for (Map<String, Object> map : maps) {
|
||||||
String bFrienduserId = map.get("b_frienduser_Id").toString();
|
String bFrienduserId = map.get("b_frienduser_Id").toString();
|
||||||
if (CommonUtils.isNotEmpty(bFrienduserId)) {
|
if (CommonUtils.isNotEmpty(bFrienduserId) && UserChannelManager.isOnline(bFrienduserId)) {
|
||||||
boolean friendOnline = UserChannelManager.isOnline(bFrienduserId);
|
WriteManager.writeToUser(bFrienduserId,response);
|
||||||
if(friendOnline){
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 查询用户所属群组
|
||||||
|
String sqlGroups = "SELECT b_group_id FROM IM_GroupMembers WHERE b_member_id = '" + fromUserid + "'";
|
||||||
|
List<Map<String, Object>> groupList = DbUtils.queryForList(orgId, sqlGroups);
|
||||||
|
|
||||||
|
for (Map<String, Object> g : groupList) {
|
||||||
|
String groupId = (String) g.get("b_group_id");
|
||||||
|
|
||||||
|
if (CommonUtils.isEmpty(groupId)) continue;
|
||||||
|
|
||||||
|
// 2. 查询群组成员
|
||||||
|
String sqlMembers = "SELECT b_member_id FROM IM_GroupMembers WHERE b_group_id = '" + groupId + "'";
|
||||||
|
List<Map<String, Object>> memberList = DbUtils.queryForList(orgId, sqlMembers);
|
||||||
|
|
||||||
|
// 3. 构造消息
|
||||||
|
ImResponse response = ImResponse.success();
|
||||||
|
response.setFrom(fromUserid);
|
||||||
|
response.setData(CommonUtils.mapOf("groupId", groupId));
|
||||||
|
|
||||||
|
// 4. 遍历群成员发送消息
|
||||||
|
for (Map<String, Object> member : memberList) {
|
||||||
|
String memberId = (String) member.get("b_member_id");
|
||||||
|
if (!fromUserid.equals(memberId)) {
|
||||||
|
WriteManager.writeToUser(memberId, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
// 2.通知给群组
|
|
||||||
|
|
||||||
// 3.通知给全部(测试用,先留,后面会删)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package com.link.im.handler;
|
package com.link.im.handler;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.link.im.manager.GroupManager;
|
|
||||||
import com.link.im.manager.UserChannelManager;
|
import com.link.im.manager.UserChannelManager;
|
||||||
import com.link.im.manager.WriteManager;
|
import com.link.im.manager.WriteManager;
|
||||||
import com.link.im.model.ImMessage;
|
|
||||||
import com.link.im.model.ImRequest;
|
import com.link.im.model.ImRequest;
|
||||||
import com.link.im.model.ImResponse;
|
import com.link.im.model.ImResponse;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
@@ -31,9 +29,6 @@ public class ImWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocke
|
|||||||
// 下线时通过 UserChannelManager 移除
|
// 下线时通过 UserChannelManager 移除
|
||||||
UserChannelManager.removeUserByChannel(ctx.channel());
|
UserChannelManager.removeUserByChannel(ctx.channel());
|
||||||
|
|
||||||
// 同时从所有群中移除
|
|
||||||
GroupManager.removeUserFromAllGroups(ctx.channel());
|
|
||||||
|
|
||||||
System.out.println("❌ 客户端断开: " + ctx.channel().id().asShortText());
|
System.out.println("❌ 客户端断开: " + ctx.channel().id().asShortText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
package com.link.im.manager;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class GroupManager {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(GroupManager.class);
|
|
||||||
|
|
||||||
// groupId -> Set<userId>
|
|
||||||
private static final ConcurrentHashMap<String, Set<String>> GROUP_MAP = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建或获取群成员集合
|
|
||||||
*/
|
|
||||||
private static Set<String> getGroup(String groupId) {
|
|
||||||
return GROUP_MAP.computeIfAbsent(groupId, k -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加成员到群
|
|
||||||
*/
|
|
||||||
public static void addUser(String groupId, String userId) {
|
|
||||||
if (groupId == null || userId == null) return;
|
|
||||||
getGroup(groupId).add(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从群中移除成员
|
|
||||||
*/
|
|
||||||
public static void removeUser(String groupId, String userId) {
|
|
||||||
Set<String> members = GROUP_MAP.get(groupId);
|
|
||||||
if (members != null) {
|
|
||||||
members.remove(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据 Channel 移除该用户在所有群的成员记录
|
|
||||||
*/
|
|
||||||
public static void removeUserFromAllGroups(Channel channel) {
|
|
||||||
if (channel == null) return;
|
|
||||||
|
|
||||||
// 遍历所有群
|
|
||||||
GROUP_MAP.forEach((groupId, members) -> {
|
|
||||||
members.removeIf(userId -> {
|
|
||||||
Channel ch = UserChannelManager.getChannel(userId);
|
|
||||||
boolean match = ch != null && ch.equals(channel);
|
|
||||||
if (match) {
|
|
||||||
logger.info("用户从群移除: groupId={}, userId={}", groupId, userId);
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群成员列表(不可修改)
|
|
||||||
*/
|
|
||||||
public static Set<String> getMembers(String groupId) {
|
|
||||||
Set<String> members = GROUP_MAP.get(groupId);
|
|
||||||
if (members == null) return Collections.emptySet();
|
|
||||||
return Collections.unmodifiableSet(members);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
package com.link.im.manager;
|
package com.link.im.manager;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.link.im.handler.ImWebSocketHandler;
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
|
|
||||||
public class WriteManager {
|
public class WriteManager {
|
||||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
@@ -15,8 +14,16 @@ public class WriteManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息 - 根据 userId
|
* 发送消息 - 根据 userId
|
||||||
|
* 内部会自动判断用户是否在线
|
||||||
*/
|
*/
|
||||||
public static void writeToUser(String userId, Object message) {
|
public static void writeToUser(String userId, Object message) {
|
||||||
|
if (userId == null || message == null) return;
|
||||||
|
|
||||||
|
if (!UserChannelManager.isOnline(userId)) {
|
||||||
|
// 用户不在线,直接跳过
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Channel ch = UserChannelManager.getChannel(userId);
|
Channel ch = UserChannelManager.getChannel(userId);
|
||||||
writeToChannel(ch, message);
|
writeToChannel(ch, message);
|
||||||
}
|
}
|
||||||
@@ -36,22 +43,25 @@ public class WriteManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向群内所有在线成员广播消息
|
* 广播消息给指定用户列表
|
||||||
* 只在异常时记录日志
|
* 内部会自动过滤离线用户
|
||||||
*/
|
*/
|
||||||
public static void writeToGroup(String groupId, Object message) {
|
public static void broadcastToUsers(List<String> userIds, Object message) {
|
||||||
Set<String> members = GroupManager.getMembers(groupId);
|
if (message == null || userIds == null || userIds.isEmpty()) return;
|
||||||
for (String userId : members) {
|
|
||||||
try {
|
|
||||||
Channel ch = UserChannelManager.getChannel(userId);
|
|
||||||
if (ch != null && ch.isActive() && message != null) {
|
|
||||||
String json = message instanceof String ? (String) message : MAPPER.writeValueAsString(message);
|
|
||||||
|
|
||||||
ch.writeAndFlush(new TextWebSocketFrame(json));
|
for (String userId : userIds) {
|
||||||
}
|
writeToUser(userId, message); // 内部自动判断在线
|
||||||
} catch (Exception e) {
|
}
|
||||||
logger.error("❌ 群消息发送失败 groupId={} userId={}", groupId, userId, e);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 广播消息给指定 Channel 列表
|
||||||
|
*/
|
||||||
|
public static void broadcastToChannels(List<Channel> channels, Object message) {
|
||||||
|
if (message == null || channels == null || channels.isEmpty()) return;
|
||||||
|
|
||||||
|
for (Channel ch : channels) {
|
||||||
|
writeToChannel(ch, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,37 +5,45 @@ public class ImResponse {
|
|||||||
private int code;
|
private int code;
|
||||||
private String message;
|
private String message;
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
|
private String from;
|
||||||
private Object data;
|
private Object data;
|
||||||
|
|
||||||
// 无参构造
|
public ImResponse() {
|
||||||
public ImResponse() {}
|
}
|
||||||
|
|
||||||
// 全参构造
|
public ImResponse(String type, int code, String message, long timestamp, String from,Object data) {
|
||||||
public ImResponse(String type, int code, String message, long timestamp, Object data) {
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
|
this.from = from;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImResponse success(String type, Object data) {
|
public static ImResponse success() {
|
||||||
return new ImResponse(type, 200, "success", System.currentTimeMillis(), data);
|
ImResponse response = new ImResponse();
|
||||||
|
response.setCode(200);
|
||||||
|
response.setMessage("success");
|
||||||
|
response.setTimestamp( System.currentTimeMillis());
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImResponse success(String type, String message, Object data) {
|
public static ImResponse success(String type, String from,Object data) {
|
||||||
return new ImResponse(type, 200, message, System.currentTimeMillis(), data);
|
return new ImResponse(type, 200, "success", System.currentTimeMillis(), from,data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImResponse success(String type, String message,String from,Object data) {
|
||||||
|
return new ImResponse(type, 200, message, System.currentTimeMillis(), from,data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImResponse error(String type, String message) {
|
public static ImResponse error(String type, String message) {
|
||||||
return new ImResponse(type, 500, message, System.currentTimeMillis(), null);
|
return new ImResponse(type, 500, message, System.currentTimeMillis(), null,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImResponse error(String type, int code, String message) {
|
public static ImResponse error(String type, int code, String message) {
|
||||||
return new ImResponse(type, code, message, System.currentTimeMillis(), null);
|
return new ImResponse(type, code, message, System.currentTimeMillis(), null,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter & Setter
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -68,6 +76,14 @@ public class ImResponse {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrom(String from) {
|
||||||
|
this.from = from;
|
||||||
|
}
|
||||||
|
|
||||||
public Object getData() {
|
public Object getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -75,15 +91,4 @@ public class ImResponse {
|
|||||||
public void setData(Object data) {
|
public void setData(Object data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ImResponse{" +
|
|
||||||
"type='" + type + '\'' +
|
|
||||||
", code=" + code +
|
|
||||||
", message='" + message + '\'' +
|
|
||||||
", timestamp=" + timestamp +
|
|
||||||
", data=" + data +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.link.im.utils;
|
package com.link.im.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class CommonUtils {
|
public class CommonUtils {
|
||||||
/**
|
/**
|
||||||
* 判断字符串是否为空(null 或 "")
|
* 判断字符串是否为空(null 或 "")
|
||||||
@@ -18,4 +21,29 @@ public class CommonUtils {
|
|||||||
public static boolean isNotEmpty(String str) {
|
public static boolean isNotEmpty(String str) {
|
||||||
return !isEmpty(str);
|
return !isEmpty(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速创建 Map,类似 JDK9 的 Map.of()
|
||||||
|
* 用法示例:
|
||||||
|
* MapUtils.of("groupId", 1, "userId", 2, "status", "online")
|
||||||
|
*/
|
||||||
|
public static <K, V> Map<K, V> mapOf(Object... keyValues) {
|
||||||
|
if (keyValues == null || keyValues.length == 0) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
if (keyValues.length % 2 != 0) {
|
||||||
|
throw new IllegalArgumentException("参数数量必须为偶数(键值成对出现)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<K, V> map = new HashMap<>(keyValues.length / 2);
|
||||||
|
for (int i = 0; i < keyValues.length; i += 2) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
K key = (K) keyValues[i];
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
V value = (V) keyValues[i + 1];
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@ import { useWebSocket } from './stores/websocketStore'
|
|||||||
const websocket = useWebSocket()
|
const websocket = useWebSocket()
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// websocket.disconnect()
|
websocket.disconnect()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
<el-table :data="tableData" border stripe style="width: 50%;">
|
<el-table :data="tableData" border stripe style="width: 50%;">
|
||||||
<el-table-column prop="b_name" label="用户名称" />
|
<el-table-column prop="b_name" label="用户名称" />
|
||||||
<el-table-column prop="_online" label="在线状态" />
|
|
||||||
|
|
||||||
<el-table-column fixed="right" label="操作">
|
<el-table-column fixed="right" label="操作">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|||||||
@@ -1,7 +1,42 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
import Avatar from '@/components/avatar.vue'
|
import Avatar from '@/components/avatar.vue'
|
||||||
import Icon from '@/components/icon.vue'
|
import Icon from '@/components/icon.vue'
|
||||||
|
import { axiosGet, axiosPost } from '@/utils/axios'
|
||||||
|
import { useUserStore } from '../../stores/userStore'
|
||||||
|
import { useWebSocket } from '../../stores/websocketStore'
|
||||||
|
|
||||||
|
const websocket = useWebSocket()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const friendList = ref([])
|
||||||
|
const groupList = ref([])
|
||||||
|
onMounted(async () => {
|
||||||
|
const userid = userStore.userInfo.userid
|
||||||
|
|
||||||
|
const friendSql = `
|
||||||
|
SELECT
|
||||||
|
f.*,
|
||||||
|
u.b_name,
|
||||||
|
m.b_content AS last_message,
|
||||||
|
m.b_SendTime AS last_message_time
|
||||||
|
FROM IM_Friends f
|
||||||
|
INNER JOIN b_user u
|
||||||
|
ON f.b_frienduser_id = u.b_id
|
||||||
|
LEFT JOIN IM_PrivateMessages m
|
||||||
|
ON f.b_last_message_id = m.b_id
|
||||||
|
WHERE f.b_user_id = '${userid}';
|
||||||
|
`
|
||||||
|
|
||||||
|
const friendRes = await axiosGet('/selectlistbysql', {
|
||||||
|
sql: friendSql,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (friendRes.data.records) {
|
||||||
|
friendList.value = friendRes.data.records
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -26,10 +61,13 @@ import Icon from '@/components/icon.vue'
|
|||||||
<Avatar name="User" />
|
<Avatar name="User" />
|
||||||
<div class="im__user-info">
|
<div class="im__user-info">
|
||||||
<div class="im__user-name">
|
<div class="im__user-name">
|
||||||
用户A
|
{{ userStore.userInfo.username }}
|
||||||
</div>
|
</div>
|
||||||
<div class="im__user-status im__user-status--online">
|
<div
|
||||||
<span>在线</span>
|
class="im__user-status"
|
||||||
|
:class="websocket.isConnected.value ? 'im__user-status--online' : 'im__user-status--offline'"
|
||||||
|
>
|
||||||
|
<span>{{ websocket.isConnected.value ? '在线' : '离线' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -350,6 +388,9 @@ import Icon from '@/components/icon.vue'
|
|||||||
.im__user-status--online {
|
.im__user-status--online {
|
||||||
color: #4caf50;
|
color: #4caf50;
|
||||||
}
|
}
|
||||||
|
.im__user-status--offline {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user