前阵子工作中遇到同事要记录用户访问网页页面时间的问题,后来仔细思考了一下,写了一个小demo,觉得应该记录下来。
思路:
考虑到用户访问除了正常关闭或离开页面之外还有停电断网的情况,所以最好的办法是用前端定时心跳请求法记录用户访问状态。
1.在前端公共js里设置一个定时器,每隔一段时间(这个可以根据业务自调)定时向后台访问一个url
2.后台设置一个公用map,用来记录每个用户的访问状态,map里记录有每个用户的开始页面访问时间和每个用户离开页面的时间,怎么判断用户离开页面呢,在后台用java写一个定时器,项目启动时同时启用定时器,每隔一段时间(这个可以根据业务自调)扫描map里每个用户的访问时间,用系统当前时间和map里的最后一次访问时间做比对,当超过一定时间(这个可以根据业务自调)时,表明用户已经离开页面,记录下当前用户最后一次访问时间做离开页面的时间,然后map里就有了当前用户的访问时间,离开页面时间.
3.接下来可以计算停留时间保存进数据库,在数据库保存时做累计,或者直接把用户信息保存在数据库(这个根据业务实现怎么样的功能而定).
4.最后remove掉已保存进数据库的那些用户信息。
理论上map里同时存在离开页面的用户和当前访问页面的用户,通过java定时扫描的方式,记录下离开页面的用户信息并移除map里的离线用户。
代码(用SSI框架为例):
1.前端公用js里写一个定时器:
setInterval(function(){
//要执行的代码
var nameText = $(".adminControl").text();
console.log(nameText.split(",")[1]);
var paramObj = {};
paramObj.userName = /*nameText.split(",")[1]*/"开始健";
//发送请求
$.ajax({
type: "POST",
url: ctx+"/getUserTime.do",
data: paramObj,
success: function(data){
},
error:function(){
}
});
console.log("定时器已生效");
},5000);
2.controller里声明两个变量。
public class ConfigVersionQueryController extends BaseController{
protected final Log log = LogFactory.getLog(ConfigVersionQueryController.class);
@Autowired
private ConfigVersionQueryService service;
@Autowired
private ConfigGlobalService globalService;
@Autowired
private IModuleLogManager logManager;
private static Map<String, Object> timeMap = new HashMap<String, Object>();
private static int value = 0;
}
3.写一个记录用户时间的接口:
/**
* 记录用户时间
* @return
* @throws Exception
*/
@RequestMapping("/getUserTime.do")
public void getUserTime(HttpServletRequest request,HttpServletResponse response) throws Exception
{
value++;
String userName = request.getParameter("userName");
if(null == timeMap.get(userName)){
//用户开始访问时间
timeMap.put(userName + "startTime", new Date());
}
timeMap.put(userName, new Date());
//System.out.println("用户开始访问时间=" + timeMap.get(userName + "startTime"));
//System.out.println("用户请求时间=" + timeMap.get(userName));
// System.out.println(value);
if(value == 1){
System.out.println("进来===");
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
// server
System.out.println("-------设定要指定任务--------");
Iterator iter = timeMap.entrySet().iterator(); //获取key和value的set
Date time = null;
System.out.println("==="+ timeMap.size());
Set<String> keySet = new HashSet<String>();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next(); //把hashmap转成Iterator再迭代到entry
String key = entry.getKey().toString(); //从entry获取key
if(key.indexOf("startTime") == -1 ){
time = (Date) entry.getValue();
if(((new Date()).getTime() - time.getTime())/1000 > 9){
keySet.add(key);
}
}
}
for (String key : keySet) {
System.out.println(key + "访问页面时间==" + (Date)timeMap.get(key + "startTime"));
System.out.println(key +"离开页面时间==" + (Date)timeMap.get(key));
//此处省略计入数据库的操作,可以计算停留时间等
//保存进数据库之后移除离线用户信息
timeMap.remove(key);
timeMap.remove(key + "startTime");
}
}
}, 15000, 15000);// 设定指定的时间time,此处为15000毫秒
}
}
总结:此为实验小demo,实现过程中用userId更好,这里只是为了直观起见用了userName,为了测试方便,前端设定固定请求时间为5秒,当前时间和最后一次记录时间差为9秒,后台定时器每15秒扫描一次,实际项目中这些时间都可以根据业务自调。