| @@ -9,10 +9,19 @@ package me.chanjar.weixin.common.util; | |||||
| public interface WxMessageDuplicateChecker { | public interface WxMessageDuplicateChecker { | ||||
| /** | /** | ||||
| * 检查消息ID是否重复 | |||||
| * @param wxMsgId | |||||
| * <h2>公众号的排重方式</h2> | |||||
| * | |||||
| * <p>普通消息:关于重试的消息排重,推荐使用msgid排重。<a href="http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html">文档参考</a>。</p> | |||||
| * <p>事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。<a href="http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html">文档参考</a></p> | |||||
| * | |||||
| * <h2>企业号的排重方式</h2> | |||||
| * | |||||
| * 官方文档完全没有写,参照公众号的方式排重。 | |||||
| * | |||||
| * <p>或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重</p> | |||||
| * @param messageId messageId需要根据上面讲的方式构造 | |||||
| * @return 如果是重复消息,返回true,否则返回false | * @return 如果是重复消息,返回true,否则返回false | ||||
| */ | */ | ||||
| public boolean isDuplicate(Long wxMsgId); | |||||
| public boolean isDuplicate(String messageId); | |||||
| } | } | ||||
| @@ -25,7 +25,7 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||||
| /** | /** | ||||
| * 消息id->消息时间戳的map | * 消息id->消息时间戳的map | ||||
| */ | */ | ||||
| private final ConcurrentHashMap<Long, Long> msgId2Timestamp = new ConcurrentHashMap<Long, Long>(); | |||||
| private final ConcurrentHashMap<String, Long> msgId2Timestamp = new ConcurrentHashMap<String, Long>(); | |||||
| /** | /** | ||||
| * 后台清理线程是否已经开启 | * 后台清理线程是否已经开启 | ||||
| @@ -65,7 +65,7 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||||
| while (true) { | while (true) { | ||||
| Thread.sleep(clearPeriod); | Thread.sleep(clearPeriod); | ||||
| Long now = System.currentTimeMillis(); | Long now = System.currentTimeMillis(); | ||||
| for (Map.Entry<Long, Long> entry : msgId2Timestamp.entrySet()) { | |||||
| for (Map.Entry<String, Long> entry : msgId2Timestamp.entrySet()) { | |||||
| if (now - entry.getValue() > timeToLive) { | if (now - entry.getValue() > timeToLive) { | ||||
| msgId2Timestamp.entrySet().remove(entry); | msgId2Timestamp.entrySet().remove(entry); | ||||
| } | } | ||||
| @@ -81,12 +81,12 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||||
| } | } | ||||
| @Override | @Override | ||||
| public boolean isDuplicate(Long wxMsgId) { | |||||
| if (wxMsgId == null) { | |||||
| public boolean isDuplicate(String messageId) { | |||||
| if (messageId == null) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| checkBackgroundProcessStarted(); | checkBackgroundProcessStarted(); | ||||
| Long timestamp = msgId2Timestamp.putIfAbsent(wxMsgId, System.currentTimeMillis()); | |||||
| Long timestamp = msgId2Timestamp.putIfAbsent(messageId, System.currentTimeMillis()); | |||||
| if (timestamp == null) { | if (timestamp == null) { | ||||
| // 第一次接收到这个消息 | // 第一次接收到这个消息 | ||||
| return false; | return false; | ||||
| @@ -12,21 +12,21 @@ public class WxMessageInMemoryDuplicateCheckerTest { | |||||
| // 第一次检查 | // 第一次检查 | ||||
| for (Long msgId : msgIds) { | for (Long msgId : msgIds) { | ||||
| boolean result = checker.isDuplicate(msgId); | |||||
| boolean result = checker.isDuplicate(String.valueOf(msgId)); | |||||
| Assert.assertFalse(result); | Assert.assertFalse(result); | ||||
| } | } | ||||
| // 过1秒再检查 | // 过1秒再检查 | ||||
| Thread.sleep(1000l); | Thread.sleep(1000l); | ||||
| for (Long msgId : msgIds) { | for (Long msgId : msgIds) { | ||||
| boolean result = checker.isDuplicate(msgId); | |||||
| boolean result = checker.isDuplicate(String.valueOf(msgId)); | |||||
| Assert.assertTrue(result); | Assert.assertTrue(result); | ||||
| } | } | ||||
| // 过1.5秒再检查 | // 过1.5秒再检查 | ||||
| Thread.sleep(1500l); | Thread.sleep(1500l); | ||||
| for (Long msgId : msgIds) { | for (Long msgId : msgIds) { | ||||
| boolean result = checker.isDuplicate(msgId); | |||||
| boolean result = checker.isDuplicate(String.valueOf(msgId)); | |||||
| Assert.assertFalse(result); | Assert.assertFalse(result); | ||||
| } | } | ||||
| @@ -115,7 +115,7 @@ public class WxCpMessageRouter { | |||||
| * @param wxMessage | * @param wxMessage | ||||
| */ | */ | ||||
| public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { | public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { | ||||
| if (messageDuplicateChecker.isDuplicate(wxMessage.getMsgId())) { | |||||
| if (isDuplicateMessage(wxMessage)) { | |||||
| // 如果是重复消息,那么就不做处理 | // 如果是重复消息,那么就不做处理 | ||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -177,6 +177,22 @@ public class WxCpMessageRouter { | |||||
| return res; | return res; | ||||
| } | } | ||||
| protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) { | |||||
| String messageId = ""; | |||||
| if (wxMessage.getMsgId() == null) { | |||||
| messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime()); | |||||
| } else { | |||||
| messageId = String.valueOf(wxMessage.getMsgId()); | |||||
| } | |||||
| if (messageDuplicateChecker.isDuplicate(messageId)) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | /** | ||||
| * 对session的访问结束 | * 对session的访问结束 | ||||
| * @param wxMessage | * @param wxMessage | ||||
| @@ -1,5 +1,6 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import me.chanjar.weixin.common.api.WxConsts; | |||||
| import me.chanjar.weixin.common.session.*; | import me.chanjar.weixin.common.session.*; | ||||
| import me.chanjar.weixin.common.util.WxMessageDuplicateChecker; | import me.chanjar.weixin.common.util.WxMessageDuplicateChecker; | ||||
| import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker; | import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker; | ||||
| @@ -113,7 +114,7 @@ public class WxMpMessageRouter { | |||||
| * @param wxMessage | * @param wxMessage | ||||
| */ | */ | ||||
| public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { | public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { | ||||
| if (messageDuplicateChecker.isDuplicate(wxMessage.getMsgId())) { | |||||
| if (isDuplicateMessage(wxMessage)) { | |||||
| // 如果是重复消息,那么就不做处理 | // 如果是重复消息,那么就不做处理 | ||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -175,6 +176,22 @@ public class WxMpMessageRouter { | |||||
| return res; | return res; | ||||
| } | } | ||||
| protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { | |||||
| String messageId = ""; | |||||
| if (wxMessage.getMsgId() == null) { | |||||
| messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime()); | |||||
| } else { | |||||
| messageId = String.valueOf(wxMessage.getMsgId()); | |||||
| } | |||||
| if (messageDuplicateChecker.isDuplicate(messageId)) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | /** | ||||
| * 对session的访问结束 | * 对session的访问结束 | ||||
| * @param wxMessage | * @param wxMessage | ||||