Compare commits

...

2 Commits

Author SHA1 Message Date
[yxf] f01e680f56 修改遗留问题 2 weeks ago
[yxf] 6a3493b356 修改注释 3 weeks ago
  1. 4
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  2. 4
      im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java
  3. 7
      im-platform/src/main/java/com/bx/implatform/service/UserService.java
  4. 103
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  5. 2
      im-platform/src/main/java/com/bx/implatform/util/IpLocationUtil.java
  6. 75
      im-platform/src/main/java/com/bx/implatform/util/IpUtils.java
  7. 1
      im-web/src/components/chat/ChatBox.vue
  8. 42
      im-web/src/store/chatStore.js
  9. 136
      im-web/src/view/Home.vue
  10. 35
      im-web/src/view/Login.vue

4
im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java

@ -23,9 +23,9 @@ public class GroupMessageListener implements MessageListener<GroupMessageVO> {
public void process(List<IMSendResult<GroupMessageVO>> results) { public void process(List<IMSendResult<GroupMessageVO>> results) {
for(IMSendResult<GroupMessageVO> result:results){ for(IMSendResult<GroupMessageVO> result:results){
GroupMessageVO messageInfo = result.getData(); GroupMessageVO messageInfo = result.getData();
if (result.getCode().equals(IMSendCode.SUCCESS.code())) { // if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
// log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); // log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
} // }
} }
} }

4
im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java

@ -18,9 +18,9 @@ public class SystemMessageListener implements MessageListener<SystemMessageVO> {
public void process(List<IMSendResult<SystemMessageVO>> results) { public void process(List<IMSendResult<SystemMessageVO>> results) {
for(IMSendResult<SystemMessageVO> result : results){ for(IMSendResult<SystemMessageVO> result : results){
SystemMessageVO messageInfo = result.getData(); SystemMessageVO messageInfo = result.getData();
if (result.getCode().equals(IMSendCode.SUCCESS.code())) { // if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
// log.info("消息送达,消息id:{},接收者:{},终端:{}", messageInfo.getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); // log.info("消息送达,消息id:{},接收者:{},终端:{}", messageInfo.getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
} // }
} }
} }
} }

7
im-platform/src/main/java/com/bx/implatform/service/UserService.java

@ -130,13 +130,6 @@ public interface UserService extends IService<User> {
*/ */
List<OnlineTerminalVO> getOnlineTerminals(String userIds); List<OnlineTerminalVO> getOnlineTerminals(String userIds);
/**
* 记录ip与ip地址
*
* @param user 用户
*/
void updateIpAndAddress(User user);
/** /**
* 转接客服 * 转接客服
* @param customerId 客服id targetId 转接的客服id userId 转接的用户id * @param customerId 客服id targetId 转接的客服id userId 转接的用户id

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

@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient; import com.bx.imclient.IMClient;
import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMSystemMessage;
import com.bx.imcommon.util.JwtUtil; import com.bx.imcommon.util.JwtUtil;
import com.bx.implatform.config.props.JwtProperties; import com.bx.implatform.config.props.JwtProperties;
import com.bx.implatform.dto.LoginDTO; import com.bx.implatform.dto.LoginDTO;
@ -20,17 +21,14 @@ import com.bx.implatform.entity.*;
import com.bx.implatform.enums.ResultCode; import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.exception.GlobalException; import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.mapper.UserMapper; import com.bx.implatform.mapper.UserMapper;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.*; import com.bx.implatform.service.*;
import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession; import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils; import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.util.IpUtils;
import com.bx.implatform.util.SensitiveFilterUtil; import com.bx.implatform.util.SensitiveFilterUtil;
import com.bx.implatform.vo.*; import com.bx.implatform.vo.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -39,8 +37,6 @@ import org.springframework.util.StringUtils;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.bx.implatform.enums.ResultCode.XSS_PARAM_ERROR;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -193,38 +189,39 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
/** /**
* 通过uniqueToken查询客服ID如果有多条匹配则随机返回一条 * 通过uniqueToken查询客服ID优先分配在线客服在线客服为空则随机分配离线客服
*/ */
public Long getCustomerServiceIdByUniqueToken(String uniqueToken) { public Long getCustomerServiceIdByUniqueToken(String uniqueToken) {
// 1. 构建查询条件
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(User::getIsCustomer, 2); // 只查客服 queryWrapper.eq(User::getIsCustomer, 2);
if (StrUtil.isNotBlank(uniqueToken)) {
queryWrapper.eq(User::getUniqueToken, uniqueToken); queryWrapper.eq(User::getUniqueToken, uniqueToken);
}
queryWrapper.select(User::getId); queryWrapper.select(User::getId);
// 2. 查询所有匹配的客服
List<User> customerList = this.list(queryWrapper); List<User> customerList = this.list(queryWrapper);
// 3. 如果没有匹配的客服,抛出异常
if (customerList == null || customerList.isEmpty()) { if (customerList == null || customerList.isEmpty()) {
throw new GlobalException("未找到对应的客服,请检查 uniqueToken 是否正确"); throw new GlobalException("未找到对应的客服,请检查 uniqueToken 是否正确");
} }
// 4. 如果只有一条,直接返回 List<Long> allCustomerIds = customerList.stream()
if (customerList.size() == 1) { .map(User::getId)
Long customerId = customerList.get(0).getId(); .collect(Collectors.toList());
return customerId; List<Long> onlineCustomerIds = imClient.getOnlineUser(allCustomerIds);
}
// 5. 如果有多条,随机选择一条返回 List<User> onlineCustomerList = customerList.stream()
int randomIndex = new Random().nextInt(customerList.size()); .filter(user -> onlineCustomerIds.contains(user.getId()))
Long randomCustomerId = customerList.get(randomIndex).getId(); .collect(Collectors.toList());
List<User> targetList;
if (!onlineCustomerList.isEmpty()) {
targetList = onlineCustomerList;
} else {
targetList = customerList;
}
return randomCustomerId; // 6. 随机选择一个
int randomIndex = new Random().nextInt(targetList.size());
return targetList.get(randomIndex).getId();
} }
@Override @Override
@ -320,11 +317,18 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
throw new GlobalException(ResultCode.PASSWOR_ERROR); throw new GlobalException(ResultCode.PASSWOR_ERROR);
} }
//未设置套餐或套餐过期
if(imAgentService.isPackageExpire(user.getUniqueToken())) { if(imAgentService.isPackageExpire(user.getUniqueToken())) {
throw new GlobalException("套餐已过期"); throw new GlobalException("套餐已过期");
} }
SystemMessageVO msgVo = new SystemMessageVO();
msgVo.setType(2);
IMSystemMessage<SystemMessageVO> systemMessage = new IMSystemMessage<>();
systemMessage.getRecvIds().add(user.getId());
systemMessage.setData(msgVo);
imClient.sendSystemMessage(systemMessage);
// 生成token // 生成token
UserSession session = BeanUtils.copyProperties(user, UserSession.class); UserSession session = BeanUtils.copyProperties(user, UserSession.class);
session.setUserId(user.getId()); session.setUserId(user.getId());
@ -339,9 +343,50 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
vo.setAccessTokenExpiresIn(jwtProperties.getAccessTokenExpireIn()); vo.setAccessTokenExpiresIn(jwtProperties.getAccessTokenExpireIn());
vo.setRefreshToken(refreshToken); vo.setRefreshToken(refreshToken);
vo.setRefreshTokenExpiresIn(jwtProperties.getRefreshTokenExpireIn()); vo.setRefreshTokenExpiresIn(jwtProperties.getRefreshTokenExpireIn());
vo.setUser(user);
return vo; return vo;
} }
// @Override
// public LoginVO loginCustom(LoginDTO dto) {
// User user = this.findUserByUserName(dto.getUserName());
// if (Objects.isNull(user)) {
// throw new GlobalException("用户不存在");
// }
// if(user.getIsCustomer() == 1){
// throw new GlobalException("用户不存在");
// }
// if (user.getIsBanned()) {
// String tip = String.format("您的账号因'%s'已被管理员封禁,请联系客服!",user.getReason());
// throw new GlobalException(tip);
// }
// if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
// throw new GlobalException(ResultCode.PASSWOR_ERROR);
// }
//
// //未设置套餐或套餐过期
// if(imAgentService.isPackageExpire(user.getUniqueToken())) {
// throw new GlobalException("套餐已过期");
// }
//
// // 生成token
// UserSession session = BeanUtils.copyProperties(user, UserSession.class);
// session.setUserId(user.getId());
// session.setTerminal(dto.getTerminal());
// String strJson = JSON.toJSONString(session);
// String accessToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getAccessTokenExpireIn(),
// jwtProperties.getAccessTokenSecret());
// String refreshToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getRefreshTokenExpireIn(),
// jwtProperties.getRefreshTokenSecret());
// LoginVO vo = new LoginVO();
// vo.setAccessToken(accessToken);
// vo.setAccessTokenExpiresIn(jwtProperties.getAccessTokenExpireIn());
// vo.setRefreshToken(refreshToken);
// vo.setRefreshTokenExpiresIn(jwtProperties.getRefreshTokenExpireIn());
// return vo;
// }
@Override @Override
public LoginVO refreshToken(String refreshToken) { public LoginVO refreshToken(String refreshToken) {
//验证 token //验证 token
@ -614,16 +659,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return vos; return vos;
} }
@Override
public void updateIpAndAddress(User user) {
String ip = user.getLastLoginIp();
String address = IpUtils.getIpAddress(ip);
this.update(new UpdateWrapper<User>().lambda()
.eq(User::getId, user.getId())
.set(User::getLastLoginIp, ip)
.set(User::getIpAddress, address));
}
@Override @Override
public void changeCustomer(Long customerId, Long targetId, Long userId) { public void changeCustomer(Long customerId, Long targetId, Long userId) {
// 更新好友关系 // 更新好友关系

2
im-platform/src/main/java/com/bx/implatform/util/IpLocationUtil.java

@ -36,7 +36,7 @@ public class IpLocationUtil {
String city = result.getStr("city", ""); String city = result.getStr("city", "");
// 拼接中文地址 // 拼接中文地址
return (country + " " + regionName + " " + city).trim(); return country + " " + regionName + " " + city;
} catch (Exception e) { } catch (Exception e) {
log.error("IP地理位置查询异常, ip:{}", ip, e); log.error("IP地理位置查询异常, ip:{}", ip, e);

75
im-platform/src/main/java/com/bx/implatform/util/IpUtils.java

@ -1,75 +0,0 @@
package com.bx.implatform.util;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* IP 地址查询工具类
* 使用 ipify httpbin 获取 IP 地址的地理位置信息
*/
@Slf4j
@Component
public class IpUtils {
private static final String IP_API = "http://ip-api.com/json/";
/**
* 根据 IP 地址查询地理位置
*
* @param ip IP 地址
* @return 地理位置信息格式如"中国广东省深圳市"
*/
public static String getIpAddress(String ip) {
System.out.println("333333333");
System.out.println(ip);
if (ip == null || ip.trim().isEmpty() || "unknown".equalsIgnoreCase(ip)) {
return "";
}
try {
// 使用 ip-api.com 查询 IP 归属地
String url = IP_API + ip;
String response = HttpUtil.get(url, 5000);
System.out.println("2222222222222222");
System.out.println( response);
if (response != null && !response.isEmpty()) {
JSONObject json = JSON.parseObject(response);
String status = json.getString("status");
if ("success".equals(status)) {
String country = json.getString("country");
String regionName = json.getString("regionName");
String city = json.getString("city");
StringBuilder address = new StringBuilder();
if (country != null && !country.isEmpty()) {
address.append(country);
}
if (regionName != null && !regionName.isEmpty() && !regionName.equals(country)) {
address.append(regionName);
}
if (city != null && !city.isEmpty() && !city.equals(regionName)) {
address.append(city);
}
// log.info("IP 地址查询成功:ip={}, address={}", ip, address);
return address.toString();
}
}
} catch (Exception e) {
log.error("IP 地址查询失败:ip={}, error={}", ip, e.getMessage());
}
// 如果查询失败,返回空字符串
return "";
}
}

1
im-web/src/components/chat/ChatBox.vue

@ -871,7 +871,6 @@ export default {
this.ipLocation = location; this.ipLocation = location;
} }
// userInfoIP
this.userInfo = userInfo; this.userInfo = userInfo;
this.updateFriendInfo(); this.updateFriendInfo();

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

@ -503,15 +503,43 @@ export default defineStore('chatStore', {
let userStore = useUserStore(); let userStore = useUserStore();
let userId = userStore.userInfo.id; let userId = userStore.userInfo.id;
let key = "chats-" + userId; let key = "chats-" + userId;
// ✅ 获取清理名单
let cleanedFriendIds = [];
try {
const saved = sessionStorage.getItem("cleanedOfflineFriends");
if (saved) {
cleanedFriendIds = JSON.parse(saved);
}
} catch (e) {
console.error("读取清理名单失败", e);
}
localForage.getItem(key).then((chatsData) => { localForage.getItem(key).then((chatsData) => {
if (!chatsData) { if (!chatsData) {
resolve(); resolve();
} else if (chatsData.chatKeys) { } else if (chatsData.chatKeys) {
const promises = []; const promises = [];
chatsData.chatKeys.forEach(key => {
// ✅ 过滤掉需要清理的会话key
const validChatKeys = chatsData.chatKeys.filter(chatKey => {
// 检查是否是私聊会话且需要被清理
if (cleanedFriendIds.length > 0) {
for (let cleanedId of cleanedFriendIds) {
if (chatKey.includes(`-PRIVATE-${cleanedId}`)) {
console.log(`跳过清理的会话: ${chatKey}`);
return false;
}
}
}
return true;
});
validChatKeys.forEach(key => {
promises.push(localForage.getItem(key)) promises.push(localForage.getItem(key))
promises.push(localForage.getItem(key + "-hot")) promises.push(localForage.getItem(key + "-hot"))
}) })
Promise.all(promises).then(chats => { Promise.all(promises).then(chats => {
chatsData.chats = []; chatsData.chats = [];
// 偶数下标为冷消息,奇数下标为热消息 // 偶数下标为冷消息,奇数下标为热消息
@ -530,14 +558,20 @@ export default defineStore('chatStore', {
// 冷热消息合并 // 冷热消息合并
let chat = Object.assign({}, coldChat, hotChat); let chat = Object.assign({}, coldChat, hotChat);
if (hotChat && coldChat) { if (hotChat && coldChat) {
chat.messages = coldChat.messages.concat(hotChat chat.messages = coldChat.messages.concat(hotChat.messages)
.messages)
} }
// 历史版本没有readedMessageIdx字段,做兼容一下 // 历史版本没有readedMessageIdx字段,做兼容一下
chat.readedMessageIdx = chat.readedMessageIdx || 0; chat.readedMessageIdx = chat.readedMessageIdx || 0;
chatsData.chats.push(chat); chatsData.chats.push(chat);
} }
this.initChats(chatsData); this.initChats(chatsData);
const cleanedKeys = chatsData.chatKeys.filter(key => !validChatKeys.includes(key));
cleanedKeys.forEach(key => {
localForage.removeItem(key);
localForage.removeItem(key + "-hot");
});
resolve(); resolve();
}) })
} }
@ -546,7 +580,7 @@ export default defineStore('chatStore', {
reject(e); reject(e);
}) })
}) })
} },
}, },
getters: { getters: {
isLoading: (state) => () => { isLoading: (state) => () => {

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

@ -111,7 +111,9 @@ export default {
lastPlayAudioTime: new Date().getTime() - 1000, lastPlayAudioTime: new Date().getTime() - 1000,
reconnecting: false, reconnecting: false,
privateMessagesBuffer: [], privateMessagesBuffer: [],
groupMessagesBuffer: [] groupMessagesBuffer: [],
cleanedFriendIds: [],
switchingAccount: false
} }
}, },
methods: { methods: {
@ -170,6 +172,14 @@ export default {
}); });
this.$wsApi.onClose((e) => { this.$wsApi.onClose((e) => {
console.log('WebSocket 关闭,状态码:', e.code); //
//
if (this.switchingAccount) {
console.log('切换账号中,不重连');
return;
}
if (e.code != 3000) { if (e.code != 3000) {
if (!this.reconnecting) { if (!this.reconnecting) {
this.reconnectWs(); this.reconnectWs();
@ -205,6 +215,20 @@ export default {
async onSwitchAccount(targetUser) { async onSwitchAccount(targetUser) {
this.showAccountMenu = false; this.showAccountMenu = false;
try {
await this.$confirm(
`确定要切换到账号 ${targetUser.nickName} 吗?\n如果该账号在其他地方已登录,将被强制下线。`,
'切换账号',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
} catch {
return;
}
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: '正在切换账号...', text: '正在切换账号...',
@ -226,22 +250,35 @@ export default {
if (loginData.user) { if (loginData.user) {
this.setCookie('username', loginData.user.userName); this.setCookie('username', loginData.user.userName);
sessionStorage.setItem("accessToken", loginData.accessToken); this.switchingAccount = true;
sessionStorage.setItem("refreshToken", loginData.refreshToken);
sessionStorage.setItem("newAccessToken", loginData.accessToken);
sessionStorage.setItem("newRefreshToken", loginData.refreshToken);
localStorage.setItem('userInfo', JSON.stringify(loginData.user)); localStorage.setItem('userInfo', JSON.stringify(loginData.user));
this.userStore.setUserInfo(loginData.user);
} }
loading.close(); loading.close();
this.$message.success(`已切换到客服账号:${targetUser.nickName}`); this.$message.success(`已切换到客服账号:${targetUser.nickName}`);
// WebSocket
this.$wsApi.close(3000);
setTimeout(() => { setTimeout(() => {
const newToken = sessionStorage.getItem("newAccessToken");
const newRefreshToken = sessionStorage.getItem("newRefreshToken");
if (newToken) {
sessionStorage.setItem("accessToken", newToken);
sessionStorage.setItem("refreshToken", newRefreshToken);
}
sessionStorage.removeItem("newAccessToken");
sessionStorage.removeItem("newRefreshToken");
sessionStorage.setItem("needCleanOffline", "true");
sessionStorage.removeItem("cleanedOfflineFriends");
this.switchingAccount = false;
window.location.reload(); window.location.reload();
}, 300); }, 1500);
} catch (error) { } catch (error) {
loading.close(); loading.close();
@ -336,6 +373,12 @@ export default {
this.groupMessagesBuffer = []; this.groupMessagesBuffer = [];
this.chatStore.setLoading(false); this.chatStore.setLoading(false);
this.chatStore.refreshChats(); this.chatStore.refreshChats();
if (sessionStorage.getItem("needCleanOffline") === "true") {
sessionStorage.removeItem("needCleanOffline");
this.cleanOfflineChats();
}
}).catch((e) => { }).catch((e) => {
console.log(e); console.log(e);
this.$message.error("拉取离线消息失败"); this.$message.error("拉取离线消息失败");
@ -343,6 +386,33 @@ export default {
}); });
}, },
cleanOfflineChats() {
const onlineFriendIds = this.friendStore.onlineFriendIds || [];
const removedIds = [];
this.chatStore.chats.forEach(chat => {
if (chat.type === 'PRIVATE' && !onlineFriendIds.includes(chat.targetId)) {
removedIds.push(chat.targetId);
}
});
if (removedIds.length > 0) {
sessionStorage.setItem("cleanedOfflineFriends", JSON.stringify(removedIds));
this.cleanedFriendIds = removedIds;
this.chatStore.chats = this.chatStore.chats.filter(chat => {
return !(chat.type === 'PRIVATE' && removedIds.includes(chat.targetId));
});
this.chatStore.chats.forEach(chat => {
chat.stored = false;
});
this.chatStore.saveToStorage(true);
console.log(`登录清理完成: 移除 ${removedIds.length} 个离线会话`, removedIds);
}
},
pullPrivateOfflineMessage(minId) { pullPrivateOfflineMessage(minId) {
return this.$http({ return this.$http({
url: "/message/private/loadOfflineMessage?minId=" + minId, url: "/message/private/loadOfflineMessage?minId=" + minId,
@ -401,6 +471,15 @@ export default {
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
//
if (this.cleanedFriendIds.includes(friend.id)) {
// maxId
if (msg.id && msg.id > this.chatStore.privateMsgMaxId) {
this.chatStore.privateMsgMaxId = msg.id;
}
return;
}
let chatInfo = { let chatInfo = {
type: 'PRIVATE', type: 'PRIVATE',
targetId: friend.id, targetId: friend.id,
@ -414,7 +493,8 @@ export default {
this.$msgType.isNormal(msg.type) && msg.status != this.$enums.MESSAGE_STATUS.READED) { this.$msgType.isNormal(msg.type) && msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip(); this.playAudioTip();
} }
}, },
handleGroupMessage(msg) { handleGroupMessage(msg) {
msg.selfSend = msg.sendId == this.userStore.userInfo.id; msg.selfSend = msg.sendId == this.userStore.userInfo.id;
@ -483,6 +563,9 @@ export default {
}, },
handleSystemMessage(msg) { handleSystemMessage(msg) {
console.log('系统消息完整内容:', JSON.stringify(msg)); //
console.log('系统消息 type:', msg.type); // type
if (msg.type == this.$enums.MESSAGE_TYPE.USER_BANNED) { if (msg.type == this.$enums.MESSAGE_TYPE.USER_BANNED) {
this.$wsApi.close(3000); this.$wsApi.close(3000);
this.$alert("您的账号已被管理员封禁,原因:" + msg.content, "账号被封禁", { this.$alert("您的账号已被管理员封禁,原因:" + msg.content, "账号被封禁", {
@ -493,7 +576,27 @@ export default {
}); });
return; return;
} }
},
// 线type=2
if (msg.type == 2) {
console.log('收到强制下线通知'); //
//
if (this.switchingAccount) {
console.log('正在切换账号,忽略强制下线通知');
return;
}
this.$wsApi.close(3000);
this.$alert("您已在其他地方登录,将被强制下线", "强制下线通知", {
confirmButtonText: '确定',
callback: () => {
this.onExit();
}
});
return;
}
},
closeUserInfo() { closeUserInfo() {
if (this.$refs.userInfo) { if (this.$refs.userInfo) {
@ -512,6 +615,8 @@ export default {
onExit() { onExit() {
this.showAccountMenu = false; this.showAccountMenu = false;
this.cleanedFriendIds = [];
sessionStorage.removeItem("cleanedOfflineFriends");
this.unloadStore(); this.unloadStore();
this.configStore.setAppInit(false); this.configStore.setAppInit(false);
this.$wsApi.close(3000); this.$wsApi.close(3000);
@ -520,6 +625,7 @@ export default {
location.href = "/"; location.href = "/";
}, },
playAudioTip() { playAudioTip() {
if (new Date().getTime() - this.lastPlayAudioTime > 1000) { if (new Date().getTime() - this.lastPlayAudioTime > 1000) {
this.lastPlayAudioTime = new Date().getTime(); this.lastPlayAudioTime = new Date().getTime();
@ -586,8 +692,16 @@ export default {
mounted() { mounted() {
const savedFullScreen = localStorage.getItem('im_full_screen') === 'true'; const savedFullScreen = localStorage.getItem('im_full_screen') === 'true';
this.configStore.setFullScreen(savedFullScreen); this.configStore.setFullScreen(savedFullScreen);
//
const saved = sessionStorage.getItem("cleanedOfflineFriends");
if (saved) {
this.cleanedFriendIds = JSON.parse(saved);
}
this.init(); this.init();
}, },
unmounted() { unmounted() {
this.$wsApi.close(); this.$wsApi.close();
} }

35
im-web/src/view/Login.vue

@ -89,53 +89,24 @@ export default {
data: this.loginForm data: this.loginForm
}); });
// cookie
this.setCookie('username', this.loginForm.userName); this.setCookie('username', this.loginForm.userName);
this.setCookie('password', this.loginForm.password); this.setCookie('password', this.loginForm.password);
// token
sessionStorage.setItem("accessToken", data.accessToken); sessionStorage.setItem("accessToken", data.accessToken);
sessionStorage.setItem("refreshToken", data.refreshToken); sessionStorage.setItem("refreshToken", data.refreshToken);
this.$message.success("登录成功"); sessionStorage.setItem("needCleanOffline", "true");
sessionStorage.removeItem("cleanedOfflineFriends");
// this.$message.success("登录成功");
this.$router.push("/home/chat"); this.$router.push("/home/chat");
//
this.$nextTick(() => {
this.initAfterLogin();
});
} catch (error) { } catch (error) {
console.error('登录失败:', error); console.error('登录失败:', error);
this.$message.error('登录失败'); this.$message.error('登录失败');
} }
}, },
async initAfterLogin() {
const friendStore = useFriendStore();
const chatStore = useChatStore();
try {
await friendStore.loadFriend();
await chatStore.loadChat();
chatStore.refreshChats();
chatStore.setLoading(false);
const removedCount = chatStore.cleanOfflineChats();
console.log(`=== 初始化完成: 清理了 ${removedCount} 个离线会话 ===`);
} catch (error) {
console.error('初始化失败:', error);
}
},
resetForm(formName) { resetForm(formName) {
this.$refs[formName].resetFields(); this.$refs[formName].resetFields();

Loading…
Cancel
Save