diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java index 6ae35de..2311468 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java @@ -1,7 +1,9 @@ package com.bx.imcommon.enums; +import lombok.AllArgsConstructor; +@AllArgsConstructor public enum IMCmdType { LOGIN(0,"登陆"), @@ -11,15 +13,10 @@ public enum IMCmdType { GROUP_MESSAGE(4,"群发消息"); - private Integer code; private String desc; - IMCmdType(Integer index, String desc) { - this.code =index; - this.desc=desc; - } public static IMCmdType fromCode(Integer code){ for (IMCmdType typeEnum:values()) { @@ -31,10 +28,6 @@ public enum IMCmdType { } - public String description() { - return desc; - } - public Integer code(){ return this.code; } diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java index 6ec1828..c0de103 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java @@ -1,5 +1,8 @@ package com.bx.imcommon.enums; +import lombok.AllArgsConstructor; + +@AllArgsConstructor public enum IMListenerType{ ALL(0,"全部消息"), PRIVATE_MESSAGE(1,"私聊消息"), @@ -9,15 +12,6 @@ public enum IMListenerType{ private String desc; - IMListenerType(Integer index, String desc) { - this.code =index; - this.desc=desc; - } - - - public String description() { - return desc; - } public Integer code(){ diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java index 37de1d7..5189a2f 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java @@ -1,6 +1,8 @@ package com.bx.imcommon.enums; +import lombok.AllArgsConstructor; +@AllArgsConstructor public enum IMSendCode { SUCCESS(0,"发送成功"), @@ -11,11 +13,6 @@ public enum IMSendCode { private Integer code; private String desc; - // 构造方法 - IMSendCode(int code, String desc) { - this.code = code; - this.desc = desc; - } public static IMSendCode fromCode(Integer code){ for (IMSendCode typeEnum:values()) { @@ -27,9 +24,6 @@ public enum IMSendCode { } - public String description() { - return desc; - } public Integer code(){ diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java index 7ca76f1..d0f4b81 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java @@ -1,9 +1,12 @@ package com.bx.imcommon.enums; +import lombok.AllArgsConstructor; + import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +@AllArgsConstructor public enum IMTerminalType { WEB(0,"web"), @@ -13,10 +16,6 @@ public enum IMTerminalType { private String desc; - IMTerminalType(Integer index, String desc) { - this.code =index; - this.desc=desc; - } public static IMTerminalType fromCode(Integer code){ for (IMTerminalType typeEnum:values()) { @@ -31,10 +30,6 @@ public enum IMTerminalType { return Arrays.stream(values()).map(IMTerminalType::code).collect(Collectors.toList()); } - public String description() { - return desc; - } - public Integer code(){ return this.code; } diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java b/im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java index 9bbc905..2c298ca 100644 --- a/im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java @@ -3,6 +3,8 @@ package com.bx.imcommon.model; import com.bx.imcommon.enums.IMTerminalType; import lombok.Data; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; @Data @@ -14,9 +16,9 @@ public class IMGroupMessage { private IMUserInfo sender; /** - * 接收者id列表(群成员列表) + * 接收者id列表(群成员列表,为空则不会推送) */ - private List recvIds; + private List recvIds = Collections.EMPTY_LIST; /** diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java index 8d57a81..73abf24 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java @@ -6,6 +6,7 @@ import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IGroupMessageService; import com.bx.implatform.dto.GroupMessageDTO; +import com.bx.implatform.vo.PrivateMessageVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +39,7 @@ public class GroupMessageController { return ResultUtils.success(); } + // todo 删除 @PostMapping("/pullUnreadMessage") @ApiOperation(value = "拉取未读消息",notes="拉取未读消息") public Result pullUnreadMessage(){ @@ -45,6 +47,22 @@ public class GroupMessageController { return ResultUtils.success(); } + @GetMapping("/loadMessage") + @ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条") + public Result> loadMessage(@RequestParam Long minId){ + return ResultUtils.success(groupMessageService.loadMessage(minId)); + } + + + @PutMapping("/readed") + @ApiOperation(value = "消息已读",notes="将群聊中的消息状态置为已读") + public Result readedMessage(@RequestParam Long groupId){ + groupMessageService.readedMessage(groupId); + return ResultUtils.success(); + } + + + @GetMapping("/history") @ApiOperation(value = "查询聊天记录",notes="查询聊天记录") public Result> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId, 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 cde3c38..ea55123 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 @@ -37,7 +37,7 @@ public class PrivateMessageController { return ResultUtils.success(); } - + // todo 删除 @PostMapping("/pullUnreadMessage") @ApiOperation(value = "拉取未读消息",notes="拉取未读消息") public Result pullUnreadMessage(){ @@ -46,6 +46,19 @@ public class PrivateMessageController { } + @GetMapping("/loadMessage") + @ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条") + public Result> loadMessage(@RequestParam Long minId){ + return ResultUtils.success(privateMessageService.loadMessage(minId)); + } + + @PutMapping("/readed") + @ApiOperation(value = "消息已读",notes="将会话中接收的消息状态置为已读") + public Result readedMessage(@RequestParam Long friendId){ + privateMessageService.readedMessage(friendId); + return ResultUtils.success(); + } + @GetMapping("/history") @ApiOperation(value = "查询聊天记录",notes="查询聊天记录") public Result> recallMessage(@NotNull(message = "好友id不能为空") @RequestParam Long friendId, 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 22222c0..2165cde 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 @@ -54,7 +54,7 @@ public class UserController { @GetMapping("/find/{id}") @ApiOperation(value = "查找用户",notes="根据id查找用户") - public Result findById(@NotEmpty @PathVariable("id") Long id){ + public Result findById(@NotEmpty @PathVariable("id") Long id){ return ResultUtils.success(userService.findUserById(id)); } diff --git a/im-platform/src/main/java/com/bx/implatform/enums/FileType.java b/im-platform/src/main/java/com/bx/implatform/enums/FileType.java index dc2071f..912029b 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/FileType.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/FileType.java @@ -1,5 +1,8 @@ package com.bx.implatform.enums; +import lombok.AllArgsConstructor; + +@AllArgsConstructor public enum FileType { FILE(0,"文件"), @@ -13,15 +16,6 @@ public enum FileType { private final String desc; - FileType(Integer index, String desc) { - this.code =index; - this.desc=desc; - } - - - public String description() { - return desc; - } public Integer code(){ return this.code; diff --git a/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java b/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java index 8a7968e..3707a7f 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java @@ -1,24 +1,19 @@ package com.bx.implatform.enums; +import lombok.AllArgsConstructor; +@AllArgsConstructor public enum MessageStatus { - UNREAD(0,"未读"), - ALREADY_READ(1,"已读"), - RECALL(2,"已撤回"); + UNSEND(0,"未送达"), + SENDED(1,"送达"), + RECALL(2,"撤回"), + READED(3,"已读"); private final Integer code; private final String desc; - MessageStatus(Integer index, String desc) { - this.code =index; - this.desc=desc; - } - - public String description() { - return desc; - } public Integer code(){ return this.code; diff --git a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java index 5e6d2e8..559531c 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java @@ -1,6 +1,8 @@ package com.bx.implatform.enums; +import lombok.AllArgsConstructor; +@AllArgsConstructor public enum MessageType { TEXT(0,"文字"), @@ -9,6 +11,7 @@ public enum MessageType { AUDIO(3,"音频"), VIDEO(4,"视频"), RECALL(10,"撤回"), + READED(11, "已读"), RTC_CALL(101,"呼叫"), RTC_ACCEPT(102,"接受"), @@ -22,14 +25,6 @@ public enum MessageType { private final String desc; - MessageType(Integer index, String desc) { - this.code =index; - this.desc=desc; - } - - public String description() { - return desc; - } public Integer code(){ return this.code; diff --git a/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java index 8b8a4b0..109ac59 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java @@ -1,5 +1,8 @@ package com.bx.implatform.enums; +import lombok.AllArgsConstructor; +import lombok.Getter; + /** * 响应码枚举 * @@ -7,6 +10,8 @@ package com.bx.implatform.enums; * @date 2020/10/19 * **/ +@Getter +@AllArgsConstructor public enum ResultCode { SUCCESS(200,"成功"), NO_LOGIN(400,"未登录"), @@ -21,24 +26,6 @@ public enum ResultCode { private int code; private String msg; - ResultCode(int code, String msg) { - this.code = code; - this.msg = msg; - } - public int getCode() { - return code; - } - - public void setCode(int code) { - this.code = code; - } - - public String getMsg() { - return msg; - } - public void setMsg(String msg) { - this.msg = msg; - } } diff --git a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java index 68a6f2b..fe82817 100644 --- a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java +++ b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java @@ -29,8 +29,8 @@ public class PrivateMessageListener implements MessageListener if(result.getCode().equals(IMSendCode.SUCCESS.code())){ UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId()) - .eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code()) - .set(PrivateMessage::getStatus, MessageStatus.ALREADY_READ.code()); + .eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code()) + .set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); privateMessageService.update(updateWrapper); log.info("消息已读,消息id:{},发送者:{},接收者:{},终端:{}",messageInfo.getId(),result.getSender().getId(),result.getReceiver().getId(),result.getReceiver().getTerminal()); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java b/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java index a003afe..304a00c 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java @@ -17,5 +17,9 @@ public interface IGroupMessageService extends IService { void pullUnreadMessage(); + List loadMessage(Long minId); + + void readedMessage(Long groupId); + List findHistoryMessage(Long groupId, Long page, Long size); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java b/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java index 0b42d21..f0036e0 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java @@ -18,4 +18,7 @@ public interface IPrivateMessageService extends IService { void pullUnreadMessage(); + List loadMessage(Long minId); + + void readedMessage(Long friendId); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java index c2f992a..8a90f4e 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bx.imclient.IMClient; import com.bx.imcommon.contant.IMConstant; +import com.bx.implatform.util.DateTimeUtils; import com.bx.implatform.vo.GroupMessageVO; import com.bx.imcommon.model.IMGroupMessage; import com.bx.imcommon.model.IMUserInfo; @@ -33,6 +34,7 @@ import org.springframework.stereotype.Service; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; @Slf4j @@ -43,7 +45,7 @@ public class GroupMessageServiceImpl extends ServiceImpl redisTemplate; + private RedisTemplate redisTemplate; @Autowired private IMClient imClient; @@ -57,16 +59,16 @@ public class GroupMessageServiceImpl extends ServiceImpl userIds = groupMemberService.findUserIdsByGroupId(group.getId()); - if(!userIds.contains(session.getUserId())){ - throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊里面,无法发送消息"); + if (!userIds.contains(session.getUserId())) { + throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊里面,无法发送消息"); } // 保存消息 GroupMessage msg = BeanUtils.copyProperties(dto, GroupMessage.class); @@ -74,21 +76,19 @@ public class GroupMessageServiceImpl extends ServiceImpl!session.getUserId().equals(id)).collect(Collectors.toList()); + userIds = userIds.stream().filter(id -> !session.getUserId().equals(id)).collect(Collectors.toList()); // 群发 GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class); IMGroupMessage sendMessage = new IMGroupMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setRecvIds(userIds); sendMessage.setData(msgInfo); imClient.sendGroupMessage(sendMessage); - log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),dto.getGroupId(),dto.getContent()); + log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getGroupId(), dto.getContent()); return msg.getId(); } - - /** * 撤回消息 * @@ -98,19 +98,19 @@ public class GroupMessageServiceImpl extends ServiceImpl IMConstant.ALLOW_RECALL_SECOND * 1000){ - throw new GlobalException(ResultCode.PROGRAM_ERROR,"消息已发送超过5分钟,无法撤回"); + if (System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000) { + throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回"); } // 判断是否在群里 - GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(),session.getUserId()); - if(member == null){ - throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊里面,无法撤回消息"); + GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(), session.getUserId()); + if (member == null) { + throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊里面,无法撤回消息"); } // 修改数据库 msg.setStatus(MessageStatus.RECALL.code()); @@ -118,15 +118,15 @@ public class GroupMessageServiceImpl extends ServiceImpl userIds = groupMemberService.findUserIdsByGroupId(msg.getGroupId()); // 不用发给自己 - userIds = userIds.stream().filter(uid->!session.getUserId().equals(uid)).collect(Collectors.toList()); + userIds = userIds.stream().filter(uid -> !session.getUserId().equals(uid)).collect(Collectors.toList()); GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class); msgInfo.setType(MessageType.RECALL.code()); - String content = String.format("'%s'撤回了一条消息",member.getAliasName()); + String content = String.format("'%s'撤回了一条消息", member.getAliasName()); msgInfo.setContent(content); msgInfo.setSendTime(new Date()); IMGroupMessage sendMessage = new IMGroupMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setRecvIds(userIds); sendMessage.setData(msgInfo); sendMessage.setSendResult(false); @@ -139,40 +139,39 @@ public class GroupMessageServiceImpl extends ServiceImpl members = groupMemberService.findByUserId(session.getUserId()); - for(GroupMember member:members){ + for (GroupMember member : members) { // 获取群聊已读的最大消息id,只推送未读消息 - String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,member.getGroupId().toString(),session.getUserId().toString()); - Integer maxReadedId = (Integer)redisTemplate.opsForValue().get(key); + String key = String.join(":", RedisKey.IM_GROUP_READED_POSITION, member.getGroupId().toString(), session.getUserId().toString()); + Integer maxReadedId = (Integer) redisTemplate.opsForValue().get(key); LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(GroupMessage::getGroupId,member.getGroupId()) - .gt(GroupMessage::getSendTime,member.getCreatedTime()) + wrapper.eq(GroupMessage::getGroupId, member.getGroupId()) + .gt(GroupMessage::getSendTime, member.getCreatedTime()) .ne(GroupMessage::getSendId, session.getUserId()) .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()); - if(maxReadedId!=null){ - wrapper.gt(GroupMessage::getId,maxReadedId); + if (maxReadedId != null) { + wrapper.gt(GroupMessage::getId, maxReadedId); } wrapper.last("limit 100"); List messages = this.list(wrapper); - if(messages.isEmpty()){ + if (messages.isEmpty()) { continue; } // 推送 - for (GroupMessage message:messages ){ + for (GroupMessage message : messages) { GroupMessageVO msgInfo = BeanUtils.copyProperties(message, GroupMessageVO.class); IMGroupMessage sendMessage = new IMGroupMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); // 只推给自己当前终端 sendMessage.setRecvIds(Collections.singletonList(session.getUserId())); sendMessage.setRecvTerminals(Collections.singletonList(session.getTerminal())); @@ -180,41 +179,109 @@ public class GroupMessageServiceImpl extends ServiceImpl loadMessage(Long minId) { + UserSession session = SessionContext.getSession(); + List members = groupMemberService.findByUserId(session.getUserId()); + List ids = members.stream().map(GroupMember::getGroupId).collect(Collectors.toList()); + // 只能拉取最近3个月的 + Date minDate = DateTimeUtils.addMonths(new Date(), -1); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.gt(GroupMessage::getId, minId) + .gt(GroupMessage::getSendTime, minDate) + .in(GroupMessage::getGroupId, ids) + .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()) + .last("limit 100"); + + List messages = this.list(wrapper); + // 转成vo + List vos = messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList()); + // 消息状态,数据库没有存群聊的消息状态,需要从redis取 + List keys = ids.stream() + .map(id -> String.join(":", RedisKey.IM_GROUP_READED_POSITION, id.toString(), session.getUserId().toString())) + .collect(Collectors.toList()); + List sendPos = redisTemplate.opsForValue().multiGet(keys); + int idx = 0; + for (Long id : ids) { + Object o = sendPos.get(idx); + Integer sendMaxId = Objects.isNull(o) ? -1 : (Integer) o; + vos.stream().filter(vo -> vo.getGroupId().equals(id)).forEach(vo -> { + if (vo.getId() <= sendMaxId) { + // 已推送过 + vo.setStatus(MessageStatus.SENDED.code()); + } else { + // 未推送 + vo.setStatus(MessageStatus.UNSEND.code()); + } + }); + idx++; } + return vos; + } + /** + * 消息已读,同步其他终端,清空未读数量 + * + * @param groupId 群聊 + */ + @Override + public void readedMessage(Long groupId) { + UserSession session = SessionContext.getSession(); + // 推送消息给自己的其他终端 + GroupMessageVO msgInfo = new GroupMessageVO(); + msgInfo.setType(MessageType.READED.code()); + msgInfo.setSendTime(new Date()); + msgInfo.setSendId(session.getUserId()); + msgInfo.setGroupId(groupId); + IMGroupMessage sendMessage = new IMGroupMessage<>(); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); + sendMessage.setSendToSelf(true); + sendMessage.setData(msgInfo); + sendMessage.setSendResult(false); + imClient.sendGroupMessage(sendMessage); } /** * 拉取历史聊天记录 * * @param groupId 群聊id - * @param page 页码 - * @param size 页码大小 + * @param page 页码 + * @param size 页码大小 * @return 聊天记录列表 */ @Override public List findHistoryMessage(Long groupId, Long page, Long size) { - page = page > 0 ? page:1; - size = size > 0 ? size:10; + page = page > 0 ? page : 1; + size = size > 0 ? size : 10; Long userId = SessionContext.getSession().getUserId(); - long stIdx = (page-1)* size; + long stIdx = (page - 1) * size; // 群聊成员信息 - GroupMember member = groupMemberService.findByGroupAndUserId(groupId,userId); - if(member == null || member.getQuit()){ - throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊中"); + GroupMember member = groupMemberService.findByGroupAndUserId(groupId, userId); + if (member == null || member.getQuit()) { + throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊中"); } // 查询聊天记录,只查询加入群聊时间之后的消息 QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.lambda().eq(GroupMessage::getGroupId,groupId) - .gt(GroupMessage::getSendTime,member.getCreatedTime()) + wrapper.lambda().eq(GroupMessage::getGroupId, groupId) + .gt(GroupMessage::getSendTime, member.getCreatedTime()) .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()) .orderByDesc(GroupMessage::getId) - .last("limit "+stIdx + ","+size); + .last("limit " + stIdx + "," + size); List messages = this.list(wrapper); - List messageInfos = messages.stream().map(m->BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList()); - log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}",userId,groupId,messageInfos.size()); + List messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList()); + log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}", userId, groupId, messageInfos.size()); return messageInfos; } 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 0093db6..7de2679 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 @@ -2,6 +2,7 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bx.imclient.IMClient; @@ -9,6 +10,7 @@ import com.bx.imcommon.contant.IMConstant; import com.bx.imcommon.model.IMPrivateMessage; import com.bx.imcommon.model.IMUserInfo; import com.bx.implatform.entity.Friend; +import com.bx.implatform.util.DateTimeUtils; import com.bx.implatform.vo.PrivateMessageVO; import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.enums.MessageStatus; @@ -25,6 +27,7 @@ import com.bx.implatform.dto.PrivateMessageDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.Date; @@ -40,6 +43,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl sendMessage = new IMPrivateMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setRecvId(msgInfo.getRecvId()); sendMessage.setSendToSelf(true); sendMessage.setData(msgInfo); @@ -99,7 +103,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl sendMessage = new IMPrivateMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setRecvId(msgInfo.getRecvId()); sendMessage.setSendToSelf(false); sendMessage.setData(msgInfo); @@ -147,7 +151,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl friends = friendService.findFriendByUserId(session.getUserId()); - if(friends.isEmpty()){ + if (friends.isEmpty()) { return; } List friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList()); // 获取当前用户所有未读消息 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(PrivateMessage::getRecvId, session.getUserId()) - .eq(PrivateMessage::getStatus, MessageStatus.UNREAD) - .in(PrivateMessage::getSendId,friendIds); + .eq(PrivateMessage::getStatus, MessageStatus.UNSEND) + .in(PrivateMessage::getSendId, friendIds); List messages = this.list(queryWrapper); // 上传至redis,等待推送 - for(PrivateMessage message:messages){ + for (PrivateMessage message : messages) { PrivateMessageVO msgInfo = BeanUtils.copyProperties(message, PrivateMessageVO.class); // 推送消息 IMPrivateMessage sendMessage = new IMPrivateMessage<>(); - sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal())); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setRecvId(session.getUserId()); sendMessage.setRecvTerminals(Collections.singletonList(session.getTerminal())); sendMessage.setSendToSelf(false); @@ -183,4 +186,82 @@ public class PrivateMessageServiceImpl extends ServiceImpl loadMessage(Long minId) { + UserSession session = SessionContext.getSession(); + List friends = friendService.findFriendByUserId(session.getUserId()); + if (friends.isEmpty()) { + return Collections.EMPTY_LIST; + } + List friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList()); + // 获取当前用户的消息 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + // 只能拉取最近6个月的 + Date minDate = DateTimeUtils.addMonths(new Date(), -1); + queryWrapper.gt(PrivateMessage::getId, minId) + .ge(PrivateMessage::getSendTime, minDate) + .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()) + .and(wrap -> wrap.and( + wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()) + .in(PrivateMessage::getRecvId, friendIds)) + .or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId()) + .in(PrivateMessage::getSendId, friendIds))) + .last("limit 100"); + + List messages = this.list(queryWrapper); + // 更新发送状态 + List ids = messages.stream() + .filter(m -> !m.getSendId().equals(session.getUserId()) && m.getStatus().equals(MessageStatus.UNSEND.code())) + .map(PrivateMessage::getId) + .collect(Collectors.toList()); + if (!ids.isEmpty()) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.in(PrivateMessage::getId, ids) + .set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); + this.update(updateWrapper); + } + log.info("拉取消息,用户id:{},数量:{}", session.getUserId(), messages.size()); + return messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class)).collect(Collectors.toList()); + } + + + /** + * 消息已读,将整个会话的消息都置为已读状态 + * + * @param friendId 好友id + */ + @Transactional + @Override + public void readedMessage(Long friendId) { + UserSession session = SessionContext.getSession(); + // 推送消息 + PrivateMessageVO msgInfo = new PrivateMessageVO(); + msgInfo.setType(MessageType.READED.code()); + msgInfo.setSendTime(new Date()); + msgInfo.setSendId(session.getUserId()); + msgInfo.setRecvId(friendId); + IMPrivateMessage sendMessage = new IMPrivateMessage<>(); + sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); + sendMessage.setRecvId(friendId); + sendMessage.setSendToSelf(true); + sendMessage.setData(msgInfo); + sendMessage.setSendResult(false); + imClient.sendPrivateMessage(sendMessage); + // 修改消息状态为已读 + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.eq(PrivateMessage::getSendId,friendId) + .eq(PrivateMessage::getRecvId,session.getUserId()) + .eq(PrivateMessage::getStatus,MessageStatus.SENDED.code()) + .set(PrivateMessage::getStatus,MessageStatus.READED.code()); + this.update(updateWrapper); + log.info("消息已读,接收方id:{},发送方id:{}", session.getUserId(),friendId); + } } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java index 72480e8..f00cccc 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java @@ -2,6 +2,7 @@ package com.bx.implatform.vo; import com.bx.imcommon.serializer.DateToLongSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Date; @@ -9,34 +10,25 @@ import java.util.Date; @Data public class GroupMessageVO { - /* - * 消息id - */ + @ApiModelProperty(value = "消息id") private Long id; - /* - * 群聊id - */ + @ApiModelProperty(value = "群聊id") private Long groupId; - /* - * 发送者id - */ + @ApiModelProperty(value = " 发送者id") private Long sendId; - /* - * 消息内容 - */ + @ApiModelProperty(value = "消息内容") private String content; - /* - * 消息内容类型 具体枚举值由应用层定义 - */ + @ApiModelProperty(value = "消息内容类型 具体枚举值由应用层定义") private Integer type; - /** - * 发送时间 - */ + @ApiModelProperty(value = " 状态") + private Integer status; + + @ApiModelProperty(value = "发送时间") @JsonSerialize(using = DateToLongSerializer.class) private Date sendTime; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java index 1b7c41a..431dc9a 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java @@ -2,41 +2,35 @@ package com.bx.implatform.vo; import com.bx.imcommon.serializer.DateToLongSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Date; @Data +@ApiModel("私聊消息VO") public class PrivateMessageVO { - /* - * 消息id - */ + @ApiModelProperty(value = " 消息id") private long id; - /* - * 发送者id - */ + @ApiModelProperty(value = " 发送者id") private Long sendId; - /* - * 接收者id - */ + @ApiModelProperty(value = " 接收者id") private Long recvId; - /* - * 发送内容 - */ + @ApiModelProperty(value = " 发送内容") private String content; - /* - * 消息内容类型 IMCmdType - */ + @ApiModelProperty(value = "消息内容类型 IMCmdType") private Integer type; - /** - * 发送时间 - */ + @ApiModelProperty(value = " 状态") + private Integer status; + + @ApiModelProperty(value = " 发送时间") @JsonSerialize(using = DateToLongSerializer.class) private Date sendTime; } diff --git a/im-ui/src/api/emotion.js b/im-ui/src/api/emotion.js index 89fc18c..86af031 100644 --- a/im-ui/src/api/emotion.js +++ b/im-ui/src/api/emotion.js @@ -15,6 +15,9 @@ let transform = (content) => { let textToImg = (emoText) => { let word = emoText.replace(/\#|\;/gi, ''); let idx = emoTextList.indexOf(word); + if(idx==-1){ + return ""; + } let url = require(`@/assets/emoji/${idx}.gif`); return `` } diff --git a/im-ui/src/api/enums.js b/im-ui/src/api/enums.js index 27bfb78..d5f2390 100644 --- a/im-ui/src/api/enums.js +++ b/im-ui/src/api/enums.js @@ -6,6 +6,7 @@ const MESSAGE_TYPE = { AUDIO:3, VIDEO:4, RECALL:10, + READED:11, TIP_TIME:20, RTC_CALL: 101, RTC_ACCEPT: 102, @@ -27,8 +28,17 @@ const TERMINAL_TYPE = { APP: 1 } +const MESSAGE_STATUS = { + UNSEND: 0, + SENDED: 1, + RECALL:2, + READED:3 +} + + export { MESSAGE_TYPE, USER_STATE, - TERMINAL_TYPE + TERMINAL_TYPE, + MESSAGE_STATUS } diff --git a/im-ui/src/components/chat/ChatBox.vue b/im-ui/src/components/chat/ChatBox.vue index 6a7e33c..f8a6261 100644 --- a/im-ui/src/components/chat/ChatBox.vue +++ b/im-ui/src/components/chat/ChatBox.vue @@ -48,10 +48,10 @@
+ placeholder="温馨提示:可以粘贴截图到这里了哦~"> -
-
+
+
@@ -120,7 +120,6 @@ }, methods: { handlePaste(e) { - console.log(e); let txt = event.clipboardData.getData('Text') if (typeof(txt) == 'string') { this.sendText += txt @@ -172,7 +171,8 @@ sendTime: new Date().getTime(), selfSend: true, type: 1, - loadStatus: "loading" + loadStatus: "loading", + status: this.$enums.MESSAGE_STATUS.UNSEND } // 填充对方id this.fillTargetId(msgInfo, this.chat.targetId); @@ -202,7 +202,7 @@ }) }, handleFileFail(e, file) { - + let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); msgInfo.loadStatus = 'fail'; this.$store.commit("insertMessage", msgInfo); @@ -221,7 +221,8 @@ sendTime: new Date().getTime(), selfSend: true, type: 2, - loadStatus: "loading" + loadStatus: "loading", + status: this.$enums.MESSAGE_STATUS.UNSEND } // 填充对方id this.fillTargetId(msgInfo, this.chat.targetId); @@ -256,7 +257,6 @@ this.showVoice = false; }, showVideoBox() { - console.log(this.friend) this.$store.commit("showChatPrivateVideoBox", { friend: this.friend, master: true @@ -280,11 +280,11 @@ method: 'post', data: msgInfo }).then((id) => { - this.$message.success("发送成功"); msgInfo.id = id; msgInfo.sendTime = new Date().getTime(); msgInfo.sendId = this.$store.state.userStore.userInfo.id; msgInfo.selfSend = true; + msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND; this.$store.commit("insertMessage", msgInfo); // 保持输入框焦点 this.$refs.sendBox.focus(); @@ -323,7 +323,7 @@ this.handleImageSuccess(res, file); }) this.sendImageFile = null; - this.sendImageUrl= ""; + this.sendImageUrl = ""; this.$nextTick(() => this.$refs.sendBox.focus()); this.scrollToBottom(); }, @@ -344,12 +344,12 @@ method: 'post', data: msgInfo }).then((id) => { - this.$message.success("发送成功"); this.sendText = ""; msgInfo.id = id; msgInfo.sendTime = new Date().getTime(); msgInfo.sendId = this.$store.state.userStore.userInfo.id; msgInfo.selfSend = true; + msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND; this.$store.commit("insertMessage", msgInfo); }).finally(() => { // 解除锁定 @@ -390,10 +390,24 @@ msgInfo = JSON.parse(JSON.stringify(msgInfo)); msgInfo.type = 10; msgInfo.content = '你撤回了一条消息'; + msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL; this.$store.commit("insertMessage", msgInfo); }) }); - + }, + readedMessage() { + if(this.chat.type == "GROUP"){ + var url = `/message/group/readed?groupId=${this.chat.targetId}` + }else{ + url = `/message/private/readed?friendId=${this.chat.targetId}` + } + this.$http({ + url: url, + method: 'put' + }).then(() => { + this.$store.commit("resetUnreadCount",this.chat) + this.scrollToBottom(); + }) }, loadGroup(groupId) { this.$http({ @@ -420,7 +434,6 @@ method: 'get' }).then((friend) => { this.friend = friend; - console.log(this.friend) this.$store.commit("updateChatFromFriend", friend); this.$store.commit("updateFriend", friend); }) @@ -469,12 +482,16 @@ }, messageAction() { return `/message/${this.chat.type.toLowerCase()}/send`; + }, + unreadCount() { + return this.chat.unreadCount; } }, watch: { chat: { handler(newChat, oldChat) { - if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat.targetId)) { + if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat + .targetId)) { if (this.chat.type == "GROUP") { this.loadGroup(this.chat.targetId); } else { @@ -489,6 +506,14 @@ } }, immediate: true + }, + unreadCount: { + handler(newCount, oldCount) { + if(newCount > 0){ + // 消息已读 + this.readedMessage() + } + } } } } @@ -617,7 +642,6 @@ } .send-btn-area { - padding: 10px; position: absolute; bottom: 0; diff --git a/im-ui/src/components/chat/ChatMessageItem.vue b/im-ui/src/components/chat/ChatMessageItem.vue index 45d89b3..4fde6eb 100644 --- a/im-ui/src/components/chat/ChatMessageItem.vue +++ b/im-ui/src/components/chat/ChatMessageItem.vue @@ -1,11 +1,13 @@