00:00:00
网站访问统计系统设计方案
网站访问统计系统设计方案
我为您设计了一个基于 SpringBoot 和 VitePress 的网站访问统计系统,包含服务端 API 和前端埋点方案。
一、系统架构设计
plaintext
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ VitePress │─────▶│ SpringBoot │─────▶│ MySQL │
│ 博客前端 │ │ 统计服务 │ │ 数据库 │
└───────────────────┘ └───────────────────┘ └───────────────────┘
│ ▲
│ │
└───────────────────┐ │
│ │
┌───▼───────────────────┴───┐
│ 可选:Redis │
│ 访问缓存 │
└─────────────────────────┘
二、数据库表设计
建议创建以下四张表来存储统计数据:
1. t_visit_site
(网站信息表)
sql
CREATE TABLE `t_visit_site` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '网站ID',
`site_name` varchar(100) NOT NULL COMMENT '网站名称',
`site_domain` varchar(100) NOT NULL COMMENT '网站域名',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='网站信息表';
2. t_visit_page
(页面信息表)
sql
CREATE TABLE `t_visit_page` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '页面ID',
`site_id` bigint(20) NOT NULL COMMENT '所属网站ID',
`page_url` varchar(500) NOT NULL COMMENT '页面URL',
`page_title` varchar(200) DEFAULT NULL COMMENT '页面标题',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '首次访问时间',
PRIMARY KEY (`id`),
KEY `idx_site_id` (`site_id`),
KEY `idx_page_url` (`page_url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='页面信息表';
3. t_visit_log
(访问日志表)
sql
CREATE TABLE `t_visit_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`site_id` bigint(20) NOT NULL COMMENT '网站ID',
`page_id` bigint(20) NOT NULL COMMENT '页面ID',
`visitor_id` varchar(50) NOT NULL COMMENT '访问者ID',
`ip_address` varchar(50) NOT NULL COMMENT 'IP地址',
`user_agent` varchar(500) NOT NULL COMMENT 'User-Agent信息',
`referer` varchar(500) DEFAULT NULL COMMENT '来源URL',
`visit_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '访问时间',
`visit_duration` int(11) DEFAULT NULL COMMENT '访问时长(秒)',
`is_new_visitor` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否新访问者',
PRIMARY KEY (`id`),
KEY `idx_site_id` (`site_id`),
KEY `idx_page_id` (`page_id`),
KEY `idx_visitor_id` (`visitor_id`),
KEY `idx_visit_time` (`visit_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志表';
4. t_visitor_info
(访问者信息表)
sql
CREATE TABLE `t_visitor_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`visitor_id` varchar(50) NOT NULL COMMENT '访问者ID',
`first_visit_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '首次访问时间',
`last_visit_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后访问时间',
`visit_count` int(11) NOT NULL DEFAULT '1' COMMENT '访问次数',
`browser_name` varchar(50) DEFAULT NULL COMMENT '浏览器名称',
`browser_version` varchar(50) DEFAULT NULL COMMENT '浏览器版本',
`os_name` varchar(50) DEFAULT NULL COMMENT '操作系统名称',
`os_version` varchar(50) DEFAULT NULL COMMENT '操作系统版本',
`device_type` varchar(20) DEFAULT NULL COMMENT '设备类型(PC/Mobile/Tablet)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问者信息表';
三、SpringBoot 服务端实现思路
1. 核心依赖
xml
<dependencies>
<!-- Web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 工具库 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<!-- 可选:Redis缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
2. 核心 API 设计
java
@RestController
@RequestMapping("/api/visit")
public class VisitController {
@Autowired
private VisitService visitService;
/**
* 记录页面访问
*/
@PostMapping("/record")
public Result recordVisit(@RequestBody VisitRecordDTO recordDTO,
HttpServletRequest request) {
// 获取客户端IP
String clientIp = getClientIp(request);
// 获取User-Agent
String userAgent = request.getHeader("User-Agent");
// 记录访问
visitService.recordVisit(recordDTO, clientIp, userAgent);
return Result.success();
}
// 获取客户端真实IP
private String getClientIp(HttpServletRequest request) {
String xffHeader = request.getHeader("X-Forwarded-For");
if (xffHeader == null) {
return request.getRemoteAddr();
}
return xffHeader.split(",")[0];
}
}
3. 访问记录处理服务
java
@Service
public class VisitServiceImpl implements VisitService {
@Autowired
private VisitLogRepository visitLogRepository;
@Autowired
private VisitorInfoRepository visitorInfoRepository;
@Autowired
private PageInfoRepository pageInfoRepository;
@Override
public void recordVisit(VisitRecordDTO recordDTO, String clientIp, String userAgent) {
// 解析User-Agent获取浏览器和操作系统信息
UserAgent userAgentInfo = UserAgent.parseUserAgentString(userAgent);
Browser browser = userAgentInfo.getBrowser();
OperatingSystem os = userAgentInfo.getOperatingSystem();
// 生成或获取访问者ID (可使用Cookie或UUID)
String visitorId = generateVisitorId(recordDTO.getVisitorId());
// 保存或更新访问者信息
saveOrUpdateVisitorInfo(visitorId, clientIp, userAgentInfo);
// 获取或创建页面信息
PageInfo pageInfo = getOrCreatePageInfo(recordDTO.getSiteId(), recordDTO.getPageUrl(), recordDTO.getPageTitle());
// 保存访问日志
saveVisitLog(visitorId, pageInfo.getId(), clientIp, userAgent, recordDTO);
}
// 其他方法实现...
}
四、VitePress 前端埋点方案
1. 创建统计插件
javascript
// .vitepress/theme/statistics.js
import { onMounted } from 'vue'
import { useRoute } from 'vitepress'
export default function useStatistics() {
const route = useRoute()
onMounted(() => {
// 页面加载完成后发送统计请求
sendVisitStatistics()
})
const sendVisitStatistics = () => {
const siteId = 'YOUR_SITE_ID' // 替换为您的网站ID
const pageUrl = route.path
const pageTitle = document.title
// 生成或获取访问者ID (使用localStorage存储)
let visitorId = localStorage.getItem('visitor_id')
if (!visitorId) {
visitorId = generateUUID()
localStorage.setItem('visitor_id', visitorId)
}
// 构建统计数据
const visitData = {
siteId,
pageUrl,
pageTitle,
visitorId,
referer: document.referrer
}
// 发送POST请求到统计API
fetch('https://your-statistics-api.com/api/visit/record', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(visitData)
})
.catch(error => {
console.error('统计数据发送失败:', error)
})
}
// 生成UUID
const generateUUID = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0
const v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
}
return {
sendVisitStatistics
}
}
2. 在主题中集成统计插件
javascript
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import useStatistics from './statistics.js'
export default {
...DefaultTheme,
enhanceApp({ app, router }) {
// 在应用启动时初始化统计
app.mixin({
setup() {
const { sendVisitStatistics } = useStatistics()
// 监听路由变化,记录每次页面访问
router.afterEach(() => {
sendVisitStatistics()
})
return {}
}
})
}
}
五、数据统计与可视化
1. 统计 API 示例
java
@RestController
@RequestMapping("/api/statistics")
public class StatisticsController {
@Autowired
private StatisticsService statisticsService;
/**
* 获取网站总访问量
*/
@GetMapping("/site/total")
public Result getSiteTotalVisits(@RequestParam Long siteId) {
return Result.success(statisticsService.getSiteTotalVisits(siteId));
}
/**
* 获取网站每日访问趋势
*/
@GetMapping("/site/daily-trend")
public Result getSiteDailyTrend(@RequestParam Long siteId,
@RequestParam(required = false) Integer days) {
return Result.success(statisticsService.getSiteDailyTrend(siteId, days));
}
// 其他统计API...
}
2. 可视化展示
可以使用 ECharts 或 Chart.js 等前端图表库,基于统计 API 返回的数据生成图表:
javascript
// 示例:生成每日访问趋势图
const chart = echarts.init(document.getElementById('visit-trend-chart'));
fetch('/api/statistics/site/daily-trend?siteId=1&days=30')
.then(response => response.json())
.then(data => {
const option = {
xAxis: {
type: 'category',
data: data.dates
},
yAxis: {
type: 'value'
},
series: [{
data: data.visits,
type: 'line'
}]
};
chart.setOption(option);
});
六、部署与优化建议
- 性能优化:
- 使用 Redis 缓存热门页面的统计数据
- 异步处理访问日志(如使用 RabbitMQ 或 Kafka)
- 定期归档历史数据
- 数据安全:
- 对 IP 地址进行脱敏处理(如只保留前三位)
- 遵守 GDPR 等隐私法规,提供用户数据访问和删除接口
- 部署架构:
- 前端统计脚本可部署在 CDN 上
- 服务端采用微服务架构,可水平扩展
- 数据库使用主从复制,确保高可用性
通过以上设计,您可以实现一个完整的网站访问统计系统,既能收集详细的访问数据,又不会对 VitePress 博客的性能造成显著影响。