站内信(我的消息)业务在我司的实践
文章目录
- 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;
}
站内信(我的消息)业务在我司的实践相关推荐
- 基于workerman实现的web消息推送站内信功能
流程说明 使用 web-msg-sender 作为 服务器监听程序. 客户端(浏览器)通过websocket连接 服务器监听程序. 服务器应用程序(后端) 通过curl访问 服务器监听程序,将需要推送 ...
- mysql群发消息_百万级用户量的站内信群发数据库设计
随着WEB2.0的发展,用户之间的信息交互也变得十分庞大,而且实时性要求越来越高.现在很多SNS网站和一部分CMS网站都广泛地应用了站内信这一模块,这个看似简单的东西其实背后隐藏着很多需要设计师重视的 ...
- mysql群发消息_分享网站群发站内信数据库表设计
本文和大家分享一下网站站内信实现表设计的功能.需要的朋友可以参考下. "站内信"不同于电子邮件,电子邮件通过专门的邮件服务器发送.保存.而"站内信"是系统内的消 ...
- 单系统站内信数据库设计思路
第一版设计 需求 :单用户之间通信(融合了用户反馈需求) 数据库设计:Message内容和收发者存在一张表中 message表: 这里一条Message存两次,类似邮件服务. status:已读.未读 ...
- ASP.NET 实现站内信功能(点对点发送,管理员群发)
正好这段时间在研究这个功能,还是得感谢这位大神,没有他的引路,我就不可能把站内信做出来. http://www.cnblogs.com/grenet/archive/2010/03/08/168065 ...
- 单系统站内信设计概述(满足百万级信息)
基本功能 点到点的消息传送: 用户给用户 管理员给用户 点到面的消息传送 管理员给用户群 少量用户(10-999) 对于用户非常少的情况,没有必要深入的考虑数据库的优化,采用简单的表设计: 如表mes ...
- 网站系统 群发“站内信”的实现
在很多网站系统(如CMS系统,SNS系统等),都有"站内信"的功能. "站内信"不同于电子邮件,电子邮件通过专门的邮件服务器发送.保存.而"站内信&q ...
- 两年后,再议“站内信”的实现
两年前,万仓一黍在博客园发了两篇关于站内信的设计实现博文,<群发"站内信"的实现>.<群发"站内信"的实现(续)>,其中阐述了他关于站内 ...
- 百万级用户量的站内信设计
1. 方案描述 该方案用于系统站内信功能模块在百万级用户量情况下的效率问题,只是后台管理员给前台用户发送站内信,用户与用户之间的发送不在讨论内. 2. 方案详情 假设系统的用户量达到了200W,活跃用 ...
最新文章
- SPOJ 375. Query on a tree (树链剖分)
- Bytom BIP-32协议和BIP-44协议解读
- 【转载】Deep learning:十九(RBM简单理解)
- [SOJ] 简单哈希
- 除了《深入理解 Java 虚拟机》,还可以看怎么系统学习 Java 虚拟机?
- python下载安装教程2.7-Linux下安装python-2.7
- java开发支持类库
- P3868 [TJOI2009]猜数字(CRT板子)
- CSS:给 input 中 type=text 设置CSS样式
- 【深度学习】caffe 中的一些参数介绍
- 树莓派测试USB摄像头是否可用
- 教你js生成二维码-QrCodeJS
- pool.map()爬取美文网标题内容
- 惠普计算机如何用u盘引导启动不了系统安装系统,惠普笔记本进BIOS设置U盘启动教程...
- mysql多对多表设计_数据库怎么设计多对多的数据表
- java中的URLEncoder和URLDecoder类
- 【Codecs系列】CABAC熵编码详解
- 13 MATLAB 三维图形绘制
- maglev hash算法
- 调用聚合数据平台天气接口