对话聊天设计

一、数据库设计

1、表1:user_message

主要用于消息内容的保存,发送方与接收方的信息保存

 2、表2:user_message_log

主要用于记录用户的聊天记录以及未读消息个数保存,用于后去聊天用户列表展示与移除,未读消息计数等

二、功能介绍

 用户发送消息 :/userapp/message/insertUserMessage
展示用户列表 :/userapp/message/selectUserList (返回用户信息和未读消息的条数)
移除用户列表:/userapp/message/removeUser
用户聊天详细信息:/userapp/message/selectUserMessage
用户撤回消息(三分钟之内可以撤回):/userapp/message/removeUserMessage
(*)、使用定时任务做到用户十分钟未读信息,发送短信提醒

 1、用户发送消息 :/userapp/message/insertUserMessage

主要业务代码如下 

@Override
    public ResultBody insertUserMessage(UserMessageVO messageVO,Integer senderUserId) {
        UserMessageDO userMessageDO = new UserMessageDO(messageVO);
        if(senderUserId == null){
            return ResultBody.success("消息发送失败,请先登录");
        }
        userMessageDO.setSenderUserId(senderUserId);
        if(messageDao.insertUserMessage(userMessageDO) != 0){
            UserAccountDO userAccount = userServiceDao.getUserAccountById(userMessageDO.getReceiverUserId());
            UserDO userDO = userAccount.buildUserDO();
            if(scheduledTaskUtil.check(userDO.getUserAccountId()) == 1){
                scheduledTaskUtil.create(userDO);
            }
            if(messageDao.isUserMessageLog(userMessageDO.getReceiverUserId(), userMessageDO.getSenderUserId()) == null){
                UserMessageLogDO userMessageLogDO = new UserMessageLogDO();
                userMessageLogDO.setSenderUserId(userMessageDO.getSenderUserId());
                userMessageLogDO.setReceiverUserId(userMessageDO.getReceiverUserId());
                messageDao.insertUserMessageLog(userMessageLogDO);
            }
            Integer logId = messageDao.isUserMessageLog(userMessageDO.getSenderUserId(),userMessageDO.getReceiverUserId());
            if(logId == null){
                UserMessageLogDO userMessageLogDO = new UserMessageLogDO();
                userMessageLogDO.setSenderUserId(userMessageDO.getReceiverUserId());
                userMessageLogDO.setReceiverUserId(userMessageDO.getSenderUserId());
                userMessageLogDO.setMessageCount(1);
                messageDao.insertUserMessageLog(userMessageLogDO);
            }else {
                messageDao.updateUserMessageLog(logId);
            }
            return ResultBody.success("消息发送成功");
        }
        return ResultBody.error("消息发送失败");
    }

 2、展示用户列表 :/userapp/message/selectUserList (返回用户信息和未读消息的条数)

主要业务代码如下

    @Override
    public ResultBody selectUserList(Integer senderUserId) {
        List<UserDO> userAccountDOList = new ArrayList<>();
        List<UserMessageLogDO> list = messageDao.selectUserList(senderUserId);
        for (UserMessageLogDO userMessageLogDO : list) {
            UserAccountDO userAccount = userServiceDao.getUserAccountById(userMessageLogDO.getReceiverUserId());
            UserDO userDO = userAccount.buildUserDO();
            userDO.setUnreadCount(userMessageLogDO.getMessageCount());
            UserMessageDO userMessageDO = messageDao.selectEndUserMessage(userMessageLogDO.getReceiverUserId(), senderUserId);
            if(userMessageDO.getContent() != null){
                userDO.setEndMessage(userMessageDO.getContent());
            }else {
                userDO.setEndMessage(userMessageDO.getImgUrl());
            }
            userDO.setCreateTime(userMessageDO.getCreateTime());
            userAccountDOList.add(userDO);
        }
        // 使用 Collections.sort() 方法和自定义的比较器进行排序
        Collections.sort(userAccountDOList, Comparator.comparing(UserDO::getCreateTime).reversed());
        return ResultBody.success(userAccountDOList);
    }

 3、用户聊天详细信息:/userapp/message/selectUserMessage

 主要业务代码如下

    @Override
    public PageResult selectUserMessage(MessageUserQO messageQO,Integer senderUserId) {
        scheduledTaskUtil.destroy(senderUserId);
        int count = messageDao.countUserMessage(messageQO.getReceiverUserId(),senderUserId);
        if (count == 0) {
            return PageResult.buildPage(messageQO.getPageNo(), messageQO.getPageSize(), count);
        }
        Integer pageNo = messageQO.getPageNo();
        messageQO.buildCurrentPage();
        List<UserMessageDO> messageList = messageDao.selectUserMessage(messageQO,senderUserId);
//        Integer logId = messageDao.isUserMessageLog(senderUserId,messageQO.getReceiverUserId());
//        if(logId != null){
//            messageDao.updateUserMessageCount(logId);
//        }
        Integer logId2 = messageDao.isUserMessageLog(messageQO.getReceiverUserId(),senderUserId);
        if(logId2 != null){
            messageDao.updateUserMessageCount(logId2);
        }
        return PageResult.buildPage(pageNo, messageQO.getPageSize(), count, messageList);
    }



<select id="selectUserMessage" resultType="com.mmc.iuav.user.entity.UserMessageDO">
        SELECT id, img_url, content, sender_user_id, receiver_user_id, create_time
        FROM (
                 SELECT id, img_url, content, sender_user_id, receiver_user_id, create_time
                 FROM user_message
                 WHERE is_deleted = 0 AND sender_user_id IN (#{senderUserId} ,#{messageQO.receiverUserId}) AND receiver_user_id IN (#{senderUserId} ,#{messageQO.receiverUserId})
                 ORDER BY create_time desc
                     LIMIT #{messageQO.pageNo},#{messageQO.pageSize}
             ) AS subquery
        ORDER BY create_time ASC;
    </select>

4、移除用户列表:/userapp/message/removeUser

 主要业务代码如下

    @Override
    public ResultBody removeMessageByUser(Integer userId, Integer messageId) {
        if(messageDao.removeReceiverMessage(userId, messageId) != 0){
            return ResultBody.success("删除成功");
        }
        return ResultBody.error("删除失败");
    }




    <update id="removeReceiverMessage">
        update receiver_message
        set is_deleted = 1, deleted_time = NOW()
        where   user_id = #{userId} and message_id = #{messageId}
    </update>

5、用户撤回消息(三分钟之内可以撤回):/userapp/message/removeUserMessage

  主要业务代码如下

    @Override
    public ResultBody removeUserMessage(Integer id) {
        UserMessageDO userMessageDO = messageDao.selectUserMessageById(id);
        Date createTime = userMessageDO.getCreateTime();
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTime);
        if(isOverThreeMinutes(time)){
            return ResultBody.error("消息发送已经超过三分钟,撤回失败");
        }
        if(messageDao.removeUserMessage(id) != 0){
            Integer logId = messageDao.isUserMessageLog(userMessageDO.getSenderUserId(),userMessageDO.getReceiverUserId());
            if(logId != null){
                messageDao.removeUserMessageCount(logId);
            }
            return ResultBody.success("消息撤回成功已撤回");
        }
        return ResultBody.error("消息撤回失败");
    }

public static boolean isOverThreeMinutes(String createTime) {
        // 定义日期时间格式化对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 将字符串格式的时间转换为 LocalDateTime 对象
        LocalDateTime createDateTime = LocalDateTime.parse(createTime, formatter);
        // 计算当前时间与创建时间的时间差(单位:秒)
        long secondsDiff = ChronoUnit.SECONDS.between(createDateTime, LocalDateTime.now());
        // 如果时间差大于 180 秒(即三分钟),返回 true
        return secondsDiff > 180;
    }

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
消息聊天系统MySQL表设计_聊天系统-数据库设计 //常⽤的redis命令 CONFIG SET requirepass "mypass" //Hashmap hset [key] [field] value] hget [key] [field] hgetall [key] //List LPUSH [key] [value] RPUSH [key] [value] LPOP [key] RPOP [key] //删除表头2个值为value的元素 LREM [key] [count > 0] [value] //删除表尾2个值为value的元素 LREM [key] [count < 0] [value] //删除所有值为value的元素 LREM [key] [count = 0] [value] LREM mylist 2 "hello" LREM mylist -2 "hello" //查询0~最后1个元素(不删除) LRANGE mylist 0 -1 BRPOP [key] [timeout] BLPOP [key] [timeout] //例:获取message_1最左的元素,如果没有,等待5秒,超时返回nil BLPOP message_1 5 采⽤Redis进⾏数据存储,主要包括频控、限流、⽤户表、在线⽤户表、聊天消息表(redis list实现消息队列)、好友表(TODO) 频控 CheckFrequency(userId uint64) bool 返回true检查通过,false触发频控 visited_{user_id} >3触发 //频控key前缀 const freqPre string = "visited_" /* Desc: 检查频控key Input: userId Output: False 触发频控/异常 True 正常请求 */ func CheckFrequency(userId uint64) bool { key := freqPre + strconv.FormatUint(userId, 10) resExists, err := Client.Exists(key).Result() if err != nil { return false } if resExists == 0 { //该key不存在 Client.Set(key, 0, 100 * time.Second) return true } res, err := Client.Get(key).Result() if err != nil { return false } resCnt, err := strconv.Atoi(res) if err != nil { return false } if resCnt > 2 {//10秒同⼀userid不能访问超过2次 fmt.Println("触发频控") //... return false } Client.Incr(key) return true } 频控测试 redis.CreateClient() var res bool = true for res { time.Sleep(time.Second * 1) res = redis.CheckFrequency(54508) fmt.Println(res) } 限流 /* 令牌桶算法API */ const LevelFast int = 1 const LevelMedium int = 2 const LevelSlow int = 3 var tokenBucket *ratelimit.Bucket func InitToken(speedLevel int) { var bucketFillDuring time.Duration if speedLevel == LevelFast { bucketFillDuring = 20 * time.Millisecond } else if speedLevel == LevelMedium { bucketFillDuring = 100 * time.Millisecond } else if speedLevel == LevelSlow { bucketFillDuring = 1000 * time.Millisecond } else { //⽇志 fmt.Println("speedLevel只能为Fast, Medium, Slow三个值!") return } var bucketMax int64 = 1//令牌桶最⼤容量,初始也会有这么多个令牌,此处为1是测试所⽤,⽅便看到限流效果,后期需增加⼤⼩ toke
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值