网站数据统计
定义相关的Redis Key
java
复制代码
/**
* 单日UV
*/
public static String getUVKey(String date) {
return PREFIX_UV+SPLIT+date;
}
/**
* 记录区间UV
* @param startData 开始日期
* @param endDate 结束日期
* @return
*/
public static String getUVkey(String startData,String endDate){
return PREFIX_UV+SPLIT+startData+SPLIT+endDate;
}
/**
* 单日活跃用户
* @param date
* @return
*/
public static String getDAUkey(String date){
return PREFIX_DAU+SPLIT+date;
}
/**
* 区间活跃用户
* @param startDate 开始日期
* @param endDate 结束日期
* @return
*/
public static String getDAUKey(String startDate,String endDate){
return PREFIX_DAU+SPLIT+startDate+SPLIT+endDate;
}
定义DataService
java
复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Service
public class DataService {
@Autowired
private RedisTemplate redisTemplate;
//定义日期格式
private SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd");
//将指定的IP计入UV
public void recordUV(String ip){
//获取redisKey
String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));
//存入ip
redisTemplate.opsForHyperLogLog().add(redisKey,ip);
}
//统计指定日期范围内的UV
public long calculateUV(Date start,Date end){
//参数判空
if(start ==null || end == null){
throw new IllegalArgumentException("参数不能为空");
}
/**
* 整理改时间范围内的key
*/
List<String> keyList =new ArrayList<>();
//可以进行日期计算
Calendar calendar = Calendar.getInstance();
calendar.setTime(start);
//当前日期小于结束日期
while (!calendar.getTime().after(end)){
String key=RedisKeyUtil.getUVKey(df.format(calendar.getTime()));
keyList.add(key);
calendar.add(Calendar.DATE,1);
//对日期进行递加
}
/**
* 合并数据
*/
String redisKey =RedisKeyUtil.getUVkey(df.format(start),df.format(end));
redisTemplate.opsForHyperLogLog().union(redisKey,keyList.toArray());
//返回统计结果 访问数量
return redisTemplate.opsForHyperLogLog().size(redisKey);
}
//将指定用户计入DAU
public void recordDAU(int userId){
String redisKey =RedisKeyUtil.getDAUkey(df.format(new Date()));
redisTemplate.opsForValue().setBit(redisKey,userId,true);
}
//统计指定日期范围内的DAU
//每一天的统计结果做一个或运算
public long calculateDAU(Date start,Date end){
//参数判空
if(start ==null || end == null){
throw new IllegalArgumentException("参数不能为空");
}
/**
* 整理改时间范围内的key
*/
List<byte[]> keyList =new ArrayList<>();
//可以进行日期计算
Calendar calendar = Calendar.getInstance();
calendar.setTime(start);
//当前日期小于结束日期
while (!calendar.getTime().after(end)){
String key=RedisKeyUtil.getDAUkey(df.format(calendar.getTime()));
keyList.add(key.getBytes());
calendar.add(Calendar.DATE,1);
//对日期进行递加
}
/**
* 整合进行or运算
* 使用redis底层的链接调用or运算
*/
return (long) redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
String redisKey = RedisKeyUtil.getDAUKey(df.format(start),df.format(end));
//解释
connection.bitOp(RedisStringCommands.BitOperation.OR,redisKey.getBytes(),keyList.toArray(new byte[0][0]));
return connection.bitCount(redisKey.getBytes());
}
});
}
}
定义拦截器
java
复制代码
@Component
public class DataInterceptor implements HandlerInterceptor {
@Autowired
private DataService dataService;
@Autowired
private HostHolder hostHolder;
//在请求之初统计数据
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{
//统计UV
String ip= request.getRemoteHost();
dataService.recordUV(ip);
//统计DAU
User user = hostHolder.getUser();
if(user!=null){
dataService.recordDAU(user.getId());
}
return true;
}
}
定义Controller
java
复制代码
@Controller
public class DataController {
@Autowired
private DataService dataService;
/**
* 统计页面的函数
* @return
*/
@RequestMapping(path = "/data",method = {RequestMethod.GET,RequestMethod.POST})
public String getDataPage(){
return "/site/admin/data";
}
/**
* 统计网站UV
* @param start
* @param end
* @param model
* @return
*/
@RequestMapping(path = "/data/uv",method = RequestMethod.POST)
public String getUV(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start , @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model){
long uv =dataService.calculateUV(start,end);
model.addAttribute("uvResult",uv);
model.addAttribute("uvStartDate",start);
model.addAttribute("uvEndDate",end);
//将处理结果发给/data进行另一半的处理
return "forward:/data";
// return "/site/admin/data";
}
@RequestMapping(path = "/data/dau",method = RequestMethod.POST)
public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start , @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model){
long dau =dataService.calculateDAU(start,end);
model.addAttribute("dauResult",dau);
model.addAttribute("dauStartDate",start);
model.addAttribute("dauEndDate",end);
//将处理结果发给/data进行另一半的处理
return "forward:/data";
// return "/site/admin/data";
}
}
添加权限
java
复制代码
.antMatchers(
"/discuss/delete",
"/data/**"
)
页面示例
javascript
复制代码
<div class="main">
<!-- 网站UV -->
<div class="container pl-5 pr-5 pt-3 pb-3 mt-3">
<h6 class="mt-3"><b class="square"></b> 网站 UV</h6>
<form class="form-inline mt-3" method="post" th:action="@{/data/uv}">
<input type="date" class="form-control" required name="start" th:value="${#dates.format(uvStartDate,'yyyy-MM-dd')}"/>
<input type="date" class="form-control ml-3" required name="end" th:value="${#dates.format(uvEndDate,'yyyy-MM-dd')}"/>
<button type="submit" class="btn btn-primary ml-3">开始统计</button>
</form>
<ul class="list-group mt-3 mb-3">
<li class="list-group-item d-flex justify-content-between align-items-center">
统计结果
<span class="badge badge-primary badge-danger font-size-14" th:text="${uvResult}">0</span>
</li>
</ul>
</div>
<!-- 活跃用户 -->
<div class="container pl-5 pr-5 pt-3 pb-3 mt-4">
<h6 class="mt-3"><b class="square"></b> 活跃用户</h6>
<form class="form-inline mt-3"method="post" th:action="@{/data/dau}">
<input type="date" class="form-control" required name="start" th:value="${#dates.format(dauStartDate,'yyyy-MM-dd')}"/>
<input type="date" class="form-control ml-3" required name="end" th:value="${#dates.format(dauEndDate,'yyyy-MM-dd')}"/>
<button type="submit" class="btn btn-primary ml-3">开始统计</button>
</form>
<ul class="list-group mt-3 mb-3">
<li class="list-group-item d-flex justify-content-between align-items-center">
统计结果
<span class="badge badge-primary badge-danger font-size-14" th:text="${dauResult}">0</span>
</li>
</ul>
</div>
</div>