最近参与的项目微信工众平台开发,扫码关注某个开发者公众账号时,由于操作没有反应,而扫描了两次,结果,微信服务器也推送了两条消息到开发者服务器。结果,业务处理过程中入库微信粉丝记录也入了两条,微信关注事件的处理流程如下:
/**
* 关注事件:入库粉丝记录
* @param msgRequest
* @return
*/
private String subscribe(MsgRequest msgRequest){
MsgResponseText response = new MsgResponseText();
response.setFromUserName(msgRequest.getToUserName());
response.setToUserName(msgRequest.getFromUserName());
response.setMsgType(MsgType.Text.toString());
response.setContent("欢迎您关注我们!");
response.setCreateTime(System.currentTimeMillis()/1000);
/*
* 根据用户的关注情况进行回复
*/
String openId = msgRequest.getFromUserName();
String account = msgRequest.getToUserName();
FansAccount fansAccount = fansDao.queryByOpenId(openId);
if(fansAccount==null){
//新用户(添加)
fansAccount = new FansAccount();
//调用微信接口获取关注者详细信息
refreshFansAccount(fansAccount,openId, account);
//记录入库
if(StringUtils.isEmpty(fansAccount.getOpenId())){
logger.info("获取微信用户信息失败,不作处理...");
}else{
fansDao.addFansAccount(fansAccount);
}
}else{
//老用户(修正状态)
refreshFansAccount(fansAccount,openId, account);
//修改记录
fansDao.updateWhenFollowOrNot(fansAccount);
}
//如果设置获取被关注回复消息,则回复该内容
List<WexinBaseMsg> list = baseMsgDao.queryByReplyType(Constants.BeaddedReply);
if(list!=null&&list.size()>0){
response.setContent(list.get(0).getContent());
}
String newsToXml = MsgXmlUtil.textToXml(response);
logger.info("newToXml:\n"+newsToXml);
return newsToXml;
}
微信连续推送两条消息给工作平台服务器时,服务器开启两个线程去处理对应的请求,由于两个请求相距太近(本质上是用户误操作),结果在入库判断时出现两个线程判断均是新关注粉丝而同时入库两条记录的情况。在多线程环境下,查询判断和入库操作应该是一个原子操作,才能保证数据的一致性。解决办法是,可以在同一个事务中执行判断并插入操作,也可以对该段代码加锁以保证原子性。
在表单提交时,也应该考虑重复提交的问题,这点威客中国的用户体验非常不好,经常会在提交方案时页面没有反应时重复提交,结果刷新页面后看到重复提交N条记录。web项目有必要考虑用户重复操作的情况,毕竟如果响应速度不够快而用户又没有耐心的情况下,会产生重复数据。