From efac214d69361118699cbf3c5820837b08e27e8b Mon Sep 17 00:00:00 2001 From: "[yxf]" <[1524240689@qq.com]> Date: Mon, 20 Apr 2026 16:43:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=87=E6=8D=A2=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=8A=9F=E8=83=BD=E7=B1=BB=E4=BC=BC=E9=A3=9E=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PrivateMessageController.java | 19 + .../implatform/controller/UserController.java | 111 +- .../java/com/bx/implatform/dto/LoginDTO.java | 3 + .../java/com/bx/implatform/entity/User.java | 7 + .../service/PrivateMessageService.java | 9 + .../bx/implatform/service/UserService.java | 9 + .../impl/PrivateMessageServiceImpl.java | 37 +- .../service/impl/UserServiceImpl.java | 76 + .../java/com/bx/implatform/vo/LoginVO.java | 3 + im-web/src/components/setting/Setting.vue | 2 +- im-web/src/view/Home.vue | 1337 +++++++++-------- 11 files changed, 1015 insertions(+), 598 deletions(-) diff --git a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java index cf3c0fd..0090053 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java @@ -12,7 +12,9 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Tag(name = "私聊消息") @RestController @@ -48,6 +50,7 @@ public class PrivateMessageController { return ResultUtils.success(); } + @GetMapping("/maxReadedId") @Operation(summary = "获取最大已读消息的id", description = "获取某个会话中已读消息的最大id") public Result getMaxReadedId(@RequestParam Long friendId) { @@ -63,5 +66,21 @@ public class PrivateMessageController { return ResultUtils.success(privateMessageService.findHistoryMessage(friendId, page, size)); } + /** + * 批量获取指定用户的未读消息数 + */ + @PostMapping("/unreadCounts") + @Operation(summary = "批量获取指定用户的未读消息数", description = "批量获取多个指定用户的未读消息数") + public Result> getUnreadCounts(@RequestBody Map> params) { + List userIds = params.get("userIds"); + + if (userIds == null || userIds.isEmpty()) { + return ResultUtils.success(new HashMap<>()); + } + + Map result = privateMessageService.getUnreadCountsByUserIds(userIds); + return ResultUtils.success(result); + } + } 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 6ec4583..e6827c2 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 @@ -1,7 +1,10 @@ package com.bx.implatform.controller; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bx.implatform.dto.LoginDTO; import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.entity.User; import com.bx.implatform.result.Result; @@ -24,9 +27,8 @@ import com.alibaba.fastjson.JSON; import com.bx.imcommon.util.JwtUtil; import com.bx.implatform.config.props.JwtProperties; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; import static com.bx.implatform.enums.ResultCode.XSS_PARAM_ERROR; @@ -133,6 +135,109 @@ public class UserController { return ResultUtils.success(result); } + @PostMapping("/addAccounts") + @Operation(summary = "客服登录", description = "客服登录") + public Result addAccounts(@Valid @RequestBody LoginDTO dto) { + LoginVO vo = userService.addAccounts(dto); + return ResultUtils.success(vo); + } + + @PostMapping("/getSwitchableAccounts") + @Operation(summary = "获取可切换的账号列表", description = "获取当前客服可切换的账号列表") + public Result> getSwitchableAccounts() { + UserSession session = SessionContext.getSession(); + Long userId = session.getUserId(); + + if (ObjectUtil.isNull(userId)) { + return ResultUtils.error(XSS_PARAM_ERROR); + } + + // 获取当前用户信息 + User currentUser = userService.getById(userId); + if (currentUser == null) { + return ResultUtils.error(XSS_PARAM_ERROR); + } + + Map result = new HashMap<>(); + + // 获取可切换的账号ID列表(逗号分隔的字符串,如 "13,14") + String switchableIdsStr = currentUser.getSwitchableAccountIds(); + List> switchableUsers = new ArrayList<>(); + + if (StrUtil.isNotBlank(switchableIdsStr)) { + String[] idArray = switchableIdsStr.split(","); + List ids = Arrays.stream(idArray) + .filter(StrUtil::isNotBlank) + .map(Long::parseLong) + .collect(Collectors.toList()); + + if (!ids.isEmpty()) { + List users = userService.listByIds(ids); + // 过滤掉被封禁的账号 + users = users.stream() + .filter(u -> !Boolean.TRUE.equals(u.getIsBanned())) + .collect(Collectors.toList()); + + switchableUsers = users.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()); + return map; + }).collect(Collectors.toList()); + } + } + + 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(",")) + .filter(StrUtil::isNotBlank) + .map(Long::parseLong) + .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()); + + result.put("availableUsers", availableUsersList); + + return ResultUtils.success(result); + } + @PostMapping("/changeCustomer") @Operation(summary = "转接客服", description = "转接客服") public Result register(@RequestBody JSONObject jsonObject) { diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java index 26262f5..992e60d 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java @@ -37,4 +37,7 @@ public class LoginDTO { @Schema(description = "客服ID") private String keFuId; + @Schema(description = "可切换客服账号") + private String switchableAccountIds; + } 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 7ea084a..6825817 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 @@ -129,4 +129,11 @@ public class User { */ private String welcomeMsg; + /** + * 可切换的账号IDs列表 + */ + private String switchableAccountIds; + + + } diff --git a/im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java b/im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java index d6b0d10..9a2093b 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java @@ -6,6 +6,7 @@ import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.vo.PrivateMessageVO; import java.util.List; +import java.util.Map; public interface PrivateMessageService extends IService { @@ -63,4 +64,12 @@ public interface PrivateMessageService extends IService { * @param customerId 客服id */ void changeMessageRecord(Long customerId,Long targetId, Long userId); + + /** + * 批量获取指定用户的未读消息数 + * @param userIds 要查询的用户ID列表 + * @return key: 用户ID, value: 该用户的未读消息数 + */ + Map getUnreadCountsByUserIds(List userIds); + } diff --git a/im-platform/src/main/java/com/bx/implatform/service/UserService.java b/im-platform/src/main/java/com/bx/implatform/service/UserService.java index b9f60bc..abc4a5a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/UserService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/UserService.java @@ -29,6 +29,15 @@ public interface UserService extends IService { */ LoginVO loginCustom(LoginDTO dto); + /** + * 客服登录 + * + * @param dto 登录dto + * @return 登录token + */ + LoginVO addAccounts(LoginDTO dto); + + /** * 修改用户密码 * diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java index 0dec307..a28358e 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java @@ -32,10 +32,7 @@ import org.apache.commons.lang3.time.DateUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.stream.Collectors; @@ -242,6 +239,36 @@ public class PrivateMessageServiceImpl extends ServiceImpl getUnreadCountsByUserIds(List userIds) { + if (userIds == null || userIds.isEmpty()) { + return new HashMap<>(); + } + + Map result = new HashMap<>(); + + // 初始化所有用户为0 + for (Long userId : userIds) { + result.put(userId, 0); + } + + // 批量查询未读消息(status = 0 表示未读) + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.in(PrivateMessage::getRecvId, userIds) + .eq(PrivateMessage::getStatus, 0) // 0 表示未读 + .select(PrivateMessage::getRecvId); + + List messages = this.list(wrapper); + + // 统计每个用户的未读消息数 + for (PrivateMessage msg : messages) { + Long recvId = msg.getRecvId(); + result.put(recvId, result.getOrDefault(recvId, 0) + 1); + } + + return result; + } + @Transactional(rollbackFor = Exception.class) @Override public void readedMessage(Long friendId) { @@ -307,7 +334,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl list = new ArrayList<>(); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 1faf1a3..08ba574 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -225,6 +225,82 @@ public class UserServiceImpl extends ServiceImpl implements Us return randomCustomerId; } + @Override + @Transactional(rollbackFor = Exception.class) + public LoginVO addAccounts(LoginDTO dto) { + // 获取当前登录用户 + UserSession session = SessionContext.getSession(); + Long currentUserId = session.getUserId(); + + if (ObjectUtil.isNull(currentUserId)) { + throw new GlobalException("用户未登录"); + } + + // 获取当前用户信息 + User currentUser = this.getById(currentUserId); + if (ObjectUtil.isNull(currentUser)) { + throw new GlobalException("当前用户不存在"); + } + + // 验证要添加的账号 + User targetUser = this.findUserByUserName(dto.getUserName()); + if (Objects.isNull(targetUser)) { + throw new GlobalException("用户不存在"); + } + if (targetUser.getIsCustomer() == 1) { + throw new GlobalException("只能添加客服账号"); + } + if (targetUser.getIsBanned()) { + String tip = String.format("该账号因'%s'已被管理员封禁", targetUser.getReason()); + throw new GlobalException(tip); + } + if (!passwordEncoder.matches(dto.getPassword(), targetUser.getPassword())) { + throw new GlobalException(ResultCode.PASSWOR_ERROR); + } + + // 未设置套餐或套餐过期 + if (imAgentService.isPackageExpire(targetUser.getUniqueToken())) { + throw new GlobalException("套餐已过期"); + } + + // 验证是否属于同一个代理(unique_token 相同) + if (!Objects.equals(currentUser.getUniqueToken(), targetUser.getUniqueToken())) { + throw new GlobalException("只能添加同属一个代理的客服账号"); + } + + // 不能添加自己 + if (currentUserId.equals(targetUser.getId())) { + throw new GlobalException("不能添加当前登录的账号"); + } + + // 获取当前用户的可切换账号ID列表 + String switchableIdsStr = currentUser.getSwitchableAccountIds(); + Set idSet = new HashSet<>(); + + if (StrUtil.isNotBlank(switchableIdsStr)) { + idSet.addAll(Arrays.asList(switchableIdsStr.split(","))); + } + + // 添加新ID + String newId = String.valueOf(targetUser.getId()); + if (idSet.contains(newId)) { + throw new GlobalException("该账号已在切换列表中"); + } + + idSet.add(newId); + + // 更新当前用户的 switchable_account_ids + String newIdsStr = String.join(",", idSet); + currentUser.setSwitchableAccountIds(newIdsStr); + this.updateById(currentUser); + + log.info("用户 {} 添加了可切换账号 {},当前可切换账号列表: {}", + currentUserId, targetUser.getId(), newIdsStr); + + LoginVO vo = new LoginVO(); + return vo; + } + @Override public LoginVO loginCustom(LoginDTO dto) { User user = this.findUserByUserName(dto.getUserName()); diff --git a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java index b662700..42a3403 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java @@ -26,4 +26,7 @@ public class LoginVO { @Schema(description = "当前登录用户信息") private User user; + @Schema(description = "可切换客服账号") + private String switchableAccountIds; + } diff --git a/im-web/src/components/setting/Setting.vue b/im-web/src/components/setting/Setting.vue index e380269..42fa394 100644 --- a/im-web/src/components/setting/Setting.vue +++ b/im-web/src/components/setting/Setting.vue @@ -30,7 +30,7 @@ - 切换账号 + 取 消 确 定 diff --git a/im-web/src/view/Home.vue b/im-web/src/view/Home.vue index d7d5358..a7f447a 100644 --- a/im-web/src/view/Home.vue +++ b/im-web/src/view/Home.vue @@ -1,57 +1,88 @@ \ No newline at end of file