Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building an NBA Game Data Analysis Platform with Vue, Spring Boot, and DataGear

Tech May 17 3

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:

  1. All endpoints are blocked by default.
  2. Public-facing APIs require a static token passed in the header.
  3. 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.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.