diff --git a/hula-im-service/src/main/java/com/hula/core/chat/controller/ContactController.java b/hula-im-service/src/main/java/com/hula/core/chat/controller/ContactController.java index dd75797d..72001099 100644 --- a/hula-im-service/src/main/java/com/hula/core/chat/controller/ContactController.java +++ b/hula-im-service/src/main/java/com/hula/core/chat/controller/ContactController.java @@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + /** * 会话相关接口 * @author nyh @@ -42,6 +44,12 @@ public class ContactController { return ApiResult.success(roomService.getContactPage(request, uid)); } + @GetMapping("/contact/list") + @Operation(summary ="会话列表") + public ApiResult> getRoomList() { + return ApiResult.success(roomService.getContactPage(RequestHolder.get().getUid())); + } + @GetMapping("/contact/detail") @Operation(summary ="会话详情") public ApiResult getContactDetail(@Valid IdReqVO request) { diff --git a/hula-im-service/src/main/java/com/hula/core/chat/dao/ContactDao.java b/hula-im-service/src/main/java/com/hula/core/chat/dao/ContactDao.java index 64be631f..a438c216 100644 --- a/hula-im-service/src/main/java/com/hula/core/chat/dao/ContactDao.java +++ b/hula-im-service/src/main/java/com/hula/core/chat/dao/ContactDao.java @@ -82,7 +82,16 @@ public class ContactDao extends ServiceImpl { return CursorUtils.getCursorPageByMysql(this, request, wrapper -> wrapper.eq(Contact::getUid, uid).eq(Contact::getHide, false), Contact::getActiveTime); } - public List getByRoomIds(List roomIds, Long uid) { + /** + * 查询用户的所有会话 + * @param uid + * @return + */ + public List getAllContactsByUid(Long uid) { + return lambdaQuery().eq(Contact::getUid, uid).list(); + } + + public List getAllContactsByUid(List roomIds, Long uid) { return lambdaQuery() .in(Contact::getRoomId, roomIds) .eq(Contact::getHide, false) diff --git a/hula-im-service/src/main/java/com/hula/core/chat/service/RoomAppService.java b/hula-im-service/src/main/java/com/hula/core/chat/service/RoomAppService.java index c0b98889..75a380ce 100644 --- a/hula-im-service/src/main/java/com/hula/core/chat/service/RoomAppService.java +++ b/hula-im-service/src/main/java/com/hula/core/chat/service/RoomAppService.java @@ -43,6 +43,11 @@ public interface RoomAppService { */ CursorPageBaseResp getContactPage(CursorPageBaseReq request, Long uid); + /** + * 获取会话列表 + */ + List getContactPage(Long uid); + /** * 获取群组信息 */ diff --git a/hula-im-service/src/main/java/com/hula/core/chat/service/impl/RoomAppServiceImpl.java b/hula-im-service/src/main/java/com/hula/core/chat/service/impl/RoomAppServiceImpl.java index 3861a029..9096afc2 100644 --- a/hula-im-service/src/main/java/com/hula/core/chat/service/impl/RoomAppServiceImpl.java +++ b/hula-im-service/src/main/java/com/hula/core/chat/service/impl/RoomAppServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.hula.ai.utils.BeanUtils; import com.hula.common.annotation.RedissonLock; import com.hula.common.domain.vo.req.CursorPageBaseReq; import com.hula.common.domain.vo.res.CursorPageBaseResp; @@ -49,7 +48,6 @@ import com.hula.core.chat.service.strategy.msg.MsgHandlerFactory; import com.hula.core.user.dao.UserBackpackDao; import com.hula.core.user.dao.UserDao; import com.hula.core.user.domain.entity.User; -import com.hula.core.user.domain.entity.UserBackpack; import com.hula.core.user.domain.enums.RoleTypeEnum; import com.hula.core.user.domain.enums.WsBaseResp; import com.hula.core.user.domain.vo.req.MergeMessageReq; @@ -106,38 +104,69 @@ public class RoomAppServiceImpl implements RoomAppService { @Override public CursorPageBaseResp getContactPage(CursorPageBaseReq request, Long uid) { // 查出用户要展示的会话列表 - CursorPageBaseResp page; - HashMap contactHashMap = new HashMap<>(); - if (Objects.nonNull(uid)) { - Double hotEnd = getCursorOrNull(request.getCursor()); - Double hotStart = null; - // 用户基础会话 - CursorPageBaseResp contactPage = contactDao.getContactPage(uid, request); - contactHashMap = contactPage.getList().stream().collect(Collectors.toMap( - contact -> StrUtil.format("{}_{}", contact.getUid(), contact.getRoomId()), - contact -> contact, (existing, replacement) -> existing, HashMap::new - )); - List baseRoomIds = contactPage.getList().stream().map(Contact::getRoomId).collect(Collectors.toList()); - if (!contactPage.getIsLast()) { - hotStart = getCursorOrNull(contactPage.getCursor()); - } - // 热门房间 - Set> typedTuples = hotRoomCache.getRoomRange(hotStart, hotEnd); - List hotRoomIds = typedTuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList()); - baseRoomIds.addAll(hotRoomIds); - // 基础会话和热门房间合并 - page = CursorPageBaseResp.init(contactPage, baseRoomIds, 0L); - } else {// 用户未登录,只查全局房间 - CursorPageBaseResp> roomCursorPage = hotRoomCache.getRoomCursorPage(request); - List roomIds = roomCursorPage.getList().stream().map(Pair::getKey).collect(Collectors.toList()); - page = CursorPageBaseResp.init(roomCursorPage, roomIds,0L); - } + Double hotEnd = getCursorOrNull(request.getCursor()); + Double hotStart = null; + // 用户基础会话 + CursorPageBaseResp contactPage = contactDao.getContactPage(uid, request); + HashMap contactHashMap = contactPage.getList().stream().collect(Collectors.toMap( + contact -> StrUtil.format("{}_{}", contact.getUid(), contact.getRoomId()), + contact -> contact, (existing, replacement) -> existing, HashMap::new + )); + List baseRoomIds = contactPage.getList().stream().map(Contact::getRoomId).collect(Collectors.toList()); + if (!contactPage.getIsLast()) { + hotStart = getCursorOrNull(contactPage.getCursor()); + } + // 热门房间 + Set> typedTuples = hotRoomCache.getRoomRange(hotStart, hotEnd); + List hotRoomIds = typedTuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList()); + baseRoomIds.addAll(hotRoomIds); + // 基础会话和热门房间合并 + CursorPageBaseResp page = CursorPageBaseResp.init(contactPage, baseRoomIds, 0L); + // 最后组装会话信息(名称,头像,未读数等) List result = buildContactResp(contactHashMap, uid, page.getList()); return CursorPageBaseResp.init(page, result,0L); } - @Override + /** + * 返回当前登录用户的全部会话 + * @param uid + * @return + */ + @Override + public List getContactPage(Long uid) { + // 1. 查出用户要展示的会话列表 + List contacts = contactDao.getAllContactsByUid(uid); + + // 2. 构建会话映射表(uid_roomId -> Contact) + HashMap contactMap = contacts.stream() + .collect(Collectors.toMap( + contact -> StrUtil.format("{}_{}", contact.getUid(), contact.getRoomId()), + contact -> contact, + (existing, replacement) -> existing, + HashMap::new + )); + + // 3. 提取所有基础会话的 roomId + List baseRoomIds = contacts.stream().map(Contact::getRoomId).collect(Collectors.toList()); + + // 4. 查询所有热门房间 +// Set> hotRooms = hotRoomCache.getAllRooms(); +// List hotRoomIds = hotRooms.stream() +// .map(ZSetOperations.TypedTuple::getValue) +// .filter(Objects::nonNull) +// .map(Long::parseLong) +// .collect(Collectors.toList()); + + // 5. 合并基础会话和热门房间的 roomId + baseRoomIds.addAll(baseRoomIds); +// baseRoomIds.addAll(hotRoomIds); + + // 6. 组装最终会话信息(名称、头像、未读数等) + return buildContactResp(contactMap, uid, baseRoomIds); + } + + @Override public ChatRoomResp getContactDetail(Long uid, Long roomId) { Room room = roomCache.get(roomId); AssertUtil.isNotEmpty(room, "房间号有误"); @@ -672,7 +701,7 @@ public class RoomAppServiceImpl implements RoomAppService { Map batch = roomCache.getBatch(roomIds); Map contactMap = new HashMap<>(); if (Objects.nonNull(uid)) { - List byRoomIds = contactDao.getByRoomIds(roomIds, uid); + List byRoomIds = contactDao.getAllContactsByUid(roomIds, uid); contactMap = byRoomIds.stream().collect(Collectors.toMap(Contact::getRoomId, Function.identity())); } Map finalContactMap = contactMap; @@ -773,7 +802,7 @@ public class RoomAppServiceImpl implements RoomAppService { if (Objects.isNull(uid)) { return new HashMap<>(); } - List contacts = contactDao.getByRoomIds(roomIds, uid); + List contacts = contactDao.getAllContactsByUid(roomIds, uid); return contacts.parallelStream() .map(contact -> Pair.of(contact.getRoomId(), messageDao.getUnReadCount(contact.getRoomId(), contact.getReadTime()))) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); @@ -804,40 +833,53 @@ public class RoomAppServiceImpl implements RoomAppService { // 获取好友信息 List friendRoomId = groupRoomIdMap.get(RoomTypeEnum.FRIEND.getType()); Map friendRoomMap = getFriendRoomMap(friendRoomId, uid); + // 处理脏数据 + Set dirtyRoomIds = new HashSet<>(); - return roomMap.values().stream().filter(Objects::nonNull).map(room -> { - RoomBaseInfo roomBaseInfo = new RoomBaseInfo(); - roomBaseInfo.setRoomId(room.getId()); - roomBaseInfo.setType(room.getType()); - roomBaseInfo.setHotFlag(room.getHotFlag()); - roomBaseInfo.setLastMsgId(room.getLastMsgId()); - roomBaseInfo.setActiveTime(room.getActiveTime()); - if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.GROUP) { - RoomGroup roomGroup = roomInfoBatch.get(room.getId()); + Map collect = roomMap.values().stream().filter(Objects::nonNull).map(room -> { + RoomBaseInfo roomBaseInfo = new RoomBaseInfo(); + roomBaseInfo.setRoomId(room.getId()); + roomBaseInfo.setType(room.getType()); + roomBaseInfo.setHotFlag(room.getHotFlag()); + roomBaseInfo.setLastMsgId(room.getLastMsgId()); + roomBaseInfo.setActiveTime(room.getActiveTime()); + if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.GROUP) { + RoomGroup roomGroup = roomInfoBatch.get(room.getId()); roomBaseInfo.setId(roomGroup.getId()); - roomBaseInfo.setName(roomGroup.getName()); - roomBaseInfo.setAvatar(roomGroup.getAvatar()); + roomBaseInfo.setName(roomGroup.getName()); + roomBaseInfo.setAvatar(roomGroup.getAvatar()); roomBaseInfo.setAccount(roomGroup.getAccount()); - GroupMember member = groupMemberCache.getMemberDetail(roomBaseInfo.getId(), uid); + GroupMember member = null; + try { + member = groupMemberCache.getMemberDetail(roomBaseInfo.getId(), uid); + } catch (Exception e) { + dirtyRoomIds.add(room.getId()); // 记录脏数据 + } // todo 稳定了这里可以不用判空,理论上100% 在群里 - if(ObjectUtil.isNotNull(member)){ + if (ObjectUtil.isNotNull(member)) { roomBaseInfo.setMyName(member.getMyName()); roomBaseInfo.setRemark(member.getRemark()); roomBaseInfo.setRole(member.getRole()); - }else { + } else { roomBaseInfo.setMyName("会话异常"); roomBaseInfo.setRemark("会话异常"); roomBaseInfo.setRole(0); } - } else if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.FRIEND) { - User user = friendRoomMap.get(room.getId()); + } else if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.FRIEND) { + User user = friendRoomMap.get(room.getId()); roomBaseInfo.setId(user.getId()); roomBaseInfo.setRole(0); - roomBaseInfo.setName(user.getName()); - roomBaseInfo.setAvatar(user.getAvatar()); + roomBaseInfo.setName(user.getName()); + roomBaseInfo.setAvatar(user.getAvatar()); roomBaseInfo.setAccount(user.getAccount()); - } - return roomBaseInfo; - }).collect(Collectors.toMap(RoomBaseInfo::getRoomId, Function.identity())); - } + } + return roomBaseInfo; + }).collect(Collectors.toMap(RoomBaseInfo::getRoomId, Function.identity())); + + // 统一打印脏数据 + if (!dirtyRoomIds.isEmpty()) { + System.out.println(dirtyRoomIds); + } + return collect; + } } diff --git a/hula-im-service/src/main/resources/application-dev.yml b/hula-im-service/src/main/resources/application-dev.yml index 2d16789b..f8183bfe 100644 --- a/hula-im-service/src/main/resources/application-dev.yml +++ b/hula-im-service/src/main/resources/application-dev.yml @@ -1,55 +1,4 @@ version: 2.6.13 - -spring: - autoconfigure: - exclude: - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 - main: - allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 - ai: - vectorstore: # 向量存储 - redis: - initialize-schema: true - index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 - prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构 - qdrant: - initialize-schema: true - collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行 - host: 127.0.0.1 - port: 6334 - milvus: - initialize-schema: true - database-name: default # Milvus 中数据库的名称 - collection-name: knowledge_segment # Milvus 中集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行 - client: - host: 127.0.0.1 - port: 19530 - qianfan: # 文心一言 - api-key: x0cuLZ7XsaTCU08vuJWO87Lg - secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK - zhipuai: # 智谱 AI - api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs - openai: # OpenAI 官方 - api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17 - base-url: https://api.gptsapi.net - azure: # OpenAI 微软 - openai: - endpoint: https://eastusprejade.openai.azure.com - api-key: xxx - ollama: - base-url: http://127.0.0.1:11434 - chat: - model: llama3 - stabilityai: - api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx - dashscope: # 通义千问 - api-key: sk-71800982914041848008480000000000 - minimax: # Minimax:https://www.minimaxi.com/ - api-key: xxxx - moonshot: # 月之暗灭(KIMI) - api-key: sk-abc - HuLa-IM: HOST: 127.0.0.1 #域名 MYSQL_PORT: 3306 #端口