Building an NBA Game Data Analysis Platform with Vue, Spring Boot, and DataGear
Rationale for a Backend Service
The platform functions as an interactive data dashboard rather than a static blog. It dynamically fetches and displays daily NBA match schedules and statistics, necessitating a robust backend to handle data processing and delivery.
Middleware and Infrastructure
Due to limited server resources, the stack is kept minimal:
- Runtime: JRE for the Spring Boot application.
- Web Server: Nginx for reverse proxying.
- Database: MySQL for data persistence.
With this setup, the backend memory consumption remains stable at approximately 60%.
API Security Strategy
To protect endpoints from unauthorized access, a custom interceptor is implemented instead of full server-side rendering. The logic enforces a token-based verificaiton system:
- All endpoints are blocked by default.
- Public-facing APIs require a static token passed in the header.
- Sensitive endpoints utilize dynamic tokens with expiration times.
Access is granted only after successful validation within the interceptor.
Frontend Framework Selection
The administrative interface is built using Fantastic-admin. While options like Vben Admin were considered, prior experience with Fantastic-admin made it the efficient choice for rapid development.
Data Visualization with DataGear
DataGear is integrated to create visual dashboards for analyzing game statistics and team performance metrics.
Server and Domain Setup
- Hosting: An Aliyun instance was provisioned (renewable at a promotional rate).
- Environment: Standard setup including JDK, MySQL, and Nginx (details omitted for brevity).
- Domain: Purchased and configured via Aliyun.
- ICP Filing: Completed through the Aliyun ICP service.
Analytics Integration
Baidu Tongji (Analytics) is used for traffic monitoring. The following script is injected into the HTML head to track visitor data:
<head>
<meta charset="UTF-8">
<title>Game Data</title>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?1680a53c5ea55b9b57f1f04aeb2d4472";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</head>
Comment System
The Changyan comment system is integrated into the Vue application using jQuery to load the external script dynamically:
<script setup lang="ts">
import $ from 'jquery';
const sysWindow = window as any;
// Reset global variables to prevent conflicts
if (sysWindow.changyan) sysWindow.changyan = undefined;
if (sysWindow.cyan) sysWindow.cyan = undefined;
$.getScript('https://changyan.sohu.com/upload/changyan.js', () => {
sysWindow.changyan.api.config({
appid: 'your_app_id',
conf: 'your_conf_string'
});
});
</script>
<template>
<div>
<div id="SOHUCS"></div>
</div>
</template>
Clipboard Functionality
The @vueuse/core useClipboard composable handles copy actions. Note that the Clipboard API requires a secure context (HTTPS).
<script setup lang="ts">
import { useClipboard } from '@vueuse/core';
import { watch } from 'vue';
import { Message } from 'your-ui-library';
const { copy, copied, isSupported } = useClipboard();
const shareLink = ref('https://nba.example.com');
watch(copied, (newVal) => {
if (newVal) {
Message.success('Copied! Share it with your friends.', { zIndex: 99999 });
}
});
const triggerCopy = () => {
if (isSupported.value) {
copy(shareLink.value);
}
};
</script>
Watermark Implementation
A custom script applies a site-wide watermark to the DOM. It calculates the viewport size to tile the text efficiently and uses CSS transforsm for the rotation effect.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Stats</title>
<style>
#water-container { position: relative; width: 100%; }
.watermark-item {
position: absolute;
pointer-events: none;
color: #aaaaaa;
font-size: 25px;
opacity: 0.2;
transform: rotate(-20deg);
z-index: 9999;
font-family: 'Microsoft YaHei';
}
</style>
</head>
<body>
<div id="water-container"></div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const siteUrl = "http://nba.xiaorui.icu/";
applyWatermark(siteUrl);
window.onresize = () => applyWatermark(siteUrl);
function applyWatermark(text) {
const container = document.getElementById('water-container');
container.innerHTML = '';
container.style.height = document.documentElement.clientHeight - 100 + 'px';
const settings = {
text: text,
startX: 80, startY: 80,
gapX: 100, gapY: 70,
width: 210, height: 80,
angle: -20
};
const areaWidth = Math.max(container.scrollWidth, container.clientWidth);
const areaHeight = Math.max(container.scrollHeight, container.clientHeight);
const cols = Math.ceil(areaWidth / (settings.gapX + settings.width));
const rows = Math.ceil(areaHeight / (settings.gapY + settings.height));
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const posX = settings.startX + (settings.width + settings.gapX) * c;
const posY = settings.startY + (settings.height + settings.gapY) * r;
const span = document.createElement('span');
span.className = 'watermark-item';
span.innerText = settings.text;
span.style.left = posX + 'px';
span.style.top = posY + 'px';
span.style.transform = `rotate(${settings.angle}deg)`;
container.appendChild(span);
}
}
}
});
</script>
</body>
</html>
SEO
Optimization strategies are currently under development and will be added iteratively.