最近忙着做App,涉及到消息提醒的小红点功能,本文章不限编程语言,讲解大致的开发思路。
首先,请大家跟着我思路一起走,不要看得太快,保证您肯定满意,您也一定能实现您的需求。
我们无非从两个地方实现,一:前端,二:后端。先说说前端,我思前想后,给前端做缓存,确实实现了,但是我忽略了一点,就是服务器会挂掉,如果服务挂掉了,就不存在缓存了,那么一切都要重来。接着,我们再说说后端,既然缓存无法满足我们的需求,我们就必须要给它一个持久性,那么就涉及到数据库。
我们先想想,消息提醒,可能会有一条消息,可能会有多条消息,可能某条消息既给A用户,又要给B用户,那么怎么保证A用户读的消息,B用户不再去读,对于公共的消息,我们又怎么处理。很显然的一对多关系,就涉及两张数据库表而已。如下:
我们给这张表起名JPUSH_MESSAGE,消息推送表。您不用关心每个字段的含义。对于他的另一张表,如下:
我们给它起名字叫JPUSH_MESSAGE_SPLIT(消息推送拆分表)。您只需要记得他们两个的主键和IS_READ(是否已读)字段即可。我们的消息通常都会有两个界面:
一个是消息列表,带有小红点的界面:
另一个就是点击去之后的具体消息界面。为此,我们为了降低开发成本,用两个接口实现。第一个就是访问小红点的接口,取名“/jpushMessageRedDot”。另一个就是点进去之后的具体消息界面,取名“/jpushMessage”。
我们整理下思路:
1.用户进入消息列表,获取JPUSH_MESSAGE表里一堆和他有关的消息,然后根据他自己的userId(或是其他标识)去已读表JPUSH_MESSAGE_SPLIT里查询(这样只会查出和他自己相关的,免得查到其他人的),如果已读表里没有数据,说明他获取的这些数据都是未读过的。
2.如果已读表里有部分数据,那么就和他一进来所拿到的有关数据进行对比,当这些数据存在已读表里的呢,我们就过滤掉,不存在的,说明是未读过的(我们就把它们加入到已读表里,并打上自己的特殊标记,就是给IS_READ存入自己的userId,这句话等下在另一个方法中实现)就展示给用户。
3.当我们发现查询出的已读数据和一进来所获取的相关数据是一模一样的,说明,这些都是度过了的,那么,我们就要给其一条默认的已读过的数据。
4.别忘了,数据表初始化的时候肯定是空的,所以要提前对空表进行预知的判空处理。
好了,现在查询小红点逻辑目前是没问题的。那我们怎么样让小红点被点击后,变成已读呢?就涉及另一个接口了。
1.以分页的形式查出和用户有关的所有数据。可以把这些数据直接展示给用户,不必逻辑处理。(我的需求要分页)
2.去已读表里查出和自己有关的已读数据与分页查出的所有相关数据做对比,发现如果已读为空,说名这些都是未读的,把他们主键记住,插入已读表中。
3.发现已读表有部分数据,继续和上述分页的所有数据做对比,如果有部分主键没在已读表中,那么就能肯定,这些是新数据,就要将它存入已读表。
4.还是那个问题,初始化的表都是空的,记得预处理。
下面,看下我的代码,本文以Java演示:
@RequestMapping(value = "/jpushMessageRedDot", method = RequestMethod.POST)
@ResponseBody
public ResultMessage<Map<String, String>> jpushMessageRedDot() {
List<Map<String,String>> listdata = new ArrayList<>();
ResultMessage<Map<String, String>> result = new ResultMessage<>();
Map<String,String> mapdata = new HashMap<>();
List<String> repeatList = new ArrayList<>();
try {
TokenDomain token = getToken();
if(token != null){
String userId = String.valueOf(token.getUserId());//用户id
String compId = String.valueOf(token.getUser().getCompanyId());//公司id
/*String userId = "1886";//用户id
String compId = "1";//公司id
*/ //首先根据用户ID和公司ID来查询出当前用户的所有消息
JpushMessage jpushMessage = new JpushMessage();
jpushMessage.setEMPLOYEE_CODE(userId);
jpushMessage.setCOMPANY_ID(compId);
//根据用户ID和公司ID来查询出当前用户的所有消息
List<JpushMessage> selectJpushMessageByCondition = apiJpushMessageService.selectJpushMessageByCondition(jpushMessage);
//在写出根据JPUSH_MESSAGE_SPLIT表的IS_READ字段判断端该条数数据是否已读,由于数据是一对多的关系,所以需要关联表,IS_READ存的是userId
List<JpushMessage> selectJpushMessageOfIsRead = apiJpushMessageService.selectJpushMessageOfIsRead(jpushMessage);
if(selectJpushMessageOfIsRead.size() < 1){
//说明当前用户没有已读信息,那就把根据用户ID和公司ID的查询结果返回,如果有值就返回第一条给用户,无值就无值。
if(selectJpushMessageByCondition.size() > 0){
//当根据用户ID和公司ID来查询出当前用户的所有消息数量大于零时
for (int i = 0; i < selectJpushMessageByCondition.size(); i++) {
mapdata = new HashMap<>();
mapdata.put("messageTitle", selectJpushMessageByCondition.get(0).getMESSAGE_TITLE());
mapdata.put("messageContent", selectJpushMessageByCondition.get(0).getMESSAGE_CONTENT());
mapdata.put("messageCreateTime", selectJpushMessageByCondition.get(0).getMESSAGE_CREATE_TIME());
mapdata.put("unreadNum", String.valueOf(selectJpushMessageByCondition.size()));
listdata.add(mapdata);
}
}else{
//说明初始化表的时候就什么都没有
mapdata = new HashMap<>();
mapdata.put("messageTitle", "");
mapdata.put("messageContent", "");
mapdata.put("messageCreateTime", "");
mapdata.put("unreadNum", "0");
listdata.add(mapdata);
}
}else{
//此时说明当前用户有部分已读信息,可能读了一部分,还剩一部分,那就就要分清楚哪些已读,哪些未读,还要给公共的消息加上特殊标记(为此,我们用userId添加)
for (int i = 0; i < selectJpushMessageOfIsRead.size(); i++) {
//先把已知的读过了的主键存储起来
repeatList.add(selectJpushMessageOfIsRead.get(i).getJPUSH_MESSAGES_SPLIT_ID());
}
for (int i = 0; i < selectJpushMessageByCondition.size(); i++) {
JpushMessage totalData = selectJpushMessageByCondition.get(i);//总的数据
if(repeatList.contains(totalData.getID())){
//当已读表的主键在总数据表里时,就过滤掉,那么剩下的,必然是未读的
continue;
}else{
mapdata = new HashMap<>();
mapdata.put("messageTitle", totalData.getMESSAGE_TITLE());
mapdata.put("messageContent", totalData.getMESSAGE_CONTENT());
mapdata.put("messageCreateTime", totalData.getMESSAGE_CREATE_TIME());
mapdata.put("unreadNum", String.valueOf(selectJpushMessageByCondition.size() - repeatList.size()));
listdata.add(mapdata);
}
}
/*mapdata = listdata.get(0);*/
//此时,如果listdata里面仍然无值,说明已读的和总数据的持平了,就是没有未读的了,那么,就要取一条已读的数据来作为默认的展示
if(listdata.size() < 1){
mapdata = new HashMap<>();
mapdata.put("messageTitle", selectJpushMessageOfIsRead.get(0).getMESSAGE_TITLE());
mapdata.put("messageContent", selectJpushMessageOfIsRead.get(0).getMESSAGE_CONTENT());
mapdata.put("messageCreateTime", selectJpushMessageOfIsRead.get(0).getMESSAGE_CREATE_TIME());
mapdata.put("unreadNum", "0");
listdata.add(mapdata);
}
}
}
result.setCount(listdata.size());
result.setData(listdata.get(0));
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
上面的是小红点的接口。下面的是小红点点击进去的具体信息:
@RequestMapping(value = "/jpushMessage", method = RequestMethod.POST)
@ResponseBody
public ResultMessage<List<Map<String, String>>> jpushMessage(@ApiParam(value = "当前页码")@RequestParam("currentPage") int currentPage,
@ApiParam(value = "每页显示数量")@RequestParam("pageSize") int pageSize) {
/**
* 逻辑分析:该接口查询所有和当前用户有关的消息,并且用于消息展示。为了程序能区分出哪条消息已读,所以就要形成一对多的关系,并且给相应的数据加上特有的标记
* */
String startPage = String.valueOf(currentPage * pageSize - pageSize + 1);//开始页等于:当前页 * 页码大小 - 页码大小 + 1
String endPage = String.valueOf(currentPage * pageSize);//结束页等于:当前页 * 页码大小
List<Map<String,String>> listdata = new ArrayList<>();
ResultMessage<List<Map<String, String>>> result = new ResultMessage<>();
List<String> repeatList = new ArrayList<>();
try {
TokenDomain token = getToken();
if(token != null){
String userId = String.valueOf(token.getUserId());
String compId = String.valueOf(token.getUser().getCompanyId());
//查出所有和当前用户有关的消息
List<JpushMessage> selectJpushMessageOfPageForApp = apiJpushMessageService.selectJpushMessageOfPageForApp(startPage,endPage,userId,compId);
Map<String,String> mapdata = new HashMap<>();
if(selectJpushMessageOfPageForApp.size() > 0){
for (int i = 0; i < selectJpushMessageOfPageForApp.size(); i++) {
mapdata = new HashMap<>();
JpushMessage message = selectJpushMessageOfPageForApp.get(i);
mapdata.put("messageTitle", message.getMESSAGE_TITLE());
mapdata.put("messageContent", message.getMESSAGE_CONTENT());
mapdata.put("messageCreateTime", message.getMESSAGE_CREATE_TIME());
listdata.add(mapdata);
}
//到这里为止,listdata拿到了要给用户展示的数据
}else{
mapdata = new HashMap<>();
mapdata.put("messageTitle", "");
mapdata.put("messageContent", "");
mapdata.put("messageCreateTime", "");
listdata.add(mapdata);
}
//上面,用户已经拿到了要读取的数据,下面我们就要把这些已读的数据加标记,让程序区分出它被谁读过了,谁就不在显示小红点,谁没读,谁就应该显示小红点
JpushMessage jpushMessage = new JpushMessage();
jpushMessage.setCOMPANY_ID(compId);
jpushMessage.setEMPLOYEE_CODE(userId);
//写出根据JPUSH_MESSAGE_SPLIT表的IS_READ字段判断端该条数数据是否已读,由于数据是一对多的关系,所以需要关联表,IS_READ存的是userId
List<JpushMessage> selectJpushMessageOfIsRead = apiJpushMessageService.selectJpushMessageOfIsRead(jpushMessage);
//我们把这些已读的数据存在list里面,去和上面的所有数据做对比,用主键对比即可,如果某条数据在已读表中,就过滤掉,其余的就一定是未读的,然后将未读的主键记住存入已读表中
for (int i = 0; i < selectJpushMessageOfIsRead.size(); i++) {
repeatList.add(selectJpushMessageOfIsRead.get(i).getJPUSH_MESSAGES_SPLIT_ID());
}
for (int i = 0; i < selectJpushMessageOfPageForApp.size(); i++) {
String notReadId = selectJpushMessageOfPageForApp.get(i).getID();
if(repeatList.contains(notReadId)){
continue;
}else{
jpushMessage = new JpushMessage();
jpushMessage.setJPUSH_MESSAGES_SPLIT_ID(notReadId);
jpushMessage.setIS_READ(userId);
apiJpushMessageService.insertJpushMessageSplit(jpushMessage);
}
}
}
result.setCount(listdata.size());
result.setData(listdata);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
以上便是整个实现过程,代码很简单,主要的逻辑性很重要,大家自己在仔细捋一捋。