diff --git a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java index e6827c2..31fd57c 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java @@ -68,7 +68,7 @@ public class UserController { @Operation(summary = "保存用户分组", description = "单个分组,保存到 group_ids 字段") public Result saveGroup(@RequestBody JSONObject jsonObject) { Long userId = jsonObject.getLong("userId"); - String groupId = jsonObject.getStr("groupIds"); // 前端传分组ID + String groupId = jsonObject.getStr("groupIds"); userService.saveUserGroup(userId, groupId); return ResultUtils.success(); @@ -104,6 +104,27 @@ public class UserController { return ResultUtils.success(); } + @PostMapping("/updateLanguage") + @Operation(summary = "更新用户语言", description = "更新当前用户的语言设置:zh/en/jp/kor等") + public Result updateLanguage(@RequestBody JSONObject jsonObject) { + String language = jsonObject.getStr("language"); + if (StrUtil.isBlank(language)) { + return ResultUtils.error(ResultCode.XSS_PARAM_ERROR, "语言不能为空"); + } + + UserSession session = SessionContext.getSession(); + Long userId = session.getUserId(); + + User user = userService.getById(userId); + if (user == null) { + return ResultUtils.error(ResultCode.XSS_PARAM_ERROR, "用户不存在"); + } + + user.setLanguage(language); + boolean success = userService.updateById(user); + return success ? ResultUtils.success("语言更新成功") : ResultUtils.error(ResultCode.XSS_PARAM_ERROR, "更新失败"); + } + @GetMapping("/findByName") @Operation(summary = "查找用户", description = "根据用户名或昵称查找用户") public Result> findByName(@RequestParam String name) { @@ -113,18 +134,15 @@ public class UserController { @PostMapping("/getEnableChangeCustomer") @Operation(summary = "获取可转接的客服", description = "转接客服") public Result>> getEnableChangeCustomer() { - // 获取当前客服id、转接客服id、转接用户id UserSession session = SessionContext.getSession(); Long userId = session.getUserId(); - if(ObjectUtil.isNull(userId)){ return ResultUtils.error(XSS_PARAM_ERROR); } List list = userService.getEnableChangeCustomerList(userId); - //使用Map返回id、昵称 List> result = list.stream().map(user -> { Map map = new HashMap<>(); map.put("id", user.getId()); @@ -152,7 +170,6 @@ public class UserController { return ResultUtils.error(XSS_PARAM_ERROR); } - // 获取当前用户信息 User currentUser = userService.getById(userId); if (currentUser == null) { return ResultUtils.error(XSS_PARAM_ERROR); @@ -160,7 +177,6 @@ public class UserController { Map result = new HashMap<>(); - // 获取可切换的账号ID列表(逗号分隔的字符串,如 "13,14") String switchableIdsStr = currentUser.getSwitchableAccountIds(); List> switchableUsers = new ArrayList<>(); @@ -173,7 +189,6 @@ public class UserController { if (!ids.isEmpty()) { List users = userService.listByIds(ids); - // 过滤掉被封禁的账号 users = users.stream() .filter(u -> !Boolean.TRUE.equals(u.getIsBanned())) .collect(Collectors.toList()); @@ -192,27 +207,21 @@ public class UserController { result.put("switchableUsers", switchableUsers); - // 获取当前用户的 unique_token String currentUserUniqueToken = currentUser.getUniqueToken(); - // 构建查询条件 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .eq(User::getIsCustomer, 2) .ne(User::getId, userId) .eq(User::getIsBanned, 0); - // 添加 unique_token 条件 if (StrUtil.isNotBlank(currentUserUniqueToken)) { - // 当前用户有 unique_token,只查询相同 unique_token 的客服 queryWrapper.eq(User::getUniqueToken, currentUserUniqueToken); } else { - // 当前用户没有 unique_token,只查询也没有 unique_token 的客服 queryWrapper.isNull(User::getUniqueToken).or().eq(User::getUniqueToken, ""); } List availableUsers = userService.list(queryWrapper); - // 获取已添加的ID集合 Set existingIds = new HashSet<>(); if (StrUtil.isNotBlank(switchableIdsStr)) { Arrays.stream(switchableIdsStr.split(",")) @@ -221,14 +230,12 @@ public class UserController { .forEach(existingIds::add); } - // 标记是否已添加 List> availableUsersList = availableUsers.stream().map(user -> { Map map = new HashMap<>(); map.put("id", user.getId()); map.put("userName", user.getUserName()); map.put("nickName", user.getNickName()); map.put("headImage", user.getHeadImage()); -// map.put("headImageThumb", user.getHeadImageThumb()); map.put("isAdded", existingIds.contains(user.getId())); return map; }).collect(Collectors.toList()); @@ -244,7 +251,6 @@ public class UserController { UserSession session = SessionContext.getSession(); - // 获取当前客服id、转接客服id、转接用户id Long customerId = session.getUserId(); Long targetId = jsonObject.getLong("targetId"); Long userId = jsonObject.getLong("userId"); @@ -267,22 +273,18 @@ public class UserController { Long targetUserId = jsonObject.getLong("targetUserId"); Integer terminal = jsonObject.getInt("terminal"); - // 获取当前登录用户 UserSession currentSession = SessionContext.getSession(); User currentUser = userService.getById(currentSession.getUserId()); - // 权限校验:只有客服才能切换账号 if (currentUser.getIsCustomer() != 2) { return ResultUtils.error(XSS_PARAM_ERROR, "无权限切换账号"); } - // 获取目标用户信息 User targetUser = userService.getById(targetUserId); if (ObjectUtil.isNull(targetUser)) { return ResultUtils.error(ResultCode.XSS_PARAM_ERROR, "目标用户不存在"); } - // 生成新的token UserSession newSession = BeanUtils.copyProperties(targetUser, UserSession.class); newSession.setUserId(targetUser.getId()); newSession.setTerminal(terminal); @@ -299,10 +301,41 @@ public class UserController { vo.setRefreshTokenExpiresIn(jwtProperties.getRefreshTokenExpireIn()); vo.setUser(targetUser); -// log.info("账号切换:从用户 {} 切换到用户 {}", currentSession.getUserId(), targetUserId); - return ResultUtils.success(vo); } -} + @PostMapping("/deleteSwitchAccount") + @Operation(summary = "删除可切换账号", description = "从当前客服的可切换列表中移除指定账号") + public Result deleteSwitchAccount(@RequestBody JSONObject jsonObject) { + Long targetUserId = jsonObject.getLong("targetUserId"); + if (ObjectUtil.isNull(targetUserId)) { + return ResultUtils.error(XSS_PARAM_ERROR, "参数错误"); + } + + UserSession session = SessionContext.getSession(); + Long currentUserId = session.getUserId(); + User currentUser = userService.getById(currentUserId); + if (currentUser == null) { + return ResultUtils.error(XSS_PARAM_ERROR); + } + + String switchableIdsStr = currentUser.getSwitchableAccountIds(); + if (StrUtil.isBlank(switchableIdsStr)) { + return ResultUtils.success("移除成功"); + } + + List idList = Arrays.stream(switchableIdsStr.split(",")) + .filter(StrUtil::isNotBlank) + .map(Long::parseLong) + .filter(id -> !id.equals(targetUserId)) + .collect(Collectors.toList()); + + String newIds = idList.stream().map(String::valueOf).collect(Collectors.joining(",")); + + currentUser.setSwitchableAccountIds(newIds); + userService.updateById(currentUser); + + return ResultUtils.success("移除成功"); + } +} \ No newline at end of file diff --git a/im-platform/src/main/java/com/bx/implatform/entity/User.java b/im-platform/src/main/java/com/bx/implatform/entity/User.java index 6825817..b3f4ece 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/User.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/User.java @@ -134,6 +134,8 @@ public class User { */ private String switchableAccountIds; - - + /** + * 语言 + */ + private String language; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java index 205b338..29e0453 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java @@ -78,6 +78,8 @@ public class UserVO { // 修改为完整的UserGroup列表 private List labelList; // 完整的分组信息列表 - private String platformName; // 新增:平台名称 + private String platformName; + + private String language; } \ No newline at end of file diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index f6cfd3b..3443174 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -99,26 +99,37 @@ export default { }); }, // 处理客服变更 + // 处理客服变更【终极修复版】 handleCustomerChanged(msgInfo) { - console.log('客服已变更,刷新聊天记录:', msgInfo); - - // 刷新好友列表(会获取最新的客服信息) + console.log('【客服转接】后台下发变更通知', msgInfo); + // msgInfo 里自带:oldKfId 旧客服id、newKfId 新客服id + const oldKfId = msgInfo.oldKfId; + const newKfId = msgInfo.newKfId; + + // ========== 强制合并:旧客服所有消息 -> 新客服 ========== + if (oldKfId && newKfId && oldKfId != newKfId) { + const oldChat = this.chatStore.findChatByFriend(oldKfId); + const newChat = this.chatStore.findChatByFriend(newKfId); + + if (oldChat && oldChat.messages.length > 0) { + console.log('【开始强制合并】旧客服', oldKfId, '→ 新客服', newKfId); + // 执行合并 + this.chatStore.mergeOldCustomerToNew(oldKfId, newKfId); + } + } + + // 原有刷新逻辑保留 this.friendStore.loadFriend().then(() => { - // 刷新聊天列表 this.chatStore.refreshChats(); - - // 如果当前正在聊天页面,重新加载消息 + const pages = getCurrentPages(); const currentPage = pages[pages.length - 1]; - if (currentPage.route === 'pages/chat/chat-box') { - // 重新加载当前会话的消息 const targetId = currentPage.options.targetId; this.reloadChatMessages(targetId); } }); }, - // 重新加载聊天消息 reloadChatMessages(targetId) { // 找到对应的会话 @@ -201,77 +212,76 @@ export default { }) }, handlePrivateMessage(msg) { - // 标记这条消息是不是自己发的 - msg.selfSend = msg.sendId == this.userStore.userInfo.id; - // 好友id - let friendId = msg.selfSend ? msg.recvId : msg.sendId; - // 检查是否为未知用户(只检查收到的消息) - let existingFriend = this.friendStore.findFriend(friendId); - if (!existingFriend && !msg.selfSend) { - console.log("收到未知用户消息,刷新应用:", friendId); - - // 重新加载数据 - this.loadStore().then(() => { - // 刷新整个应用 - // #ifdef H5 - window.location.reload(); - // #endif - - // #ifdef APP-PLUS - plus.runtime.restart(); - // #endif - - // #ifdef MP-WEIXIN - // 小程序重新启动 - uni.reLaunch({ - url: '/pages/chat/chat' - }); - // #endif - }); - - return; - } - // 会话信息 - let chatInfo = { - type: 'PRIVATE', - targetId: friendId - } - // 消息已读处理,清空已读数量 - if (msg.type == enums.MESSAGE_TYPE.READED) { - this.chatStore.resetUnreadCount(chatInfo); - return; - } - // 消息回执处理,改消息状态为已读 - if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { - this.chatStore.readedMessage({ - friendId: msg.sendId - }) - return; - } - // 消息撤回 - if (msg.type == enums.MESSAGE_TYPE.RECALL) { - this.chatStore.recallMessage(msg, chatInfo); - return; - } - // 新增好友 - if (msg.type == enums.MESSAGE_TYPE.FRIEND_NEW) { - this.friendStore.addFriend(JSON.parse(msg.content)); - return; - } - // 删除好友 - if (msg.type == enums.MESSAGE_TYPE.FRIEND_DEL) { - this.friendStore.removeFriend(friendId); - return; - } - // 对好友设置免打扰 - if (msg.type == enums.MESSAGE_TYPE.FRIEND_DND) { - this.friendStore.setDnd(friendId, JSON.parse(msg.content)); - this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); - return; - } - // 消息插入 - let friend = this.loadFriendInfo(friendId); - this.insertPrivateMessage(friend, msg); + // 标记这条消息是不是自己发的 + msg.selfSend = msg.sendId == this.userStore.userInfo.id; + // 好友id + let friendId = msg.selfSend ? msg.recvId : msg.sendId; + + + // 检查是否为未知用户(只检查收到的消息) + let existingFriend = this.friendStore.findFriend(friendId); + if (!existingFriend && !msg.selfSend) { + console.log("收到未知用户消息,刷新应用:", friendId); + + this.loadStore().then(() => { + // #ifdef H5 + window.location.reload(); + // #endif + + // #ifdef APP-PLUS + plus.runtime.restart(); + // #endif + + // #ifdef MP-WEIXIN + uni.reLaunch({ + url: '/pages/chat/chat' + }); + // #endif + }); + + return; + } + // 会话信息 + let chatInfo = { + type: 'PRIVATE', + targetId: friendId + } + // 消息已读处理,清空已读数量 + if (msg.type == enums.MESSAGE_TYPE.READED) { + this.chatStore.resetUnreadCount(chatInfo); + return; + } + // 消息回执处理,改消息状态为已读 + if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { + this.chatStore.readedMessage({ + friendId: msg.sendId + }) + return; + } + // 消息撤回 + if (msg.type == enums.MESSAGE_TYPE.RECALL) { + this.chatStore.recallMessage(msg, chatInfo); + return; + } + // 新增好友 + if (msg.type == enums.MESSAGE_TYPE.FRIEND_NEW) { + this.friendStore.addFriend(JSON.parse(msg.content)); + return; + } + // 删除好友 + if (msg.type == enums.MESSAGE_TYPE.FRIEND_DEL) { + this.friendStore.removeFriend(friendId); + return; + } + // 对好友设置免打扰 + if (msg.type == enums.MESSAGE_TYPE.FRIEND_DND) { + this.friendStore.setDnd(friendId, JSON.parse(msg.content)); + this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); + return; + } + // 插入消息 + let friend = this.loadFriendInfo(friendId); + this.insertPrivateMessage(friend, msg); }, insertPrivateMessage(friend, msg) { // 单人视频信令 diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue index 2de99fe..0e348ba 100644 --- a/im-uniapp/pages/chat/chat-box.vue +++ b/im-uniapp/pages/chat/chat-box.vue @@ -268,8 +268,8 @@ export default { isFileOpen: false, isPasting: false, hasPasteListener: false, - currentLang: 'zh', showLangModal: false, // 新增 + currentLang: uni.getStorageSync("app_language") || "zh", langList: [ { label: "中文", value: "zh" }, { label: "English", value: "en" }, @@ -285,17 +285,25 @@ export default { }; }, methods: { - // 下拉选择语言(必刷新) selectLang(lang) { this.currentLang = lang; - this.$i18n.locale = lang; - uni.setStorageSync("app_language", lang); this.showLangDrop = false; - setTimeout(() => { - window.location.reload(); // H5 真正强制刷新 - }, 100); + // 保存到后端 + this.$http({ + url: "/user/updateLanguage", + method: "POST", + data: { language: lang } + }).then(() => { + console.log("语言保存成功"); + }).finally(() => { + this.$i18n.locale = lang; + uni.setStorageSync("app_language", lang); + setTimeout(() => { + window.location.reload(); + }, 100); + }); }, loadCommonQuestions(userId) { this.$http({ @@ -1257,13 +1265,18 @@ export default { this.chatStore.updateChatFromUser(this.userInfo); } }, - loadFriend(friendId) { - this.$http({ - url: `/user/find/${friendId}`, - method: "GET", - }).then((userInfo) => { - this.userInfo = userInfo; - this.updateFriendInfo(); + async loadFriend(friendId) { + return new Promise((resolve) => { + this.$http({ + url: `/user/find/${friendId}`, + method: "GET", + }).then((userInfo) => { + this.userInfo = userInfo; + this.updateFriendInfo(); + resolve(); // 让外部可以 then + }).catch(() => { + resolve(); + }); }); }, rpxTopx(rpx) { @@ -1592,6 +1605,7 @@ export default { }, }, async onLoad(options) { + try { this.currentLang = uni.getStorageSync("app_language") || "zh"; uni.showLoading({ title: this.$t('common.loading'), mask: true }); @@ -1647,7 +1661,9 @@ export default { } this.readedMessage(); - this.loadFriend(targetId); + // this.loadFriend(targetId); + await this.loadFriend(targetId); + this.loadReaded(targetId); this.loadCommonQuestions(targetId); @@ -1658,6 +1674,7 @@ export default { await this.getSetting(); this.$nextTick(() => this.scrollToBottom()); + } catch (err) { console.error("错误:", err); } finally { @@ -1678,11 +1695,10 @@ export default {