Merge remote-tracking branch 'origin/friend' into friend

# Conflicts:
#	mallchat-common/src/main/java/com/abin/mallchat/common/user/dao/UserApplyDao.java
#	mallchat-common/src/main/java/com/abin/mallchat/common/user/dao/UserFriendDao.java
#	mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/FriendServiceImpl.java
This commit is contained in:
limeng
2023-07-23 11:27:55 +08:00
76 changed files with 1726 additions and 308 deletions

View File

@@ -1,20 +0,0 @@
package com.abin.mallchat.common.chat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 会话列表 前端控制器
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
@Controller
@RequestMapping("/contact")
public class ContactController {
}

View File

@@ -1,20 +0,0 @@
package com.abin.mallchat.common.chat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 群成员表 前端控制器
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
@Controller
@RequestMapping("/groupMember")
public class GroupMemberController {
}

View File

@@ -1,20 +0,0 @@
package com.abin.mallchat.common.chat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 房间表 前端控制器
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
@Controller
@RequestMapping("/room")
public class RoomController {
}

View File

@@ -1,8 +1,11 @@
package com.abin.mallchat.common.chat.dao;
import com.abin.mallchat.common.chat.domain.entity.Contact;
import com.abin.mallchat.common.chat.domain.entity.Message;
import com.abin.mallchat.common.chat.mapper.ContactMapper;
import com.abin.mallchat.common.chat.service.IContactService;
import com.abin.mallchat.common.common.domain.vo.request.CursorPageBaseReq;
import com.abin.mallchat.common.common.domain.vo.response.CursorPageBaseResp;
import com.abin.mallchat.common.common.utils.CursorUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@@ -15,6 +18,57 @@ import org.springframework.stereotype.Service;
* @since 2023-07-16
*/
@Service
public class ContactDao extends ServiceImpl<ContactMapper, Contact> implements IContactService {
public class ContactDao extends ServiceImpl<ContactMapper, Contact> {
public Contact get(Long uid, Long roomId) {
return lambdaQuery()
.eq(Contact::getUid, uid)
.eq(Contact::getRoomId, roomId)
.one();
}
public Integer getReadCount(Message message) {
return lambdaQuery()
.eq(Contact::getRoomId, message.getRoomId())
.ge(Contact::getReadTime, message.getCreateTime())
.count();
}
public Integer getTotalCount(Long roomId) {
return lambdaQuery()
.eq(Contact::getRoomId, roomId)
.count();
}
public Integer getUnReadCount(Message message) {
return lambdaQuery()
.eq(Contact::getRoomId, message.getRoomId())
.lt(Contact::getReadTime, message.getCreateTime())
.count();
}
public CursorPageBaseResp<Contact> getReadPage(Message message, CursorPageBaseReq cursorPageBaseReq) {
return CursorUtils.getCursorPageByMysql(this, cursorPageBaseReq, wrapper -> {
wrapper.eq(Contact::getRoomId, message.getRoomId());
wrapper.ne(Contact::getUid, message.getFromUid());//不需要查询出自己
wrapper.ge(Contact::getReadTime, message.getCreateTime());//已读时间大于等于消息发送时间
}, Contact::getReadTime);
}
public CursorPageBaseResp<Contact> getUnReadPage(Message message, CursorPageBaseReq cursorPageBaseReq) {
return CursorUtils.getCursorPageByMysql(this, cursorPageBaseReq, wrapper -> {
wrapper.eq(Contact::getRoomId, message.getRoomId());
wrapper.ne(Contact::getUid, message.getFromUid());//不需要查询出自己
wrapper.lt(Contact::getReadTime, message.getCreateTime());//已读时间小于消息发送时间
}, Contact::getReadTime);
}
/**
* 获取用户会话列表
*/
public CursorPageBaseResp<Contact> getContactPage(Long uid, CursorPageBaseReq request) {
return CursorUtils.getCursorPageByMysql(this, request, wrapper -> {
wrapper.eq(Contact::getUid, uid);
}, Contact::getActiveTime);
}
}

View File

@@ -7,7 +7,6 @@ import com.abin.mallchat.common.common.domain.vo.request.CursorPageBaseReq;
import com.abin.mallchat.common.common.domain.vo.response.CursorPageBaseResp;
import com.abin.mallchat.common.common.utils.CursorUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -20,11 +19,9 @@ import org.springframework.stereotype.Service;
*/
@Service
public class MessageDao extends ServiceImpl<MessageMapper, Message> {
@Autowired
private CursorUtils cursorUtils;
public CursorPageBaseResp<Message> getCursorPage(Long roomId, CursorPageBaseReq request) {
return cursorUtils.getCursorPageByMysql(this, request, wrapper -> {
return CursorUtils.getCursorPageByMysql(this, request, wrapper -> {
wrapper.eq(Message::getRoomId, roomId);
wrapper.eq(Message::getStatus, MessageStatusEnum.NORMAL.getStatus());
}, Message::getId);

View File

@@ -2,7 +2,7 @@ package com.abin.mallchat.common.chat.dao;
import com.abin.mallchat.common.chat.domain.entity.Room;
import com.abin.mallchat.common.chat.mapper.RoomMapper;
import com.abin.mallchat.common.chat.service.IRoomService;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@@ -15,6 +15,6 @@ import org.springframework.stereotype.Service;
* @since 2023-07-16
*/
@Service
public class RoomDao extends ServiceImpl<RoomMapper, Room> implements IRoomService {
public class RoomDao extends ServiceImpl<RoomMapper, Room> implements IService<Room> {
}

View File

@@ -0,0 +1,38 @@
package com.abin.mallchat.common.chat.dao;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import com.abin.mallchat.common.chat.mapper.RoomFriendMapper;
import com.abin.mallchat.common.common.domain.enums.NormalOrNoEnum;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 单聊房间表 服务实现类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
@Service
public class RoomFriendDao extends ServiceImpl<RoomFriendMapper, RoomFriend> {
public RoomFriend getByKey(String key) {
return lambdaQuery().eq(RoomFriend::getKey, key).one();
}
public void restoreRoom(Long id) {
lambdaUpdate()
.eq(RoomFriend::getId, id)
.set(RoomFriend::getStatus, NormalOrNoEnum.NORMAL.getStatus())
.update();
}
public List<RoomFriend> listByRoomIds(List<Long> roomIds) {
return lambdaQuery()
.eq(RoomFriend::getRoomId, roomIds)
.list();
}
}

View File

@@ -0,0 +1,26 @@
package com.abin.mallchat.common.chat.dao;
import com.abin.mallchat.common.chat.domain.entity.RoomGroup;
import com.abin.mallchat.common.chat.mapper.RoomGroupMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 群聊房间表 服务实现类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
@Service
public class RoomGroupDao extends ServiceImpl<RoomGroupMapper, RoomGroup> {
public List<RoomGroup> listByRoomIds(List<Long> roomIds) {
return lambdaQuery()
.in(RoomGroup::getRoomId, roomIds)
.list();
}
}

View File

@@ -0,0 +1,28 @@
package com.abin.mallchat.common.chat.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Description:
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-17
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MsgReadInfoDTO {
@ApiModelProperty("消息id")
private Long msgId;
@ApiModelProperty("已读数")
private Integer readCount;
@ApiModelProperty("未读数")
private Integer unReadCount;
}

View File

@@ -0,0 +1,19 @@
package com.abin.mallchat.common.chat.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* Description: 房间详情
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-22
*/
@Data
public class RoomBaseInfo {
@ApiModelProperty("房间id")
private Long roomId;
@ApiModelProperty("会话名称")
private String name;
@ApiModelProperty("会话头像")
private String avatar;
}

View File

@@ -38,22 +38,10 @@ public class Contact implements Serializable {
private Long uid;
/**
* 聊天类型 1单聊 2普通群聊
* 房间id
*/
@TableField("type")
private Integer type;
/**
* 是否全员展示 0否 1是
*/
@TableField("hot_flag")
private Integer hotFlag;
/**
* 聊天对象type=1:uidtype=2:房间id
*/
@TableField("target_id")
private Long targetId;
@TableField("room_id")
private Long roomId;
/**
* 阅读到的时间
@@ -67,6 +55,12 @@ public class Contact implements Serializable {
@TableField("active_time")
private LocalDateTime activeTime;
/**
* 最后一条消息id
*/
@TableField("last_msg_id")
private Long lastMsgId;
/**
* 创建时间
*/

View File

@@ -44,10 +44,10 @@ public class GroupMember implements Serializable {
private Long uid;
/**
* 成员类型 1群主 2管理员 3普通成员
* 成员角色1群主 2管理员 3普通成员
*/
@TableField("type")
private Integer type;
@TableField("role")
private Integer role;
/**
* 创建时间

View File

@@ -24,7 +24,6 @@ import java.time.LocalDateTime;
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@@ -32,25 +31,15 @@ public class Room implements Serializable {
private Long id;
/**
* 群名称
*/
@TableField("name")
private String name;
/**
* 群头像
*/
@TableField("avatar")
private String avatar;
/**
* 房间类型 1群聊
* 房间类型 1群聊 2单聊
*/
@TableField("type")
private Integer type;
/**
* 是否全员展示 0否 1是
*
* @see com.abin.mallchat.common.chat.domain.enums.HotFlagEnum
*/
@TableField("hot_flag")
private Integer hotFlag;

View File

@@ -0,0 +1,76 @@
package com.abin.mallchat.common.chat.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 单聊房间表
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("room_friend")
public class RoomFriend implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 房间id
*/
@TableField("room_id")
private Long roomId;
/**
* uid1更小的uid
*/
@TableField("uid1")
private Long uid1;
/**
* uid2更大的uid
*/
@TableField("uid2")
private Long uid2;
/**
* 房间key由两个uid拼接先做排序uid1_uid2
*/
@TableField("key")
private String key;
/**
* 房间状态 0正常 1禁用(删好友了禁用)
*/
@TableField("status")
private Integer status;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField("update_time")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,75 @@
package com.abin.mallchat.common.chat.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 群聊房间表
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("room_group")
public class RoomGroup implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 房间id
*/
@TableField("room_id")
private Long roomId;
/**
* 群名称
*/
@TableField("name")
private String name;
/**
* 群头像
*/
@TableField("avatar")
private String avatar;
/**
* 额外信息(根据不同类型房间有不同存储的东西)
*/
@TableField("ext_json")
private String extJson;
/**
* 逻辑删除(0-正常,1-删除)
*/
@TableField("delete_status")
@TableLogic(value = "0", delval = "1")
private Integer deleteStatus;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField("update_time")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,35 @@
package com.abin.mallchat.common.chat.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Description: 热点枚举
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-03-19
*/
@AllArgsConstructor
@Getter
public enum HotFlagEnum {
NOT(0, "非热点"),
YES(1, "热点"),
;
private final Integer type;
private final String desc;
private static Map<Integer, HotFlagEnum> cache;
static {
cache = Arrays.stream(HotFlagEnum.values()).collect(Collectors.toMap(HotFlagEnum::getType, Function.identity()));
}
public static HotFlagEnum of(Integer type) {
return cache.get(type);
}
}

View File

@@ -16,17 +16,17 @@ import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
public enum RoomTypeEnum {
GROUP(1, "群聊"),
BOILING(2, "沸点"),
GROUP(1, "群聊"),
FRIEND(2, "单聊"),
;
private final Integer status;
private final Integer type;
private final String desc;
private static Map<Integer, RoomTypeEnum> cache;
static {
cache = Arrays.stream(RoomTypeEnum.values()).collect(Collectors.toMap(RoomTypeEnum::getStatus, Function.identity()));
cache = Arrays.stream(RoomTypeEnum.values()).collect(Collectors.toMap(RoomTypeEnum::getType, Function.identity()));
}
public static RoomTypeEnum of(Integer type) {

View File

@@ -0,0 +1,16 @@
package com.abin.mallchat.common.chat.mapper;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 单聊房间表 Mapper 接口
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
public interface RoomFriendMapper extends BaseMapper<RoomFriend> {
}

View File

@@ -0,0 +1,16 @@
package com.abin.mallchat.common.chat.mapper;
import com.abin.mallchat.common.chat.domain.entity.RoomGroup;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 群聊房间表 Mapper 接口
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-22
*/
public interface RoomGroupMapper extends BaseMapper<RoomGroup> {
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abin.mallchat.common.chat.mapper.RoomFriendMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abin.mallchat.common.chat.mapper.RoomGroupMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abin.mallchat.common.chat.mapper.RoomMapper">
</mapper>

View File

@@ -0,0 +1,29 @@
package com.abin.mallchat.common.chat.service;
import com.abin.mallchat.common.chat.domain.dto.MsgReadInfoDTO;
import com.abin.mallchat.common.chat.domain.entity.Contact;
import com.abin.mallchat.common.chat.domain.entity.Message;
import java.util.List;
import java.util.Map;
/**
* <p>
* 会话列表 服务类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
public interface ContactService {
/**
* 创建会话
*/
Contact createContact(Long uid, Long roomId);
Integer getMsgReadCount(Message message);
Integer getMsgUnReadCount(Message message);
Map<Long, MsgReadInfoDTO> getMsgReadInfo(List<Message> messages);
}

View File

@@ -1,16 +0,0 @@
package com.abin.mallchat.common.chat.service;
import com.abin.mallchat.common.chat.domain.entity.Contact;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 会话列表 服务类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
public interface IContactService extends IService<Contact> {
}

View File

@@ -1,16 +0,0 @@
package com.abin.mallchat.common.chat.service;
import com.abin.mallchat.common.chat.domain.entity.Room;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 房间表 服务类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-07-16
*/
public interface IRoomService extends IService<Room> {
}

View File

@@ -1,16 +0,0 @@
package com.abin.mallchat.common.chat.service;
import com.abin.mallchat.common.chat.domain.entity.WxMsg;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 微信消息表 服务类
* </p>
*
* @author <a href="https://github.com/zongzibinbin">abin</a>
* @since 2023-05-16
*/
public interface IWxMsgService extends IService<WxMsg> {
}

View File

@@ -0,0 +1,18 @@
package com.abin.mallchat.common.chat.service;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import java.util.List;
/**
* Description: 房间底层管理
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-22
*/
public interface RoomService {
/**
* 创建一个单聊房间
*/
RoomFriend createFriendRoom(List<Long> uidList);
}

View File

@@ -0,0 +1,68 @@
package com.abin.mallchat.common.chat.service.adapter;
import com.abin.mallchat.common.chat.domain.entity.Contact;
import com.abin.mallchat.common.chat.domain.entity.Room;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import com.abin.mallchat.common.chat.domain.enums.HotFlagEnum;
import com.abin.mallchat.common.chat.domain.enums.RoomTypeEnum;
import com.abin.mallchat.common.common.domain.enums.NormalOrNoEnum;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Description:
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-22
*/
public class ChatAdapter {
public static final String SEPARATOR = ",";
public static String generateRoomKey(List<Long> uidList) {
return uidList.stream()
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(SEPARATOR));
}
public static Room buildRoom(RoomTypeEnum typeEnum) {
Room room = new Room();
room.setType(typeEnum.getType());
room.setHotFlag(HotFlagEnum.NOT.getType());
return room;
}
public static RoomFriend buildFriendRoom(Long roomId, List<Long> uidList) {
List<Long> collect = uidList.stream().sorted().collect(Collectors.toList());
RoomFriend roomFriend = new RoomFriend();
roomFriend.setRoomId(roomId);
roomFriend.setUid1(collect.get(0));
roomFriend.setUid2(collect.get(1));
roomFriend.setKey(generateRoomKey(uidList));
roomFriend.setStatus(NormalOrNoEnum.NORMAL.getStatus());
return roomFriend;
}
public static Contact buildContact(Long uid, Long roomId) {
Contact contact = new Contact();
contact.setRoomId(roomId);
contact.setUid(uid);
return contact;
}
public static Set<Long> getFriendUidSet(Collection<RoomFriend> values, Long uid) {
return values.stream()
.map(a -> getFriendUid(a, uid))
.collect(Collectors.toSet());
}
/**
* 获取好友uid
*/
public static Long getFriendUid(RoomFriend roomFriend, Long uid) {
return Objects.equals(uid, roomFriend.getUid1()) ? roomFriend.getUid2() : roomFriend.getUid1();
}
}

View File

@@ -0,0 +1,46 @@
package com.abin.mallchat.common.chat.service.cache;
import com.abin.mallchat.common.chat.dao.RoomDao;
import com.abin.mallchat.common.chat.dao.RoomFriendDao;
import com.abin.mallchat.common.chat.domain.entity.Room;
import com.abin.mallchat.common.common.constant.RedisKey;
import com.abin.mallchat.common.common.service.cache.AbstractRedisStringCache;
import com.abin.mallchat.common.user.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Description: 房间基本信息的缓存
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-10
*/
@Component
public class RoomCache extends AbstractRedisStringCache<Long, Room> {
@Autowired
private UserDao userDao;
@Autowired
private RoomDao roomDao;
@Autowired
private RoomFriendDao roomFriendDao;
@Override
protected String getKey(Long roomId) {
return RedisKey.getKey(RedisKey.ROOM_INFO_STRING, roomId);
}
@Override
protected Long getExpireSeconds() {
return 5 * 60L;
}
@Override
protected Map<Long, Room> load(List<Long> roomIds) {
List<Room> rooms = roomDao.listByIds(roomIds);
return rooms.stream().collect(Collectors.toMap(Room::getId, Function.identity()));
}
}

View File

@@ -0,0 +1,40 @@
package com.abin.mallchat.common.chat.service.cache;
import com.abin.mallchat.common.chat.dao.RoomFriendDao;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import com.abin.mallchat.common.common.constant.RedisKey;
import com.abin.mallchat.common.common.service.cache.AbstractRedisStringCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Description: 群组基本信息的缓存
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-10
*/
@Component
public class RoomFriendCache extends AbstractRedisStringCache<Long, RoomFriend> {
@Autowired
private RoomFriendDao roomFriendDao;
@Override
protected String getKey(Long groupId) {
return RedisKey.getKey(RedisKey.GROUP_INFO_STRING, groupId);
}
@Override
protected Long getExpireSeconds() {
return 5 * 60L;
}
@Override
protected Map<Long, RoomFriend> load(List<Long> roomIds) {
List<RoomFriend> roomGroups = roomFriendDao.listByRoomIds(roomIds);
return roomGroups.stream().collect(Collectors.toMap(RoomFriend::getRoomId, Function.identity()));
}
}

View File

@@ -0,0 +1,40 @@
package com.abin.mallchat.common.chat.service.cache;
import com.abin.mallchat.common.chat.dao.RoomGroupDao;
import com.abin.mallchat.common.chat.domain.entity.RoomGroup;
import com.abin.mallchat.common.common.constant.RedisKey;
import com.abin.mallchat.common.common.service.cache.AbstractRedisStringCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Description: 群组基本信息的缓存
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-10
*/
@Component
public class RoomGroupCache extends AbstractRedisStringCache<Long, RoomGroup> {
@Autowired
private RoomGroupDao roomGroupDao;
@Override
protected String getKey(Long groupId) {
return RedisKey.getKey(RedisKey.GROUP_INFO_STRING, groupId);
}
@Override
protected Long getExpireSeconds() {
return 5 * 60L;
}
@Override
protected Map<Long, RoomGroup> load(List<Long> roomIds) {
List<RoomGroup> roomGroups = roomGroupDao.listByRoomIds(roomIds);
return roomGroups.stream().collect(Collectors.toMap(RoomGroup::getRoomId, Function.identity()));
}
}

View File

@@ -0,0 +1,68 @@
package com.abin.mallchat.common.chat.service.impl;
import com.abin.mallchat.common.chat.dao.ContactDao;
import com.abin.mallchat.common.chat.dao.MessageDao;
import com.abin.mallchat.common.chat.domain.dto.MsgReadInfoDTO;
import com.abin.mallchat.common.chat.domain.entity.Contact;
import com.abin.mallchat.common.chat.domain.entity.Message;
import com.abin.mallchat.common.chat.service.ContactService;
import com.abin.mallchat.common.chat.service.adapter.ChatAdapter;
import com.abin.mallchat.common.common.utils.AssertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Description: 会话列表
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-22
*/
@Service
public class ContactServiceImpl implements ContactService {
@Autowired
private ContactDao contactDao;
@Autowired
private MessageDao messageDao;
@Override
public Contact createContact(Long uid, Long roomId) {
Contact contact = contactDao.get(uid, roomId);
if (Objects.isNull(contact)) {
contact = ChatAdapter.buildContact(uid, roomId);
contactDao.save(contact);
}
return contact;
}
@Override
public Integer getMsgReadCount(Message message) {
return contactDao.getReadCount(message);
}
@Override
public Integer getMsgUnReadCount(Message message) {
return contactDao.getUnReadCount(message);
}
@Override
public Map<Long, MsgReadInfoDTO> getMsgReadInfo(List<Message> messages) {
Map<Long, List<Message>> roomGroup = messages.stream().collect(Collectors.groupingBy(Message::getRoomId));
AssertUtil.equal(roomGroup.size(), 1, "只能查相同房间下的消息");
Long roomId = roomGroup.keySet().iterator().next();
Integer totalCount = contactDao.getTotalCount(roomId);
return messages.stream().map(message -> {
MsgReadInfoDTO readInfoDTO = new MsgReadInfoDTO();
readInfoDTO.setMsgId(message.getId());
Integer readCount = contactDao.getReadCount(message);
readInfoDTO.setReadCount(readCount);
readInfoDTO.setUnReadCount(totalCount - readCount);
return readInfoDTO;
}).collect(Collectors.toMap(MsgReadInfoDTO::getMsgId, Function.identity()));
}
}

View File

@@ -0,0 +1,66 @@
package com.abin.mallchat.common.chat.service.impl;
import com.abin.mallchat.common.chat.dao.RoomDao;
import com.abin.mallchat.common.chat.dao.RoomFriendDao;
import com.abin.mallchat.common.chat.domain.entity.Room;
import com.abin.mallchat.common.chat.domain.entity.RoomFriend;
import com.abin.mallchat.common.chat.domain.enums.RoomTypeEnum;
import com.abin.mallchat.common.chat.service.RoomService;
import com.abin.mallchat.common.chat.service.adapter.ChatAdapter;
import com.abin.mallchat.common.common.domain.enums.NormalOrNoEnum;
import com.abin.mallchat.common.common.utils.AssertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
/**
* Description:
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-07-22
*/
@Service
public class RoomServiceImpl implements RoomService {
@Autowired
private RoomFriendDao roomFriendDao;
@Autowired
private RoomDao roomDao;
@Override
@Transactional(rollbackFor = Exception.class)
public RoomFriend createFriendRoom(List<Long> uidList) {
AssertUtil.isNotEmpty(uidList, "房间创建失败,好友数量不对");
AssertUtil.equal(uidList.size(), 2, "房间创建失败,好友数量不对");
String key = ChatAdapter.generateRoomKey(uidList);
RoomFriend roomFriend = roomFriendDao.getByKey(key);
if (Objects.nonNull(roomFriend)) { //如果存在房间就恢复,适用于恢复好友场景
restoreRoomIfNeed(roomFriend);
} else {//新建房间
Room room = createRoom(RoomTypeEnum.GROUP);
roomFriend = createFriendRoom(room.getId(), uidList);
}
return roomFriend;
}
private RoomFriend createFriendRoom(Long roomId, List<Long> uidList) {
RoomFriend insert = ChatAdapter.buildFriendRoom(roomId, uidList);
roomFriendDao.save(insert);
return insert;
}
private Room createRoom(RoomTypeEnum typeEnum) {
Room insert = ChatAdapter.buildRoom(typeEnum);
roomDao.save(insert);
return insert;
}
private void restoreRoomIfNeed(RoomFriend room) {
if (Objects.equals(room.getStatus(), NormalOrNoEnum.NOT_NORMAL.getStatus())) {
roomFriendDao.restoreRoom(room.getId());
}
}
}

View File

@@ -21,6 +21,16 @@ public class RedisKey {
*/
public static final String USER_INFO_STRING = "userInfo:uid_%d";
/**
* 房间详情
*/
public static final String ROOM_INFO_STRING = "roomInfo:roomId_%d";
/**
* 群组详情
*/
public static final String GROUP_INFO_STRING = "groupInfo:roomId_%d";
/**
* 用户token存放
*/

View File

@@ -36,13 +36,13 @@ public class PageBaseResp<T> {
private List<T> list;
public static PageBaseResp empty() {
PageBaseResp r = new PageBaseResp();
public static <T> PageBaseResp<T> empty() {
PageBaseResp<T> r = new PageBaseResp<>();
r.setPageNo(1);
r.setPageSize(0);
r.setIsLast(true);
r.setTotalRecords(0L);
r.setList(new ArrayList());
r.setList(new ArrayList<>());
return r;
}

View File

@@ -0,0 +1,16 @@
package com.abin.mallchat.common.common.event;
import com.abin.mallchat.common.user.domain.entity.UserApply;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
@Getter
public class UserApplyEvent extends ApplicationEvent {
private UserApply userApply;
public UserApplyEvent(Object source, UserApply userApply) {
super(source);
this.userApply = userApply;
}
}

View File

@@ -35,6 +35,9 @@ public abstract class AbstractRedisStringCache<IN, OUT> implements BatchCache<IN
@Override
public Map<IN, OUT> getBatch(List<IN> req) {
if (CollectionUtil.isEmpty(req)) {//防御性编程
return new HashMap<>();
}
req = req.stream().distinct().collect(Collectors.toList());
List<String> keys = req.stream().map(this::getKey).collect(Collectors.toList());
List<OUT> valueList = RedisUtils.mget(keys, outClass);

View File

@@ -10,7 +10,6 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@@ -24,10 +23,9 @@ import java.util.stream.Collectors;
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-03-28
*/
@Component
public class CursorUtils {
public <T> CursorPageBaseResp<Pair<T, Double>> getCursorPageByRedis(CursorPageBaseReq cursorPageBaseReq, String redisKey, Function<String, T> typeConvert) {
public static <T> CursorPageBaseResp<Pair<T, Double>> getCursorPageByRedis(CursorPageBaseReq cursorPageBaseReq, String redisKey, Function<String, T> typeConvert) {
Set<ZSetOperations.TypedTuple<String>> typedTuples;
if (StrUtil.isBlank(cursorPageBaseReq.getCursor())) {//第一次
typedTuples = RedisUtils.zReverseRangeWithScores(redisKey, cursorPageBaseReq.getPageSize());
@@ -47,7 +45,7 @@ public class CursorUtils {
return new CursorPageBaseResp<>(cursor, isLast, result);
}
public <T> CursorPageBaseResp<T> getCursorPageByMysql(IService<T> mapper, CursorPageBaseReq request, Consumer<LambdaQueryWrapper<T>> initWrapper, SFunction<T, ?> cursorColumn) {
public static <T> CursorPageBaseResp<T> getCursorPageByMysql(IService<T> mapper, CursorPageBaseReq request, Consumer<LambdaQueryWrapper<T>> initWrapper, SFunction<T, ?> cursorColumn) {
LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
initWrapper.accept(wrapper);
if (StrUtil.isNotBlank(request.getCursor())) {

View File

@@ -1,17 +1,15 @@
package com.abin.mallchat.common.user.dao;
import com.abin.mallchat.common.user.domain.entity.UserApply;
import com.abin.mallchat.common.user.domain.enums.ApplyStatusEnum;
import com.abin.mallchat.common.user.domain.enums.ApplyTypeEnum;
import com.abin.mallchat.common.user.mapper.UserApplyMapper;
import com.abin.mallchat.common.user.service.IUserApplyService;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
import static com.abin.mallchat.common.user.domain.enums.ApplyReadStatusEnum.UNREAD;
import static com.abin.mallchat.common.user.domain.enums.ApplyStatusEnum.AGREE;
/**
* <p>
@@ -22,42 +20,42 @@ import static com.abin.mallchat.common.user.domain.enums.ApplyStatusEnum.AGREE;
* @since 2023-07-16
*/
@Service
public class UserApplyDao extends ServiceImpl<UserApplyMapper, UserApply> implements IUserApplyService {
public class UserApplyDao extends ServiceImpl<UserApplyMapper, UserApply> {
public UserApply queryUserApply(Long uid, Long targetUid) {
LambdaQueryChainWrapper<UserApply> wrapper = lambdaQuery()
.eq(UserApply::getUid, uid)
.eq(UserApply::getTargetId, targetUid);
return getOne(wrapper);
public UserApply getFriendApproving(Long uid, Long targetUid) {
return lambdaQuery().eq(UserApply::getUid, uid)
.eq(UserApply::getTargetId, targetUid)
.eq(UserApply::getStatus, ApplyStatusEnum.WAIT_APPROVAL)
.eq(UserApply::getType, ApplyTypeEnum.ADD_FRIEND.getCode())
.one();
}
public void insert(UserApply userApply) {
save(userApply);
public Integer getUnReadCount(Long targetId) {
return lambdaQuery().eq(UserApply::getTargetId, targetId)
.eq(UserApply::getReadStatus, UNREAD.getCode())
.count();
}
public List<UserApply> queryUserApplyList(Long uid) {
LambdaQueryChainWrapper<UserApply> wrapper = lambdaQuery()
.eq(UserApply::getUid, uid)
.or()
.eq(UserApply::getTargetId, uid);
return list(wrapper);
}
public Integer unreadCount(Long uid) {
LambdaQueryChainWrapper<UserApply> wrapper = lambdaQuery()
public IPage<UserApply> FriendApplyPage(Long uid, Page page) {
return lambdaQuery()
.eq(UserApply::getTargetId, uid)
.eq(UserApply::getReadStatus, UNREAD.getCode());
return count(wrapper);
.eq(UserApply::getType, ApplyTypeEnum.ADD_FRIEND.getCode())
.orderByAsc(UserApply::getCreateTime)
.page(page);
}
public UserApply queryUserApplyById(Long applyId) {
return getById(applyId);
public void readApples(Long uid, List<Long> applyIds) {
lambdaUpdate()
.set(UserApply::getReadStatus, READ.getCode())
.eq(UserApply::getReadStatus, UNREAD.getCode())
.in(UserApply::getId, applyIds)
.eq(UserApply::getTargetId, uid)
.update();
}
public void agreeUserApply(Long applyId) {
LambdaUpdateChainWrapper<UserApply> updateWrapper = lambdaUpdate()
.set(UserApply::getStatus, AGREE.getCode())
.eq(UserApply::getId, applyId);
update(updateWrapper);
public void agree(Long applyId) {
lambdaUpdate().set(UserApply::getStatus, AGREE.getCode())
.eq(UserApply::getId, applyId)
.update();
}
}

View File

@@ -2,13 +2,9 @@ package com.abin.mallchat.common.user.dao;
import com.abin.mallchat.common.user.domain.entity.UserFriend;
import com.abin.mallchat.common.user.mapper.UserFriendMapper;
import com.abin.mallchat.common.user.service.IUserFriendService;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 用户联系人表 服务实现类
@@ -18,15 +14,17 @@ import java.util.List;
* @since 2023-07-16
*/
@Service
public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> implements IUserFriendService {
public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
public List<UserFriend> queryUserFriend(Long uid, List<Long> friendUidList) {
LambdaQueryChainWrapper<UserFriend> wrapper = lambdaQuery()
.eq(UserFriend::getUid, uid).in(UserFriend::getFriendUid, friendUidList);
return list(wrapper);
public List<UserFriend> getByFriends(Long uid, List<Long> uidList) {
return lambdaQuery().eq(UserFriend::getUid, uid)
.in(UserFriend::getFriendUid, uidList)
.list();
}
public void insertBatch(List<UserFriend> userFriends) {
saveBatch(userFriends);
public UserFriend getByFriend(Long uid, Long targetUid) {
return lambdaQuery().eq(UserFriend::getUid, uid)
.eq(UserFriend::getFriendUid, targetUid)
.one();
}
}

View File

@@ -54,6 +54,4 @@ public class UserRole implements Serializable {
*/
@TableField("update_time")
private LocalDateTime updateTime;
}

View File

@@ -31,8 +31,6 @@ import java.util.stream.Collectors;
@Component
public class UserCache {
@Autowired
private CursorUtils cursorUtils;
@Autowired
private UserDao userDao;
@Autowired
@@ -88,11 +86,11 @@ public class UserCache {
}
public CursorPageBaseResp<Pair<Long, Double>> getOnlineCursorPage(CursorPageBaseReq pageBaseReq) {
return cursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.ONLINE_UID_ZET), Long::parseLong);
return CursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.ONLINE_UID_ZET), Long::parseLong);
}
public CursorPageBaseResp<Pair<Long, Double>> getOfflineCursorPage(CursorPageBaseReq pageBaseReq) {
return cursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.OFFLINE_UID_ZET), Long::parseLong);
return CursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.OFFLINE_UID_ZET), Long::parseLong);
}
public List<Long> getUserModifyTime(List<Long> uidList) {