网站数据统计
定义相关的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>