网站数据统计
定义相关的Redis Key
public static String getUVKey ( String date) { return PREFIX_UV + SPLIT + date; } public static String getUVkey ( String startData, String endDate) { return PREFIX_UV + SPLIT + startData+ SPLIT + endDate; } public static String getDAUkey ( String date) { return PREFIX_DAU + SPLIT + date; } public static String getDAUKey ( String startDate, String endDate) { return PREFIX_DAU + SPLIT + startDate+ SPLIT + endDate; }
定义DataService
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" ) ; public void recordUV ( String ip) { String redisKey = RedisKeyUtil . getUVKey ( df. format ( new Date ( ) ) ) ; redisTemplate. opsForHyperLogLog ( ) . add ( redisKey, ip) ; } public long calculateUV ( Date start, Date end) { if ( start == null || end == null ) { throw new IllegalArgumentException ( "参数不能为空" ) ; } 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) ; } public void recordDAU ( int userId) { String redisKey = RedisKeyUtil . getDAUkey ( df. format ( new Date ( ) ) ) ; redisTemplate. opsForValue ( ) . setBit ( redisKey, userId, true ) ; } public long calculateDAU ( Date start, Date end) { if ( start == null || end == null ) { throw new IllegalArgumentException ( "参数不能为空" ) ; } 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 ) ; } 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 ( ) ) ; } } ) ; }
}
定义拦截器
@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 { String ip= request. getRemoteHost ( ) ; dataService. recordUV ( ip) ; User user = hostHolder. getUser ( ) ; if ( user!= null ) { dataService. recordDAU ( user. getId ( ) ) ; } return true ; }
}
定义Controller
@Controller
public class DataController { @Autowired private DataService dataService; @RequestMapping ( path = "/data" , method = { RequestMethod . GET , RequestMethod . POST } ) public String getDataPage ( ) { return "/site/admin/data" ; } @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) ; return "forward:/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) ; return "forward:/data" ;
}
}
添加权限
. antMatchers ( "/discuss/delete" , "/data/**" )
页面示例
< 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>