diff --git a/db/im-platform.sql b/db/im-platform.sql index 575610f..20d1c71 100644 --- a/db/im-platform.sql +++ b/db/im-platform.sql @@ -31,6 +31,7 @@ create table `im_friend`( create table `im_private_message`( `id` bigint not null auto_increment primary key comment 'id', + `tmp_id` varchar(32) comment '临时id,由前端生成', `send_id` bigint not null comment '发送用户id', `recv_id` bigint not null comment '接收用户id', `content` text comment '发送内容', @@ -73,6 +74,7 @@ create table `im_group_member`( create table `im_group_message`( `id` bigint not null auto_increment primary key comment 'id', + `tmp_id` varchar(32) comment '临时id,由前端生成', `group_id` bigint not null comment '群id', `send_id` bigint not null comment '发送用户id', `send_nick_name` varchar(255) DEFAULT '' comment '发送用户昵称', @@ -106,5 +108,5 @@ CREATE TABLE `im_file_info` ( `upload_time` datetime DEFAULT CURRENT_TIMESTAMP comment '上传时间', `is_permanent` tinyint DEFAULT 0 comment '是否永久文件', `md5` VARCHAR(64) NOT NULL comment '文件md5', - UNIQUE KEY `idx_md5` (md5) + KEY `idx_md5` (md5) ) ENGINE = InnoDB CHARSET = utf8mb4 comment '文件'; \ No newline at end of file diff --git a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java index 7793598..e19c8aa 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -53,6 +53,17 @@ public final class RedisKey { */ public static final String IM_REPEAT_SUBMIT = "im:repeat:submit"; + /** + * 分布式锁-添加好友 + */ + public static final String IM_LOCK_FRIEND_ADD = "im:lock:friend:add"; + + /** + * 分布式锁-进入群聊 + */ + public static final String IM_LOCK_GROUP_ENTER = "im:lock:group:enter"; + + /** * 分布式锁-清理过期文件 */ diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java index 3b54fa0..76e6e5f 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java @@ -5,6 +5,7 @@ import com.bx.implatform.dto.FriendDndDTO; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.FriendService; +import com.bx.implatform.session.SessionContext; import com.bx.implatform.vo.FriendVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -34,7 +35,7 @@ public class FriendController { @PostMapping("/add") @Operation(summary = "添加好友", description = "双方建立好友关系") public Result addFriend(@NotNull(message = "好友id不可为空") @RequestParam Long friendId) { - friendService.addFriend(friendId); + friendService.addFriend(SessionContext.getSession().getUserId(),friendId); return ResultUtils.success(); } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java index 9f8e2c8..8b4f257 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java @@ -13,6 +13,9 @@ import java.util.List; @Schema(description = "群聊消息DTO") public class GroupMessageDTO { + @Schema(description = "临时id") + private String tmpId; + @NotNull(message = "群聊id不可为空") @Schema(description = "群聊id") private Long groupId; diff --git a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java index f954f93..639325c 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java @@ -10,11 +10,13 @@ import org.hibernate.validator.constraints.Length; @Schema(description = "私聊消息DTO") public class PrivateMessageDTO { + @Schema(description = "临时id") + private String tmpId; + @NotNull(message = "接收用户id不可为空") @Schema(description = "接收用户id") private Long recvId; - @Length(max = 1024, message = "内容长度不得大于1024") @NotEmpty(message = "发送内容不可为空") @Schema(description = "发送内容") diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java index a0ec72e..77a038a 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java @@ -25,6 +25,12 @@ public class GroupMessage { @TableId private Long id; + /** + * 临时id,由前端生成 + * 作用:如果用户正在发送消息时掉线了,可以通过此字段获取该消息的实际发送状态 + */ + private String tmpId; + /** * 群id */ diff --git a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java index cc605f1..8c1dd52 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java @@ -22,6 +22,12 @@ public class PrivateMessage { */ private Long id; + /** + * 临时id,由前端生成 + * 作用:如果用户正在发送消息时掉线了,可以通过此字段获取该消息的实际发送状态 + */ + private String tmpId; + /** * 发送用户id */ 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 68769d2..73197c1 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 @@ -6,13 +6,13 @@ import lombok.AllArgsConstructor; public enum MessageStatus { /** - * 文件 + * 等待推送(未送达) */ - UNSEND(0, "未送达"), + PENDING(0, "等待推送"), /** - * 文件 + * 已送达(未读) */ - SENDED(1, "送达"), + DELIVERED(1, "已送达"), /** * 撤回 */ 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 b59db7a..d75ab86 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 @@ -41,8 +41,8 @@ public class PrivateMessageListener implements MessageListener if(CollUtil.isNotEmpty(messageIds)){ UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.lambda().in(PrivateMessage::getId, messageIds) - .eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code()) - .set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); + .eq(PrivateMessage::getStatus, MessageStatus.PENDING.code()) + .set(PrivateMessage::getStatus, MessageStatus.DELIVERED.code()); privateMessageService.update(updateWrapper); } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/FriendService.java b/im-platform/src/main/java/com/bx/implatform/service/FriendService.java index 282a090..d25a850 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/FriendService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/FriendService.java @@ -43,9 +43,10 @@ public interface FriendService extends IService { /** * 添加好友,互相建立好友关系 * + * @param userId 用户id * @param friendId 好友的用户id */ - void addFriend(Long friendId); + void addFriend(Long userId,Long friendId); /** * 删除好友,双方都会解除好友关系 diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java index efca8e5..150bc2c 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java @@ -25,6 +25,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.DigestUtils; import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Date; import java.util.Objects; @@ -63,7 +66,7 @@ public class FileServiceImpl extends ServiceImpl imple } // 如果文件已存在,直接复用 String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); - FileInfo fileInfo = findByMd5(md5); + FileInfo fileInfo = findByMd5(md5, FileType.FILE.code()); if (!Objects.isNull(fileInfo)) { // 更新上传时间 fileInfo.setUploadTime(new Date()); @@ -101,9 +104,15 @@ public class FileServiceImpl extends ServiceImpl imple throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法"); } UploadImageVO vo = new UploadImageVO(); + // 获取图片长度和宽度 + BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); + if(!Objects.isNull(bufferedImage)){ + vo.setWidth(bufferedImage.getWidth()); + vo.setHeight(bufferedImage.getHeight()); + } // 如果文件已存在,直接复用 String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); - FileInfo fileInfo = findByMd5(md5); + FileInfo fileInfo = findByMd5(md5, FileType.IMAGE.code()); if (!Objects.isNull(fileInfo)) { // 更新上传时间和持久化标记 fileInfo.setIsPermanent(isPermanent || fileInfo.getIsPermanent()); @@ -131,7 +140,7 @@ public class FileServiceImpl extends ServiceImpl imple vo.setThumbUrl(generUrl(FileType.IMAGE, thumbFileName)); // 保存文件信息 saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), isPermanent); - }else{ + } else { // 小于50k,用原图充当缩略图 vo.setThumbUrl(generUrl(FileType.IMAGE, fileName)); // 保存文件信息,由于缩略图不允许删除,此时原图也不允许删除 @@ -157,9 +166,10 @@ public class FileServiceImpl extends ServiceImpl imple }; } - private FileInfo findByMd5(String md5) { + private FileInfo findByMd5(String md5, Integer fileType) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(FileInfo::getMd5, md5); + wrapper.eq(FileInfo::getFileType, fileType); return getOne(wrapper); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java index bbe6204..e0b10ad 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java @@ -10,6 +10,7 @@ import com.bx.imclient.IMClient; import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.model.IMPrivateMessage; import com.bx.imcommon.model.IMUserInfo; +import com.bx.implatform.annotation.RedisLock; import com.bx.implatform.contant.RedisKey; import com.bx.implatform.dto.FriendDndDTO; import com.bx.implatform.entity.Friend; @@ -75,10 +76,10 @@ public class FriendServiceImpl extends ServiceImpl impleme return friends.stream().map(this::conver).collect(Collectors.toList()); } + @RedisLock(prefixKey = RedisKey.IM_LOCK_FRIEND_ADD, key = "#userId+':'+#friendId") @Transactional(rollbackFor = Exception.class) @Override - public void addFriend(Long friendId) { - long userId = SessionContext.getSession().getUserId(); + public void addFriend(Long userId, Long friendId) { if (friendId.equals(userId)) { throw new GlobalException("不允许添加自己为好友"); } @@ -233,7 +234,7 @@ public class FriendServiceImpl extends ServiceImpl impleme msg.setRecvId(friendId); msg.setContent("你们已成为好友,现在可以开始聊天了"); msg.setSendTime(new Date()); - msg.setStatus(MessageStatus.UNSEND.code()); + msg.setStatus(MessageStatus.PENDING.code()); msg.setType(MessageType.TIP_TEXT.code()); privateMessageMapper.insert(msg); // 推给对方 @@ -257,7 +258,7 @@ public class FriendServiceImpl extends ServiceImpl impleme msg.setRecvId(friendId); msg.setSendTime(new Date()); msg.setType(MessageType.TIP_TEXT.code()); - msg.setStatus(MessageStatus.UNSEND.code()); + msg.setStatus(MessageStatus.PENDING.code()); msg.setContent("你们的好友关系已被解除"); privateMessageMapper.insert(msg); // 推送 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 50cd4cb..5830074 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 @@ -42,7 +42,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Slf4j @@ -123,7 +122,7 @@ public class GroupMessageServiceImpl extends ServiceImpl atIds = CommaTextUtils.asList(m.getAtUserIds()); vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList())); // 填充状态 - vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.UNSEND.code()); + vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.PENDING.code()); // 针对回执消息填充已读人数 if (m.getReceipt()) { if (Objects.isNull(maxIdMap)) { diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index f5ab43d..34daed1 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -10,6 +10,7 @@ import com.bx.imclient.IMClient; import com.bx.imcommon.model.IMGroupMessage; import com.bx.imcommon.model.IMUserInfo; import com.bx.imcommon.util.CommaTextUtils; +import com.bx.implatform.annotation.RedisLock; import com.bx.implatform.contant.Constant; import com.bx.implatform.contant.RedisKey; import com.bx.implatform.dto.GroupDndDTO; @@ -251,6 +252,7 @@ public class GroupServiceImpl extends ServiceImpl implements }).collect(Collectors.toList()); } + @RedisLock(prefixKey = RedisKey.IM_LOCK_GROUP_ENTER,key = "#dto.getGroupId()") @Override public void invite(GroupInviteDTO dto) { UserSession session = SessionContext.getSession(); @@ -330,7 +332,7 @@ public class GroupServiceImpl extends ServiceImpl implements GroupMessage message = new GroupMessage(); message.setContent(content); message.setType(MessageType.TIP_TEXT.code()); - message.setStatus(MessageStatus.UNSEND.code()); + message.setStatus(MessageStatus.PENDING.code()); message.setSendTime(new Date()); message.setSendNickName(session.getNickName()); message.setGroupId(groupId); 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 fa88b16..d0336b8 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 @@ -57,7 +57,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(PrivateMessage::getSendId, friendId).eq(PrivateMessage::getRecvId, session.getUserId()) - .eq(PrivateMessage::getStatus, MessageStatus.SENDED.code()) + .eq(PrivateMessage::getStatus, MessageStatus.DELIVERED.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/service/impl/WebrtcPrivateServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcPrivateServiceImpl.java index 2b93199..374c740 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcPrivateServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcPrivateServiceImpl.java @@ -51,12 +51,12 @@ public class WebrtcPrivateServiceImpl implements WebrtcPrivateService { webrtcSession.setMode(mode); // 校验 if (!imClient.isOnline(uid)) { - this.sendActMessage(webrtcSession, MessageStatus.UNSEND, "未接通"); + this.sendActMessage(webrtcSession, MessageStatus.PENDING, "未接通"); log.info("对方不在线,uid:{}", uid); throw new GlobalException("对方目前不在线"); } if (userStateUtils.isBusy(uid)) { - this.sendActMessage(webrtcSession, MessageStatus.UNSEND, "未接通"); + this.sendActMessage(webrtcSession, MessageStatus.PENDING, "未接通"); log.info("对方正忙,uid:{}", uid); throw new GlobalException("对方正忙"); } @@ -171,7 +171,7 @@ public class WebrtcPrivateServiceImpl implements WebrtcPrivateService { // 通知对方取消会话 imClient.sendPrivateMessage(sendMessage); // 生成通话消息 - sendActMessage(webrtcSession, MessageStatus.UNSEND, "已取消"); + sendActMessage(webrtcSession, MessageStatus.PENDING, "已取消"); } @Override diff --git a/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java index 27c8853..44712d9 100644 --- a/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java +++ b/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java @@ -52,7 +52,7 @@ public class GroupBannedConsumerTask extends RedisMQConsumer { msg.setContent(tip); msg.setSendId(Constant.SYS_USER_ID); msg.setSendTime(new Date()); - msg.setStatus(MessageStatus.UNSEND.code()); + msg.setStatus(MessageStatus.PENDING.code()); msg.setSendNickName("系统管理员"); msg.setType(MessageType.TIP_TEXT.code()); groupMessageService.save(msg); diff --git a/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java index 67b3993..ec7c2da 100644 --- a/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java +++ b/im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java @@ -51,7 +51,7 @@ public class GroupUnbanConsumerTask extends RedisMQConsumer { msg.setContent("已解除封禁"); msg.setSendId(Constant.SYS_USER_ID); msg.setSendTime(new Date()); - msg.setStatus(MessageStatus.UNSEND.code()); + msg.setStatus(MessageStatus.PENDING.code()); msg.setSendNickName("系统管理员"); msg.setType(MessageType.TIP_TEXT.code()); groupMessageService.save(msg); 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 ea41bde..9613a1f 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 @@ -14,6 +14,9 @@ public class GroupMessageVO { @Schema(description = "消息id") private Long id; + @Schema(description = "临时id") + private String tmpId; + @Schema(description = "群聊id") private Long groupId; 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 a1bbebe..7777a5f 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 @@ -14,6 +14,9 @@ public class PrivateMessageVO { @Schema(description = " 消息id") private Long id; + @Schema(description = "临时id") + private String tmpId; + @Schema(description = " 发送者id") private Long sendId; diff --git a/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java index f58114f..671fe9a 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java @@ -12,4 +12,10 @@ public class UploadImageVO { @Schema(description = "缩略图") private String thumbUrl; + + @Schema(description = "图片宽度") + private int width; + + @Schema(description = "图片高度") + private int height; } diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index e9264ca..1bfe6a7 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -9,7 +9,6 @@ import UNI_APP from '@/.env.js' export default { data() { return { - isInit: false, // 是否已经初始化 isExit: false, // 是否已退出 audioTip: null, reconnecting: false // 正在重连标志 @@ -23,8 +22,7 @@ export default { this.loadStore().then(() => { // 初始化websocket this.initWebSocket(); - this.isInit = true; - }).catch((e) => { + }).catch(e => { console.log(e); this.exit(); }) @@ -40,7 +38,7 @@ export default { // 加载离线消息 this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId); this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId); - + this.configStore.setAppInit(true); } }); wsApi.onMessage((cmd, msgInfo) => { @@ -66,7 +64,7 @@ export default { console.log("ws断开", res); // 重新连接 this.reconnectWs(); - + this.configStore.setAppInit(false); }) }, loadStore() { @@ -92,7 +90,11 @@ export default { url: "/message/private/pullOfflineMessage?minId=" + minId, method: 'GET' }).catch(() => { - this.chatStore.setLoadingPrivateMsg(false) + uni.showToast({ + title: "消息拉取失败,请重新登陆", + icon: 'none' + }) + this.exit() }) }, pullGroupOfflineMessage(minId) { @@ -101,7 +103,11 @@ export default { url: "/message/group/pullOfflineMessage?minId=" + minId, method: 'GET' }).catch(() => { - this.chatStore.setLoadingGroupMsg(false) + uni.showToast({ + title: "消息拉取失败,请重新登陆", + icon: 'none' + }) + this.exit() }) }, handlePrivateMessage(msg) { @@ -421,6 +427,7 @@ export default { // 加载离线消息 this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId); this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId); + this.configStore.setAppInit(true); }).catch((e) => { console.log(e); this.exit(); diff --git a/im-uniapp/common/enums.js b/im-uniapp/common/enums.js index 3ddd09c..fa7ada0 100644 --- a/im-uniapp/common/enums.js +++ b/im-uniapp/common/enums.js @@ -54,10 +54,12 @@ const TERMINAL_TYPE = { } const MESSAGE_STATUS = { - UNSEND: 0, - SENDED: 1, - RECALL: 2, - READED: 3 + FAILED: -2, // 发送失败 + SENDING: -1, // 发送中(消息没到服务器) + PENDING: 0, // 未送达(消息已到服务器,但对方没收到) + DELIVERED: 1, // 已送达(对方已收到,但是未读消息) + RECALL: 2, // 已撤回 + READED: 3, // 消息已读 } export { diff --git a/im-uniapp/common/wssocket.js b/im-uniapp/common/wssocket.js index f0aaafe..3f03c9f 100644 --- a/im-uniapp/common/wssocket.js +++ b/im-uniapp/common/wssocket.js @@ -65,9 +65,9 @@ let connect = (wsurl, token) => { }) socketTask.onError((e) => { - console.log(e) + console.log("ws错误:",e) + close(); isConnect = false; - // APP 应用切出超过一定时间(约1分钟)会触发报错,此处回调给应用进行重连 closeCallBack && closeCallBack({ code: 1006 }); }) } diff --git a/im-uniapp/components/chat-item/chat-item.vue b/im-uniapp/components/chat-item/chat-item.vue index 9c9b1c3..2b7fc18 100644 --- a/im-uniapp/components/chat-item/chat-item.vue +++ b/im-uniapp/components/chat-item/chat-item.vue @@ -3,7 +3,7 @@ - + @@ -45,7 +45,7 @@ export default { methods: { showChatBox() { // 初始化期间进入会话会导致消息不刷新 - if (!getApp().$vm.isInit || this.chatStore.isLoading()) { + if (!this.configStore.appInit || this.chatStore.isLoading()) { uni.showToast({ title: "正在初始化页面,请稍后...", icon: 'none' @@ -79,6 +79,9 @@ export default { return ""; }, isTextMessage() { + if (this.chat.messages.length == 0) { + return false; + } let idx = this.chat.messages.length - 1; let messageType = this.chat.messages[idx].type; return messageType == this.$enums.MESSAGE_TYPE.TEXT; @@ -86,6 +89,13 @@ export default { nodesText() { let text = this.$str.html2Escape(this.chat.lastContent); return this.$emo.transform(text, 'emoji-small') + }, + online() { + if (this.chat.type == 'PRIVATE') { + let friend = this.friendStore.findFriend(this.chat.targetId); + return friend && friend.online; + } + return false; } } } diff --git a/im-uniapp/components/chat-message-item/chat-message-item.vue b/im-uniapp/components/chat-message-item/chat-message-item.vue index 88d6db8..f95e34a 100644 --- a/im-uniapp/components/chat-message-item/chat-message-item.vue +++ b/im-uniapp/components/chat-message-item/chat-message-item.vue @@ -14,51 +14,54 @@ {{ showName }} - - - - - - - - - - - - - - - - - - - - - - - - {{ fileSize }} + + + + + + + + + + + + + + + - - + + + + + + + + {{ fileSize }} + + + + + + + + + + {{ contentData.duration + '"' }} + + - - - - - - {{ JSON.parse(msgInfo.content).duration + '"' }} - - + + - + + {{ msgInfo.content }} - - 已读 - 未读 + + 已读 + 未读 @@ -117,16 +118,13 @@ export default { }, methods: { onSendFail() { - uni.showToast({ - title: "该文件已发送失败,目前不支持自动重新发送,建议手动重新发送", - icon: "none" - }) + this.$emit("resend", this.msgInfo); }, onPlayAudio() { // 初始化音频播放器 if (!this.innerAudioContext) { this.innerAudioContext = uni.createInnerAudioContext(); - let url = JSON.parse(this.msgInfo.content).url; + let url = this.contentData.url; this.innerAudioContext.src = url; this.innerAudioContext.onEnded((e) => { console.log('停止') @@ -157,7 +155,7 @@ export default { this.menu.show = false; }, onShowFullImage() { - let imageUrl = JSON.parse(this.msgInfo.content).originUrl; + let imageUrl = this.contentData.originUrl; uni.previewImage({ urls: [imageUrl] }) @@ -177,17 +175,17 @@ export default { } }, computed: { - loading() { - return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "loading"; + sending() { + return this.msgInfo.status == this.$enums.MESSAGE_STATUS.SENDING; }, - loadFail() { - return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "fail"; + sendFail() { + return this.msgInfo.status == this.$enums.MESSAGE_STATUS.FAILED; }, - data() { + contentData() { return JSON.parse(this.msgInfo.content) }, fileSize() { - let size = this.data.size; + let size = this.contentData.size; if (size > 1024 * 1024) { return Math.round(size / 1024 / 1024) + "M"; } @@ -227,6 +225,9 @@ export default { } return items; }, + isTextMessage() { + return this.msgInfo.type == this.$enums.MESSAGE_TYPE.TEXT + }, isAction() { return this.$msgType.isAction(this.msgInfo.type); }, @@ -239,6 +240,22 @@ export default { let text = this.$str.html2Escape(this.msgInfo.content) text = this.$url.replaceURLWithHTMLLinks(text, color) return this.$emo.transform(text, 'emoji-normal') + }, + imageStyle() { + console.log(uni.getSystemInfo()) + let maxSize = uni.getSystemInfoSync().windowWidth * 0.6; + let minSize = uni.getSystemInfoSync().windowWidth * 0.2; + let width = this.contentData.width; + let height = this.contentData.height; + if (width && height) { + let ratio = Math.min(width, height) / Math.max(width, height); + let w = Math.max(width > height ? maxSize : ratio * maxSize, minSize); + let h = Math.max(width > height ? ratio * maxSize : maxSize, minSize); + return `width: ${w}px;height:${h}px;` + } else { + // 兼容历史版本,历史数据没有记录宽高 + return `max-width: ${maxSize}px;min-width:100px;max-height: ${maxSize}px;min-height:100px;` + } } } } @@ -285,116 +302,136 @@ export default { padding-right: 80rpx; margin-top: 5rpx; - .message-text { + .message-content-wrapper { position: relative; - line-height: 1.6; - margin-top: 10rpx; - padding: 16rpx 24rpx; - background-color: $im-bg; - border-radius: 20rpx; - color: $im-text-color; - font-size: $im-font-size; - text-align: left; - display: block; - word-break: break-all; - white-space: pre-line; - - &:after { - content: ""; - position: absolute; - left: -20rpx; - top: 26rpx; - width: 6rpx; - height: 6rpx; - border-style: solid dashed dashed; - border-color: $im-bg transparent transparent; - overflow: hidden; - border-width: 18rpx; - } - } - - .message-image { display: flex; - flex-wrap: nowrap; - flex-direction: row; align-items: center; - .image-box { + .sending { position: relative; + margin: 0 6rpx; - .send-image { - min-width: 200rpx; - max-width: 420rpx; - height: 350rpx; - cursor: pointer; - border-radius: 4px; + .icon-loading { + color: $im-color-primary; } } .send-fail { - color: $im-color-danger; - font-size: $im-font-size; - cursor: pointer; - margin: 0 20px; + color: #e60c0c; + font-size: 50rpx; + margin: 0 5rpx; } - } - .message-file { - display: flex; - flex-wrap: nowrap; - flex-direction: row; - align-items: center; - cursor: pointer; - - .file-box { + .message-text { position: relative; + line-height: 1.6; + margin-top: 10rpx; + padding: 16rpx 24rpx; + background-color: $im-bg; + border-radius: 20rpx; + color: $im-text-color; + font-size: $im-font-size; + text-align: left; + display: block; + word-break: break-word; + white-space: pre-line; + + &:after { + content: ""; + position: absolute; + left: -20rpx; + top: 26rpx; + width: 6rpx; + height: 6rpx; + border-style: solid dashed dashed; + border-color: $im-bg transparent transparent; + overflow: hidden; + border-width: 18rpx; + } + } + + .message-image { display: flex; flex-wrap: nowrap; + flex-direction: row; align-items: center; - min-height: 60px; - border-radius: 4px; - padding: 10px 15px; - box-shadow: $im-box-shadow-dark; - - .file-info { - flex: 1; - height: 100%; - text-align: left; - font-size: 14px; - width: 300rpx; - - .file-name { - font-weight: 600; - margin-bottom: 15px; - word-break: break-all; + + .image-box { + position: relative; + + .send-image { + cursor: pointer; + border-radius: 10rpx; + background: $im-bg; + border: 6rpx solid $im-color-primary-light-5; } } - .file-icon { - font-size: 80rpx; - color: #d42e07; + .send-fail { + color: $im-color-danger; + font-size: $im-font-size; + cursor: pointer; + margin: 0 20px; } } - .send-fail { - color: #e60c0c; - font-size: 50rpx; + .message-file { + display: flex; + flex-wrap: nowrap; + flex-direction: row; + align-items: center; cursor: pointer; - margin: 0 20rpx; - } - } - .message-audio { - display: flex; - align-items: center; + .file-box { + position: relative; + display: flex; + flex-wrap: nowrap; + align-items: center; + min-height: 60px; + border-radius: 4px; + padding: 10px 15px; + box-shadow: $im-box-shadow-dark; + + .file-info { + flex: 1; + height: 100%; + text-align: left; + font-size: 14px; + width: 300rpx; + + .file-name { + font-weight: 600; + margin-bottom: 15px; + word-break: break-all; + } + } - .chat-audio-text { - padding-right: 8px; + .file-icon { + font-size: 80rpx; + color: #d42e07; + } + } + + .send-fail { + color: #e60c0c; + font-size: 50rpx; + cursor: pointer; + margin: 0 20rpx; + } } - .icon-voice-play { - font-size: 18px; - padding-right: 8px; + .message-audio { + display: flex; + align-items: center; + + .chat-audio-text { + padding-right: 8px; + } + + .icon-voice-play { + font-size: 18px; + padding-right: 8px; + } } } @@ -454,6 +491,10 @@ export default { padding-left: 80rpx; padding-right: 0; + .message-content-wrapper { + flex-direction: row-reverse; + } + .message-text { margin-left: 10px; background-color: $im-color-primary-light-2; @@ -466,14 +507,6 @@ export default { } } - .message-image { - flex-direction: row-reverse; - } - - .message-file { - flex-direction: row-reverse; - } - .message-audio { flex-direction: row-reverse; diff --git a/im-uniapp/components/head-image/head-image.vue b/im-uniapp/components/head-image/head-image.vue index 3e4210a..9fa8bc2 100644 --- a/im-uniapp/components/head-image/head-image.vue +++ b/im-uniapp/components/head-image/head-image.vue @@ -16,7 +16,8 @@ export default { data() { return { colors: ["#5daa31", "#c7515a", "#e03697", "#85029b", - "#c9b455", "#326eb6"] + "#c9b455", "#326eb6" + ] } }, props: { @@ -34,6 +35,10 @@ export default { type: String, default: null }, + radius: { + type: String, + default: "50%" + }, online: { type: Boolean, default: false @@ -61,7 +66,7 @@ export default { 'minier': 48, 'lage': 108, 'lager': 120, - }[this.size] + } [this.size] } }, avatarImageStyle() { @@ -71,11 +76,15 @@ export default { avatarTextStyle() { return `width: ${this._size}rpx; height:${this._size}rpx; - background-color:${this.name ? this.textColor : '#fff'}; - font-size:${this._size * 0.5}rpx; + background: linear-gradient(145deg,#ffffff20 25%,#00000060),${this.textColor}; + font-size:${this._size * 0.45}rpx; + border-radius: ${this.radius}; ` }, textColor() { + if (!this.name) { + return '#fff'; + } let hash = 0; for (var i = 0; i < this.name.length; i++) { hash += this.name.charCodeAt(i); diff --git a/im-uniapp/components/loading/loading.vue b/im-uniapp/components/loading/loading.vue index a56b8ea..b0f0c98 100644 --- a/im-uniapp/components/loading/loading.vue +++ b/im-uniapp/components/loading/loading.vue @@ -6,7 +6,6 @@ -