站内信(我的消息)业务在我司的实践

0.需求分析和设计

现在的需求是需要保存用户的消息,比如老师给学生布置了任务,老师给学生批改了作业,都需要给学生发送消息至我的消息(有的地方也称站内信)
,学生可以查看我的消息,也可以点击消息跳转进指定的app页面,h5页面或详情页面。

这里设计时主要有两个表,消息模板表和消息记录表。用到的主要是redis和mq消息队列,用mq是因为某些消息是直接发送给全员或者推送给某些班级的,受众很多,mq可以削封解耦;使用redis可以加快客户端的查询速度,同时数据也需要持久化到消息发送记录表。

这里有一个点就是点击消息可能会跳转,所以需要与前端约定参数的格式,是否有跳转,有跳转是跳到h5页面,还是app的某个模块还是自身详情,跳转自身详情还需要带资源id过去。

当时技术还不够,后面思考这里可以优化,就是消息发送记录表数据增长很快,假如有500万用户,一天发送1/50,10万,一个月就是300万,一年就将近4000,mysql查询就会非常慢了,所以分库分表势在必行,一开始如果就这么设计的话,就不用再考虑那些存量数据的初始化了。

下面说具体的实现

1.设计消息模板

消息模板表需要的字段是:

CREATE TABLE `message_base` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `platform_type` tinyint(1) NOT NULL COMMENT '平台类型(0教师端,1学生端)',
  `name` varchar(255) NOT NULL COMMENT '模板名称',
  `base_body` text NOT NULL COMMENT '消息体模板json串(模板+对应位置的值)',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8mb4

实际插入的模板如下:

insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('1','0','ER_SEASON_START','{\"baseBody\":\"新学期阅读能力测评已开启,通知同学们赶快完成吧!\",\"items\":[]}','2018-07-23 17:45:24');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('2','0','CLASS_TASK_DEADLINE','{\"baseBody\":\"您给{{0}}布置的{{1}}已到截止时间,检查一下同学们的完成情况吧~\",\"items\":[{\"value\":\"className\"},{\"value\":\"taskTypeName\"}]}','2018-07-23 17:46:48');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('3','0','CLASS_TASK_ALL_FINISH','{\"baseBody\":\"您给{{0}}布置的{{1}},学生已全部完成,检查一下同学们的完成情况吧~\",\"items\":[{\"value\":\"className\"},{\"value\":\"taskTypeName\"}]}','2018-07-23 17:51:42');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('4','0','CLASS_TRANSFER_REQUEST','{\"baseBody\":\"{{0}}老师申请将{{1}}转让给您,是否接收该班级?\",\"items\":[{\"value\":\"fromName\"},{\"value\":\"className\"}]}','2018-07-23 17:54:45');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('5','0','CLASS_TRANSFER_ACCEPT','{\"baseBody\":\"{{0}}已成功转让给{{1}}老师\",\"items\":[{\"value\":\"className\"},{\"value\":\"toName\"}]}','2018-07-23 17:58:04');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('6','0','CLASS_TRANSFER_REJECT','{\"baseBody\":\"{{0}}老师拒绝接受{{1}}\",\"items\":[{\"value\":\"toName\"},{\"value\":\"className\"}]}','2018-07-23 18:00:41');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('7','0','STUDENT_JOIN_CLASS','{\"baseBody\":\"{{0}}同学申请加入班级,是否同意?\",\"items\":[{\"value\":\"studentName\"}]}','2018-07-23 18:04:03');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('8','0','STUDENT_LEAVE_CLASS','{\"baseBody\":\"{{0}}同学主动退出了班级\",\"items\":[{\"value\":\"studentName\"}]}','2018-07-23 19:00:51');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('9','0','TEACHERSDAY_REWARD_FLOWER','{\"baseBody\":\"过去一天您共收到了{{0}}朵鲜花,去看看排名变化吧!\",\"items\":[{\"value\":\"flowerNum\"}]}','2018-08-17 15:48:52');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('26','0','TEACHERSDAY_ACTIVITY_START','{\"baseBody\":\"考拉阅读准备了100朵小红花,祝您教师节快乐!快来领取吧!\",\"items\":[]}','2018-08-17 15:48:52');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('27','1','JOIN_CLASS_REQUEST_GRANTED','{\"baseBody\":\"你已成功加入【{{0}}】【{{1}}年{{2}}班】\",\"items\":[{\"value\":\"school\"},{\"value\":\"grade\"},{\"value\":\"class\"}]}','2018-08-18 15:00:14');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('28','1','JOIN_CLASS_REQUEST_REJECTED','{\"baseBody\":\"你申请加入{{0}}老师管理班级失败,请重新申请~~\",\"items\":[{\"value\":\"teacherName\"}]}','2018-08-18 15:18:27');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('29','1','BIND_CHILD_ACCOUNT','{\"baseBody\":\"家长微信已成功绑定你的账号,会时刻关心你的进步哦~\",\"items\":[]}','2018-08-19 12:08:42');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('30','1','UNBIND_CHILD_ACCOUNT','{\"baseBody\":\"你的【家长】解除了与你考拉账号的微信绑定~\",\"items\":[]}','2018-08-19 12:20:26');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('31','1','REMOVE_FROM_CLASS','{\"baseBody\":\"你已经被【{{0}}老师】移出班级\",\"items\":[{\"value\":\"teacherName\"}]}','2018-08-19 12:32:49');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('32','1','READING_TASK_REMIND','{\"baseBody\":\"【《{{0}}》】还未完成,快去挑战它!\",\"items\":[{\"value\":\"metaName\"}]}','2018-08-19 14:01:05');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('33','1','ER_TEST_NOTIFICATION','{\"baseBody\":\"你的ER测评任务未完成,快去挑战吧!\",\"items\":[]}','2018-08-19 14:29:13');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('34','1','ER_REPORT_GENERATED','{\"baseBody\":\"你的考拉阅读能力测评报告已经生成,快去查看吧…\",\"items\":[]}','2018-08-19 14:38:42');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('35','1','ER_NEW_CREATE','{\"baseBody\":\"您有新的er测试任务。快去挑战\",\"items\":[]}','2018-08-19 14:46:53');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('36','1','CLASS_DISABLE','{\"baseBody\":\"你所在的班级已被老师停用\",\"items\":[]}','2018-08-19 20:23:09');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('37','1','CLASS_RESTART','{\"baseBody\":\"你所在班级已被老师重新启用\",\"items\":[]}','2018-08-19 20:23:35');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('38','1','NEW_READING_TASK_ASSIGN','{\"baseBody\":\"【《{{0}}》】已添加到你的阅读作业,赶快去阅读吧~\",\"items\":[{\"value\":\"metaName\"}]}','2018-08-19 20:47:23');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('39','1','NOTE_LIKED_BY_TEACHER','{\"baseBody\":\"【{{0}}老师】赞了你的读书笔记\",\"items\":[{\"value\":\"teacherName\"}]}','2018-08-19 20:56:34');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('40','1','NOTE_COMMENT_BY_TEACHER','{\"baseBody\":\"【{{0}}老师】评论了你的读书笔记\",\"items\":[{\"value\":\"teacherName\"}]}','2018-08-19 20:56:50');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('41','1','NEW_BOOK_PUBLISH','{\"baseBody\":\"考拉阅读图书馆新增了【{{0}}】本书,请快去戳戳看吧~\",\"items\":[{\"value\":\"bookCount\"}]}','2018-08-20 14:59:20');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('42','1','BOOK_COMMENT_BE_LIKED','{\"baseBody\":\"【{{0}}】给你点赞咯~快去瞅一眼\",\"items\":[{\"value\":\"operatorName\"}]}','2018-08-20 15:54:15');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('43','1','NEW_CLASSMATE_JOIN','{\"baseBody\":\"【{{0}}】同学加入咱们班了!快去看看\",\"items\":[{\"value\":\"classmateName\"}]}','2018-08-20 16:40:48');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('44','1','MEDAL_GOLD_RETROSPECTIVE','{\"baseBody\":\"你可能有金币需要补领哦,快去勋章卡牌页面看看吧\",\"items\":[]}','2018-09-11 14:41:16');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('45','1','READ_FLOWER','{\"baseBody\":\"有人给你的朗读作品送花咯~\",\"items\":[]}','2018-10-19 17:20:58');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('46','1','READ_COMMENT','{\"baseBody\": \"有人评论你的朗读作品咯~\",\"items\":[]}','2018-10-19 17:21:27');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('47','1','READ_TASK_ASSIGN','{\"baseBody\":\"《{{0}}》朗读课文任务已添加到你的任务列表了,快去挑战吧~\",\"items\":[{\"value\":\"textName\"}]}','2018-10-23 20:27:38');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('48','0','SUBMIT_VIRTUAL_ORDER','{\"baseBody\":\"您的 {{0}} 商品已兑换成功!请到【我的订单】-【订单详情】查看您的兑换码\",\"items\":[{\"value\":\"goodsName\"}]}','2018-10-15 15:48:52');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('49','0','SUBMIT_REAL_ORDER','{\"baseBody\":\"您的 {{0}} 商品已兑换成功!我们会尽快发货到您填写的收货地址\",\"items\":[{\"value\":\"goodsName\"}]}','2018-10-15 15:48:52');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('50','1','PUSH_MESSAGE','{\"baseBody\":\"{{0}}\",\"items\":[{\"value\":\"pushContent\"}]}','2018-11-30 00:59:55');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('51','1','REMIND_STUDENT_DO_TASK','{\"baseBody\":\"你还有未完成的阅读任务哦\",\"items\":[]}','2018-11-30 00:59:55');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('52','1','PASSAGE_ISLAND_REMIND','{\"baseBody\":\"快来短文星球挑战大魔王~挑战成功会得到丰厚的奖励哦\",\"items\":[]}','2018-11-30 00:59:55');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('53','1','DAILY_TASKS_REMIND','{\"baseBody\":\"你的每日任务已刷新,坚持做任务,赢取赛季大奖!\",\"items\":[]}','2018-11-30 00:59:55');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('54','0','TEACHER_FINISH_ACTIVITY','{\"baseBody\":\"恭喜您完成活动条件,您有{{0}}积分奖励待领取,您可以在【首页】- 【活动中心】处领取您的积分奖励\",\"items\":[{\"value\":\"point\"}]}','2018-12-09 22:08:54');
insert into `message_base` (`id`, `platform_type`, `name`, `base_body`, `create_time`) values('55','0','TEACHER_CASH_REWARD','{\"baseBody\":\"恭喜您完成活动,本次活动奖励您{{0}}积分,您可以在【我的】- 【我的积分】处查看您的积分余额\",\"items\":[{\"value\":\"point\"}]}','2018-12-09 22:12:52');

使用时根据name查出消息模板,然后可以放置在redis中,加快查询速度。

2.设置消息跳转参数

不同的消息是可以设置跳向不同的页面的,可以不跳转,可以跳向h5页面,可以跳向原生app模块,也可以跳转至自身详情,跳转至自身详情时还需要带着资源id的。所以这一步就设置跳转的参数信息。

@Data
@Accessors(chain = true)
public class MessageArgsBody<T> {

    /**
     *消息参数体
     */
    private T t;

    /**
     * 发送者头像
     */
    private String senderAvatar;

    /**
     *跳转类型
     */
    private Integer jumpType;

    /**
     *h5页面url
     */
    private String pageUrl;

    /**
     *跳转模块编号
     */
    private Integer moduleNo;

    /**
     *资源id,app跳转至模块详情页时需要
     */
    private Long resourceId;
}

消息参数体是一个泛型类型,消息生产者传入来的对象,里面可能包含消息的发送者id,消息的接收者id,要跳转的资源id等信息。
根据不同的消息,设置不同的跳转模块和编号。

跳转的枚举如下:

public enum JumpType {
    NONE(0,"无跳转"),
    DETAIL(1,"跳转至自身详情"),
    H5_URL(2,"跳转至指定h5 url"),
    APP_MODULE(3,"跳转至app指定模块");

    private int code;

    private String description;

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    JumpType(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public static JumpType findByCode(int code){
        for (JumpType jumpType : JumpType.values()) {
            if(jumpType.getCode() == code){
                return jumpType;
            }
        }
        return null;
    }
}

3.保存消息至redis和数据库

消息的保存模式是最近20条消息保存至redis中的list结构中,并异步持久化到消息发送记录表中,保存消息的核心代码如下:

    @WithTransaction(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
    public void consumeStudentPoetryReciteTask(StudentPoetryReciteTaskMessage studentPoetryReciteTaskMessage) throws JSONException {
        MessageBase poetryReciteTaskAssign = teacherMessageService.getMessageBaseByName(StudentMessageSeriesEnum.POETRY_RECITE_TASK_ASSIGN.name());
        UserVO teacher = usersProxy.getUserById(studentPoetryReciteTaskMessage.getFromId(),false);
        if (null != poetryReciteTaskAssign) {
            JSONObject baseObject = new JSONObject(poetryReciteTaskAssign.getBaseBody());
            baseObject.getJSONArray("items").getJSONObject(0).put("value", studentPoetryReciteTaskMessage.getMetaName());
            MessageArgsBody<StudentPoetryReciteTaskMessage> messageArgsBody = new MessageArgsBody<StudentPoetryReciteTaskMessage>()
                    .setT(studentPoetryReciteTaskMessage)
                    .setSenderAvatar(teacher.getHeadimg())
                    .setJumpType(JumpType.APP_MODULE.getCode())
                    .setModuleNo(StudentClientModuleNo.HOME_WORK.getCode());
            StudentMessageRecord studentMessageRecord = new StudentMessageRecord()
                    .setFromId(studentPoetryReciteTaskMessage.getFromId())
                    .setMessageType(StudentMessageType.RECOMMEND_AND_NOTIFICATION.getCode())
                    .setMessageBaseName(StudentMessageSeriesEnum.POETRY_RECITE_TASK_ASSIGN.name())
                    .setMessageBody(baseObject.toString())
                    .setArgsBody(JsonUtils.writeObjectAsString(messageArgsBody))
                    .setCreateTime(new Date())
                    .setUserId(studentPoetryReciteTaskMessage.getStudentId())
                    .setStatus(StudentMessageStatus.UNCHECK.getCode());
                    
            //保存到redis中        
            String key = StudentMessageConstant.getUserBoxKey(studentPoetryReciteTaskMessage.getStudentId(), StudentMessageType.RECOMMEND_AND_NOTIFICATION.getCode());
            RedisUtils.addFixedLengthList(key, StudentMessageConstant.MESSAGE_LIST_MAXSIZE, Collections.singletonList(studentMessageRecord));

            //异步持久化到数据库中
            StudentMessageAddMessage studentMessageAddMessage = new StudentMessageAddMessage();
            BeanUtils.copyProperties(studentMessageRecord, studentMessageAddMessage);
            studentMessageAddProducer.send(studentMessageAddMessage);
            log.info("StudentPoetryReciteTaskMessage Received 古詩背誦作业发布");
        }
    }
    

消息记录表结构如下:


CREATE TABLE `student_message_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `message_type` tinyint(3) DEFAULT NULL COMMENT '消息类型(0系统消息,1老师提醒、阅读提醒)',
  `from_id` bigint(20) DEFAULT NULL COMMENT '发起人id',
  `user_id` bigint(20) NOT NULL COMMENT '老师id',
  `message_body` tinytext NOT NULL COMMENT '消息体',
  `message_base_name` varchar(64) NOT NULL COMMENT '模板名称',
  `args_body` text COMMENT '参数体',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '0未读,1已读',
  `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0未删除,1已删除',
  `check_time` datetime DEFAULT NULL COMMENT '阅读消息时间',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1612 DEFAULT CHARSET=utf8mb4;

数据示例:

INSERT INTO `messages`.`student_message_record` (`id`, `message_type`, `from_id`, `user_id`, `message_body`, `message_base_name`, `args_body`, `status`, `is_delete`, `check_time`, `create_time`, `update_time`) VALUES ('1609', '1', '12345', '505', '{\"baseBody\":\"老师对你完成的古诗默写任务《{{0}}》,进行了评论,快去看看吧\",\"items\":[{\"value\":\"静夜思\"}]}', 'POETRY_WRITE_COMMENT', '{\"t\":{\"messageBody\":\"\",\"fromId\":12345,\"studentId\":505,\"workRecordId\":112051635,\"metaName\":\"静夜思\"},\"senderAvatar\":null,\"jumpType\":3,\"pageUrl\":null,\"moduleNo\":3301,\"resourceId\":112051635}', '0', '0', NULL, '2019-03-29 18:12:00', '2019-03-29 18:11:59');
INSERT INTO `messages`.`student_message_record` (`id`, `message_type`, `from_id`, `user_id`, `message_body`, `message_base_name`, `args_body`, `status`, `is_delete`, `check_time`, `create_time`, `update_time`) VALUES ('1608', '1', '236', '8401', '{\"baseBody\":\"【《{{0}}》】已加入你的任务,快去完成吧~\",\"items\":[{\"value\":\"测试课文3\"}]}', 'NEW_READING_TASK_ASSIGN', '{\"t\":{\"bookId\":100037,\"reporterId\":236,\"studentId\":8401,\"startTime\":1552924800000,\"endTime\":1553097599000,\"taskId\":112051633,\"classId\":56,\"metaName\":\"测试课文3\",\"timeStamp\":1552924800000},\"senderAvatar\":\"https://koalareading-test.oss-cn-beijing.aliyuncs.com/users/headimg/default.png\",\"jumpType\":3,\"pageUrl\":null,\"moduleNo\":1800,\"resourceId\":null}', '0', '0', NULL, '2019-03-19 10:43:02', '2019-03-19 10:43:01');

注意:这里两个优化的点
1.就是消息发送记录表数据量可能会非常大,最好是分库分表,否则轻轻松松就可以上几千万,关于分库分表可以看我其它文章。

2.因为消息内容有一部分是在redis中存储着的,redis服务器非常贵的,所以要优化保存在里面的数据量,最好是可以存储模板名称或编号之类的,再根据模板名称或编号取消息体,这点有点类似于es,就是只存最主要的信息,次要信息从redis中取出主要信息再去mysql或者hbase去取。

4.客户端查询消息列表

查询系统消息或用户消息消息列表,从redis中获取数据:

    @AuthRequired({
            AuthRequired.AuthType.Student
    })
    @RequestMapping(method = RequestMethod.GET, value = "/list")
    public Response<List<StudentMessageVO>> list(
            @RequestParam Integer type
    ){
        String key;
        int size;
        if(type == StudentMessageType.RECOMMEND_AND_NOTIFICATION.getCode()){
            key = StudentMessageConstant.getUserBoxKey(getAuthCookieItem().getUserId(),type);
            size = StudentMessageConstant.MESSAGE_LIST_MAXSIZE;
        }else if(type == StudentMessageType.SYSTEM.getCode()){
            key = StudentMessageConstant.getNoticeBoxKey();
            size = StudentMessageConstant.SYSTEM_MESSAGE_LIST_MAXSIZE;
        }else{
            return new Response<>().error("param error");
        }
        List<StudentMessageRecord> studentMessageRecords = RedisUtils.getFixedLengthList(key, size, new TypeReference<StudentMessageRecord>() {});
        List<StudentMessageVO> studentMessageVOS = new ArrayList<>(studentMessageRecords.size());
        studentMessageRecords.forEach(studentMessageRecord -> {
            StudentMessageVO studentMessageVO = new StudentMessageVO();
            JSONObject argsBody = JSONObject.parseObject(studentMessageRecord.getArgsBody());
            JSONObject messageBody = JSONObject.parseObject(studentMessageRecord.getMessageBody());
            String baseBody = messageBody.getString("baseBody");
            JSONArray itemArray = messageBody.getJSONArray("items");
            for(int i=0; i<itemArray.size(); i++){
                if(itemArray.getJSONObject(i).containsKey("value")){
                    baseBody = baseBody.replace("{{"+i+"}}",itemArray.getJSONObject(i).getString("value"));
                }
            }
            studentMessageVO.setCreateTime(studentMessageRecord.getCreateTime().getTime())
                    .setType(type)
                    .setMessageBody(baseBody)
                    .setFromId(studentMessageRecord.getFromId())
                    .setSenderAvatar(argsBody.getString("senderAvatar"))
                    .setJumpType(argsBody.getInteger("jumpType"))
                    .setModuleNo(argsBody.getInteger("moduleNo"))
                    .setPageUrl(argsBody.getString("pageUrl"))
                    .setResourceId(argsBody.getLong("resourceId"))
                    .setStatus(argsBody.getInteger("status"));
            studentMessageVOS.add(studentMessageVO);
        });
        return new Response<List<StudentMessageVO>>().ok(studentMessageVOS);
    }

将消息从redis中拿出后进行组装,返给前端,VO对象如下:

@Data
@Accessors(chain = true)
public class StudentMessageVO {
    /**
     * 发起人id
     */
    private Long fromId;

    /**
     * 发送者头像
     */
    private String senderAvatar;

    /**
     * 消息类别(0系统消息,1老师阅读提醒)
     */
    private Integer type;

    /**
     * 消息体
     */
    private String messageBody;

    /**
     * 跳转类型
     */
    private Integer jumpType;

    /**
     * 跳转h5地址
     */
    private String pageUrl;

    /**
     * 跳转模块编号
     */
    private Integer moduleNo;

    /**
     * 跳转资源id
     */
    private Long resourceId;

    /**
     * 状态:0未读,1已读
     */
    private Integer status;

    /**
     * 创建时间
     */
    private Long createTime;
}

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值