feat:后台增加日统计功能

This commit is contained in:
fanxb 2022-03-14 16:54:08 +08:00
parent 441361cdee
commit 69728cdcd0
10 changed files with 193 additions and 13 deletions

View File

@ -22,4 +22,9 @@ public class RedisConstant {
*/ */
public static final String HOST_UV_PRE = "host_uv_pre_"; public static final String HOST_UV_PRE = "host_uv_pre_";
public static final String PAGE_UV_PRE = "page_uv_pre_"; public static final String PAGE_UV_PRE = "page_uv_pre_";
/**
* 日统计数据id key前缀
*/
public static final String DAY_HOST_ID_PRE = "day_host_id_pre_";
public static final String DAY_DETAIL_PAGE_ID_PRE = "day_detail_page_id_pre_";
} }

View File

@ -46,7 +46,7 @@ public class ApplicationController {
@GetMapping("/visit") @GetMapping("/visit")
@ResponseBody @ResponseBody
public void visit(HttpServletRequest request, HttpServletResponse response, @NotBlank(message = "回调函数不能为空") String callBack public void visit(HttpServletRequest request, HttpServletResponse response, @NotBlank(message = "回调函数不能为空") String callBack
, @NotBlank(message = "key不能为空") String key, @NotBlank(message = "path不能为空") String path, boolean notAdd) throws IOException { , @NotBlank(message = "key不能为空") String key, @NotBlank(message = "path不能为空") String path, @RequestParam(defaultValue = "false") boolean notAdd) throws IOException {
applicationService.visit(request, response, callBack, key, path, notAdd); applicationService.visit(request, response, callBack, key, path, notAdd);
} }
} }

View File

@ -20,6 +20,7 @@ public interface DetailPageDao {
* date 2022/2/15 16:39 * date 2022/2/15 16:39
*/ */
@Insert("insert into detail_page(hostId,path,pv,uv) value(#{hostId},#{path},#{pv},#{uv})") @Insert("insert into detail_page(hostId,path,pv,uv) value(#{hostId},#{path},#{pv},#{uv})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
void insertOne(DetailPagePo detailPagePo); void insertOne(DetailPagePo detailPagePo);
/** /**

View File

@ -0,0 +1,42 @@
package com.fanxb.backend.dao;
import com.fanxb.backend.entity.po.DetailPageDayPo;
import com.fanxb.backend.entity.po.HostDayPo;
import org.apache.ibatis.annotations.*;
/**
* @author fanxb
*/
@Mapper
public interface DetailPageDayDao {
/**
* 获取id
*
* @param detailPageId id
* @param dayNum day
* @author fanxb
*/
@Select("select id from detail_page_day where detailPageId=#{detailPageId} and dayNum=#{dayNum}")
Integer getId(@Param("detailPageId") int detailPageId, @Param("dayNum") int dayNum);
/**
* 插入一条数据
*
* @param po po
* @author fanxb
*/
@Insert("insert into detail_page_day(detailPageId,dayNum,pv,uv) value(#{detailPageId},#{dayNum},0,0)")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
void insert(DetailPageDayPo po);
/**
* 更新pv,uv
*
* @param id id
* @param pvIncrement pv增量
* @param uvIncrement uv增量
* @author fanxb
*/
@Update("update detail_page_day set uv=uv+#{uvIncrement},pv=pv+#{pvIncrement} where id=#{id}")
void updatePvUv(@Param("id") int id, @Param("pvIncrement") int pvIncrement, @Param("uvIncrement") int uvIncrement);
}

View File

@ -0,0 +1,41 @@
package com.fanxb.backend.dao;
import com.fanxb.backend.entity.po.HostDayPo;
import org.apache.ibatis.annotations.*;
/**
* @author fanxb
*/
@Mapper
public interface HostDayDao {
/**
* 获取id
*
* @param hostId hostId
* @param dayNum day
* @author fanxb
*/
@Select("select id from host_day where hostId=#{hostId} and dayNum=#{dayNum}")
Integer getId(@Param("hostId") int hostId, @Param("dayNum") int dayNum);
/**
* 插入一条数据
*
* @param hostDayPo po
* @author fanxb
*/
@Insert("insert into host_day(hostId,dayNum,pv,uv) value(#{hostId},#{dayNum},0,0)")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
void insert(HostDayPo hostDayPo);
/**
* 更新pv,uv
*
* @param id id
* @param pvIncrement pv增量
* @param uvIncrement uv增量
* @author fanxb
*/
@Update("update host_day set uv=uv+#{uvIncrement},pv=pv+#{pvIncrement} where id=#{id}")
void updatePvUv(@Param("id") int id, @Param("pvIncrement") int pvIncrement, @Param("uvIncrement") int uvIncrement);
}

View File

@ -0,0 +1,26 @@
package com.fanxb.backend.entity.po;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class DetailPageDayPo {
/**
* id
*/
private int id;
/**
* hostId
*/
private int detailPageId;
/**
* 日期20200202
*/
private int dayNum;
private long pv;
private long uv;
}

View File

@ -2,8 +2,10 @@ package com.fanxb.backend.entity.po;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors;
@Data @Data
@Accessors(chain = true)
public class HostDayPo { public class HostDayPo {
/** /**
@ -17,7 +19,7 @@ public class HostDayPo {
/** /**
* 日期20200202 * 日期20200202
*/ */
private int dateNum; private int dayNum;
private long pv; private long pv;
private long uv; private long uv;

View File

@ -1,23 +1,25 @@
package com.fanxb.backend.service.impl; package com.fanxb.backend.service.impl;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header; import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.fanxb.backend.constants.RedisConstant;
import com.fanxb.backend.constants.CommonConstant; import com.fanxb.backend.constants.CommonConstant;
import com.fanxb.backend.constants.RedisConstant;
import com.fanxb.backend.dao.DetailPageDao; import com.fanxb.backend.dao.DetailPageDao;
import com.fanxb.backend.dao.DetailPageDayDao;
import com.fanxb.backend.dao.HostDao; import com.fanxb.backend.dao.HostDao;
import com.fanxb.backend.dao.HostDayDao;
import com.fanxb.backend.entity.dto.ApplicationSignDto; import com.fanxb.backend.entity.dto.ApplicationSignDto;
import com.fanxb.backend.entity.exception.CustomBaseException; import com.fanxb.backend.entity.exception.CustomBaseException;
import com.fanxb.backend.entity.po.DetailPageDayPo;
import com.fanxb.backend.entity.po.DetailPagePo; import com.fanxb.backend.entity.po.DetailPagePo;
import com.fanxb.backend.entity.po.HostDayPo;
import com.fanxb.backend.entity.po.HostPo; import com.fanxb.backend.entity.po.HostPo;
import com.fanxb.backend.entity.vo.ApplicationSignVo; import com.fanxb.backend.entity.vo.ApplicationSignVo;
import com.fanxb.backend.entity.vo.UvPvVo; import com.fanxb.backend.entity.vo.UvPvVo;
import com.fanxb.backend.service.ApplicationService; import com.fanxb.backend.service.ApplicationService;
import com.fanxb.backend.util.NetUtil; import com.fanxb.backend.util.NetUtil;
import com.fanxb.backend.util.ThreadPoolUtil; import com.fanxb.backend.util.ThreadPoolUtil;
import jdk.jfr.ContentType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -25,11 +27,10 @@ import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -40,15 +41,20 @@ import java.util.concurrent.TimeUnit;
*/ */
@Service @Service
public class ApplicationServiceImpl implements ApplicationService { public class ApplicationServiceImpl implements ApplicationService {
private final StringRedisTemplate stringRedisTemplate; private final StringRedisTemplate stringRedisTemplate;
private final HostDao hostDao; private final HostDao hostDao;
private final DetailPageDao detailPageDao; private final DetailPageDao detailPageDao;
private final HostDayDao hostDayDao;
private final DetailPageDayDao detailPageDayDao;
@Autowired @Autowired
public ApplicationServiceImpl(StringRedisTemplate stringRedisTemplate, HostDao hostDao, DetailPageDao detailPageDao) { public ApplicationServiceImpl(StringRedisTemplate stringRedisTemplate, HostDao hostDao, DetailPageDao detailPageDao, HostDayDao hostDayDao, DetailPageDayDao detailPageDayDao) {
this.stringRedisTemplate = stringRedisTemplate; this.stringRedisTemplate = stringRedisTemplate;
this.hostDao = hostDao; this.hostDao = hostDao;
this.detailPageDao = detailPageDao; this.detailPageDao = detailPageDao;
this.hostDayDao = hostDayDao;
this.detailPageDayDao = detailPageDayDao;
} }
@Override @Override
@ -94,10 +100,10 @@ public class ApplicationServiceImpl implements ApplicationService {
* @param hostPo hostPo * @param hostPo hostPo
* @param detailPagePo detailPagePo * @param detailPagePo detailPagePo
* @author fanxb * @author fanxb
* date 2022/2/16 15:40
*/ */
private void updateData(String ip, HostPo hostPo, DetailPagePo detailPagePo) { private void updateData(String ip, HostPo hostPo, DetailPagePo detailPagePo) {
String hostKey = RedisConstant.HOST_UV_PRE + hostPo.getId() + "_" + ip; String hostKey = RedisConstant.HOST_UV_PRE + hostPo.getId() + "_" + ip;
//记录ip是否在当天访问过host
String hostVal = stringRedisTemplate.opsForValue().get(hostKey); String hostVal = stringRedisTemplate.opsForValue().get(hostKey);
hostPo.setPv(hostPo.getPv() + 1); hostPo.setPv(hostPo.getPv() + 1);
long tomorrowZero = LocalDate.now().plusDays(1).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); long tomorrowZero = LocalDate.now().plusDays(1).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
@ -106,6 +112,7 @@ public class ApplicationServiceImpl implements ApplicationService {
hostPo.setUv(hostPo.getUv() + 1); hostPo.setUv(hostPo.getUv() + 1);
} }
String pageKey = RedisConstant.PAGE_UV_PRE + hostPo.getId() + ip + detailPagePo.getPath(); String pageKey = RedisConstant.PAGE_UV_PRE + hostPo.getId() + ip + detailPagePo.getPath();
//记录ip是否在当天访问过页面
String pageVal = stringRedisTemplate.opsForValue().get(pageKey); String pageVal = stringRedisTemplate.opsForValue().get(pageKey);
detailPagePo.setPv(detailPagePo.getPv() + 1); detailPagePo.setPv(detailPagePo.getPv() + 1);
if (pageVal == null) { if (pageVal == null) {
@ -121,16 +128,54 @@ public class ApplicationServiceImpl implements ApplicationService {
detailPageDao.updateUvPv(detailPagePo.getId(), pageVal == null ? 1 : 0); detailPageDao.updateUvPv(detailPagePo.getId(), pageVal == null ? 1 : 0);
} }
//更新站点的日pv,uv //更新站点的日pv,uv
int dayNum = Integer.parseInt(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", ""));
hostDayDao.updatePvUv(getDayTableId(hostPo.getId(), dayNum, RedisConstant.DAY_HOST_ID_PRE), 1, hostVal == null ? 1 : 0);
detailPageDayDao.updatePvUv(getDayTableId(detailPagePo.getId(), dayNum, RedisConstant.DAY_DETAIL_PAGE_ID_PRE), 1, pageVal == null ? 1 : 0);
}); });
} }
/**
* 获取表id数据行不存在时新增行
*
* @param id id
* @param day day(20220202)
* @param type 类型
* @author fanxb
*/
private int getDayTableId(int id, int day, String type) {
String key = type + "_" + id + "_" + day;
String val = stringRedisTemplate.opsForValue().get(key);
if (val != null) {
return Integer.parseInt(val);
}
//redis中没有,从数据库中查找
Integer dbId = RedisConstant.DAY_HOST_ID_PRE.equals(type) ? hostDayDao.getId(id, day) : detailPageDayDao.getId(id, day);
if (dbId == null) {
//TODO 此次应该加锁避免并发情况下重复创建报错
dbId = RedisConstant.DAY_HOST_ID_PRE.equals(type) ? hostDayDao.getId(id, day) : detailPageDayDao.getId(id, day);
if (dbId == null) {
//数据库中也没有需要先初始化数据库中数据
if (RedisConstant.DAY_HOST_ID_PRE.equals(type)) {
HostDayPo hostDayPo = new HostDayPo().setHostId(id).setDayNum(day);
hostDayDao.insert(hostDayPo);
dbId = hostDayPo.getId();
} else {
DetailPageDayPo detailPageDayPo = new DetailPageDayPo().setDetailPageId(id).setDayNum(day);
detailPageDayDao.insert(detailPageDayPo);
dbId = detailPageDayPo.getId();
}
}
}
stringRedisTemplate.opsForValue().set(key, dbId.toString());
return dbId;
}
/** /**
* 获取hostID * 获取hostID
* *
* @param key key * @param key key
* @return {@link int} * @return {@link int}
* @author fanxb * @author fanxb
* date 2022/2/16 15:37
*/ */
private int getHostId(String key) { private int getHostId(String key) {
String id = stringRedisTemplate.opsForValue().get(key); String id = stringRedisTemplate.opsForValue().get(key);
@ -141,7 +186,7 @@ public class ApplicationServiceImpl implements ApplicationService {
if (hostId == null) { if (hostId == null) {
throw new CustomBaseException("key无效:" + key); throw new CustomBaseException("key无效:" + key);
} }
stringRedisTemplate.opsForValue().set(key, hostId.toString()); stringRedisTemplate.opsForValue().set(key, hostId.toString(), 2, TimeUnit.DAYS);
return hostId; return hostId;
} }

View File

@ -0,0 +1,14 @@
package com.fanxb.backend.util;
import org.springframework.stereotype.Component;
/**
* redis分布式锁操作工具类
*
* @author fanxb
*/
@Component
public class RedisLockUtil {
}

View File

@ -1,25 +1,29 @@
drop table host_day; drop table host_day;
CREATE TABLE qiezi.host_day ( CREATE TABLE qiezi.host_day (
id INT auto_increment NOT NULL,
hostId int NOT NULL COMMENT 'host表主键', hostId int NOT NULL COMMENT 'host表主键',
dayNum INT NOT NULL COMMENT '日期20200202', dayNum INT NOT NULL COMMENT '日期20200202',
uv INT NOT NULL, uv INT NOT NULL,
pv int NOT NULL, pv int NOT NULL,
CONSTRAINT host_day_pk PRIMARY KEY (hostId,dayNum) CONSTRAINT host_day_pk PRIMARY KEY (id)
) )
ENGINE=InnoDB ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_0900_ai_ci COLLATE=utf8mb4_0900_ai_ci
COMMENT='整站,日 uv/pv记录'; COMMENT='整站,日 uv/pv记录';
CREATE UNIQUE INDEX host_day_key_IDX USING BTREE ON qiezi.host_day (hostId,dayNum);
CREATE TABLE qiezi.detail_page_day ( CREATE TABLE qiezi.detail_page_day (
id INT auto_increment NOT NULL,
detailPageId int NOT NULL COMMENT 'detail_page主键', detailPageId int NOT NULL COMMENT 'detail_page主键',
dayNum INT NOT NULL COMMENT '日期20200202', dayNum INT NOT NULL COMMENT '日期20200202',
uv INT NOT NULL, uv INT NOT NULL,
pv int NOT NULL, pv int NOT NULL,
CONSTRAINT detail_page_day_pk PRIMARY KEY (detailPageId,dayNum) CONSTRAINT detail_page_day_pk PRIMARY KEY (id)
) )
ENGINE=InnoDB ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_0900_ai_ci COLLATE=utf8mb4_0900_ai_ci
COMMENT='具体页面,日 uv/pv记录'; COMMENT='具体页面,日 uv/pv记录';
CREATE UNIQUE INDEX detail_page_day_key_IDX USING BTREE ON qiezi.detail_page_day(detailPageId,dayNum);