文章目录

  • 0.需求分析和设计
  • 1.设计消息模板
  • 2.设置消息跳转参数
  • 3.保存消息至redis和数据库
  • 4.客户端查询消息列表

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. 基于workerman实现的web消息推送站内信功能

    流程说明 使用 web-msg-sender 作为 服务器监听程序. 客户端(浏览器)通过websocket连接 服务器监听程序. 服务器应用程序(后端) 通过curl访问 服务器监听程序,将需要推送 ...

  2. mysql群发消息_百万级用户量的站内信群发数据库设计

    随着WEB2.0的发展,用户之间的信息交互也变得十分庞大,而且实时性要求越来越高.现在很多SNS网站和一部分CMS网站都广泛地应用了站内信这一模块,这个看似简单的东西其实背后隐藏着很多需要设计师重视的 ...

  3. mysql群发消息_分享网站群发站内信数据库表设计

    本文和大家分享一下网站站内信实现表设计的功能.需要的朋友可以参考下. "站内信"不同于电子邮件,电子邮件通过专门的邮件服务器发送.保存.而"站内信"是系统内的消 ...

  4. 单系统站内信数据库设计思路

    第一版设计 需求 :单用户之间通信(融合了用户反馈需求) 数据库设计:Message内容和收发者存在一张表中 message表: 这里一条Message存两次,类似邮件服务. status:已读.未读 ...

  5. ASP.NET 实现站内信功能(点对点发送,管理员群发)

    正好这段时间在研究这个功能,还是得感谢这位大神,没有他的引路,我就不可能把站内信做出来. http://www.cnblogs.com/grenet/archive/2010/03/08/168065 ...

  6. 单系统站内信设计概述(满足百万级信息)

    基本功能 点到点的消息传送: 用户给用户 管理员给用户 点到面的消息传送 管理员给用户群 少量用户(10-999) 对于用户非常少的情况,没有必要深入的考虑数据库的优化,采用简单的表设计: 如表mes ...

  7. 网站系统 群发“站内信”的实现

    在很多网站系统(如CMS系统,SNS系统等),都有"站内信"的功能. "站内信"不同于电子邮件,电子邮件通过专门的邮件服务器发送.保存.而"站内信&q ...

  8. 两年后,再议“站内信”的实现

    两年前,万仓一黍在博客园发了两篇关于站内信的设计实现博文,<群发"站内信"的实现>.<群发"站内信"的实现(续)>,其中阐述了他关于站内 ...

  9. 百万级用户量的站内信设计

    1. 方案描述 该方案用于系统站内信功能模块在百万级用户量情况下的效率问题,只是后台管理员给前台用户发送站内信,用户与用户之间的发送不在讨论内. 2. 方案详情 假设系统的用户量达到了200W,活跃用 ...

最新文章

  1. SPOJ 375. Query on a tree (树链剖分)
  2. Bytom BIP-32协议和BIP-44协议解读
  3. 【转载】Deep learning:十九(RBM简单理解)
  4. [SOJ] 简单哈希
  5. 除了《深入理解 Java 虚拟机》,还可以看怎么系统学习 Java 虚拟机?
  6. python下载安装教程2.7-Linux下安装python-2.7
  7. java开发支持类库
  8. P3868 [TJOI2009]猜数字(CRT板子)
  9. CSS:给 input 中 type=text 设置CSS样式
  10. 【深度学习】caffe 中的一些参数介绍
  11. 树莓派测试USB摄像头是否可用
  12. 教你js生成二维码-QrCodeJS
  13. pool.map()爬取美文网标题内容
  14. 惠普计算机如何用u盘引导启动不了系统安装系统,惠普笔记本进BIOS设置U盘启动教程...
  15. mysql多对多表设计_数据库怎么设计多对多的数据表
  16. java中的URLEncoder和URLDecoder类
  17. 【Codecs系列】CABAC熵编码详解
  18. 13 MATLAB 三维图形绘制
  19. maglev hash算法
  20. 调用聚合数据平台天气接口

热门文章

  1. 图灵对计算机行业的贡献论文,这届图灵奖得主究竟做了什么贡献?这篇1974年的论文给了我们答案...
  2. matlab实验报告的总结,一些数字信号处理实例(学校实验报告总结)
  3. cloudsim资料收集
  4. 如何用简单的方式将数组转成json
  5. Depthwise卷积与Pointwise卷积
  6. Xcode 14之大变化详细介绍
  7. 大型数据中心维保外包模式的选择
  8. 云弹性:它如何影响云计算?
  9. Nachos系统调用的实现
  10. Java比较两个数组是否相等(equals())