从零打造滑板文化社区平台:React 19 + Node.js + AI 微服务全栈实战

从零打造滑板文化社区平台:React 19 + Node.js + AI 微服务全栈实战

本文记录了我们参加 2026 中国大学生计算机设计大赛的作品------Skateboard Hub 滑板文化社区平台的完整技术架构与开发实践。项目涵盖 React 19 前端、Node.js 后端、Python AI 微服务、3D WebGL 定制器以及高德地图智能导览,希望能为全栈开发的同学提供一些参考。


一、项目背景与定位

滑板运动在 2020 年东京奥运会正式入奥,国内滑板文化正处于快速发展期。然而,现有的滑板类平台大多分散在微信公众号、B 站、小红书等不同渠道,缺乏一个集资讯、社区、教学、装备定制、场地导览于一体的综合性平台

我们的目标是打造一款面向滑板爱好者的"一站式数字家园",核心功能包括:

  • 📰 资讯中心:滑板历史、入奥专题、职业滑手故事
  • 💬 社区互动:发帖、评论、点赞、关注、私信
  • 🎨 3D 滑板定制器:WebGL 实时预览,支持板面/轮子/支架自定义
  • 🗺️ 智能地图:高德地图 3D 场景 + AI 场地分析(豆包大模型)
  • 🤖 AI 助手:智能问答与滑板图像生成
  • 🧑‍💼 个人主页与管理后台:完整的用户体系与内容审核

二、整体技术架构

我们采用了前后端分离 + AI 微服务 + Docker 容器化的架构设计:

scss 复制代码
┌─────────────────────────────────────────────────────────┐
│                      前端层 (React 19)                    │
│  Vite + TypeScript + React Router + Zustand + WebGL     │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                   API 网关层 (Express)                   │
│  JWT 认证 / CORS / 限流 / 错误处理 / 反向代理            │
└─────────────────────────────────────────────────────────┘
                           │
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
    ┌──────────┐   ┌──────────┐   ┌──────────────────┐
    │ MongoDB  │   │  Redis   │   │ Python Services  │
    │ (主数据)  │   │ (缓存)   │   │ Flask × 3        │
    └──────────┘   └──────────┘   │ · AI 生图        │
                                  │ · 地图 AI 分析   │
                                  │ · 智能聊天助手   │
                                  └──────────────────┘

技术选型理由:

层级 技术 选型理由
前端框架 React 19 + TS 生态成熟、类型安全、并发特性
构建工具 Vite 极速冷启动、原生 ESM、HMR
状态管理 Zustand 轻量、无样板代码、TypeScript 友好
后端 Express + MongoDB 快速开发、Schema 灵活、JSON 原生支持
缓存 Redis 会话存储、热点数据缓存、Rate Limit
AI 服务 Flask Python AI 生态成熟,快速接入大模型 API
部署 Docker Compose 一键启动 6 个容器,环境一致性

三、前端架构设计

3.1 React 19 + TypeScript + Vite 工程化

项目采用 Vite 作为构建工具,相比 CRA 有显著的性能提升。我们配置了路径别名 @/ 指向 src/ 目录,避免相对路径地狱:

tsx 复制代码
// tsconfig.json
"paths": {
  "@/*": ["./src/*"]
}

// 使用示例
import { useAuthStore } from '@/store/authStore'
import { api } from '@/lib/api'

路由采用 react-router-dom v7 的 BrowserRouter,所有页面通过统一的 Layout 组件包裹,实现导航栏和全局状态的共享:

tsx 复制代码
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route element={<Layout />}>
          <Route path="/" element={<HomePage />} />
          <Route path="/community" element={<CommunityPage />} />
          <Route path="/customizer" element={<CustomizerPage />} />
          <Route path="/map" element={<MapPage />} />
          {/* ... 共 17 个路由 */}
        </Route>
      </Routes>
    </BrowserRouter>
  )
}

3.2 Zustand 状态管理实践

我们选择了 Zustand 而非 Redux,因为它足够轻量且无需 Provider 包裹。

以用户认证状态为例,我们实现了一个完整的 AuthStore,包含登录、注册、登出、Token 自动续期等功能:

ts 复制代码
export const useAuthStore = create<AuthState>((set, get) => ({
  token: localStorage.getItem('token'),
  user: JSON.parse(localStorage.getItem('currentUser') || 'null'),
  isLoggedIn: !!localStorage.getItem('token'),

  async login(username, password) {
    const data = await api.post('/api/auth/login', { username, password })
    if (data.success) {
      get().setToken(data.data.token)
      get().setUser(data.data.user)
    }
    return data
  },

  logout() {
    localStorage.removeItem('token')
    localStorage.removeItem('currentUser')
    set({ token: null, user: null, isLoggedIn: false })
  },

  // ...
}))

亮点设计:头像动态生成。当用户未上传头像时,自动生成基于用户名的 SVG 头像,减少无意义的首屏请求:

ts 复制代码
function getAvatarUrl(user: User | null): string {
  if (user?.avatar) return user.avatar
  if (user?.defaultAvatar) {
    const { text, color } = user.defaultAvatar
    const svg = `<svg...>${text}</svg>`
    return 'data:image/svg+xml;base64,' + btoa(svg)
  }
  return '/assets/images/avatars/小狗头像.jpg'
}

四、后端架构与安全设计

4.1 Express 工程化结构

后端采用经典的 MVC 分层:

bash 复制代码
server/
├── app.js                 # 入口:中间件注册 → 路由挂载 → 静态文件 → 全局错误处理
├── routes/                # 路由层:按模块拆分
├── models/                # 数据层:Mongoose Schema
├── middleware/            # 中间件:认证、权限、上传、错误处理
└── utils/                 # 工具层:邮件、缓存、分页、软删除

app.js 的核心逻辑非常清晰:

js 复制代码
// 1. 数据库连接
connectDB()

// 2. CORS 白名单(生产环境严格限制来源)
app.use(cors({
  origin: function (origin, callback) {
    if (!origin) return callback(null, true)
    if (ALLOWED_ORIGINS.includes(origin)) return callback(null, true)
    callback(null, false)  // 静默拒绝,不暴露服务器信息
  },
  credentials: true,
}))

// 3. 请求体大小限制,防止 DoS
app.use(express.json({ limit: '1mb' }))

// 4. API 路由
app.use('/api/auth', require('./routes/auth'))
app.use('/api/posts', require('./routes/posts'))
// ...

// 5. SPA 回退:非 API 路由返回 index.html
app.get('*', (req, res) => {
  if (req.path.startsWith('/api')) {
    return res.status(404).json({ success: false, message: 'API 接口不存在' })
  }
  res.sendFile(path.join(staticRoot, 'index.html'))
})

// 6. 全局错误处理
app.use(errorHandler)

4.2 JWT 认证的安全强化

认证是社区类应用的核心,我们在标准 JWT 基础上做了多层加固:

1. 启动时强制校验密钥强度

js 复制代码
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
  console.error('[FATAL] JWT_SECRET 未设置或长度不足 32 字符,服务器拒绝启动')
  process.exit(1)
}

2. 强制算法为 HS256

避免 none 算法攻击或算法混淆攻击:

js 复制代码
jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] })

3. Token 黑名单机制

用户登出后,将 Token 加入 Redis 黑名单,即使 Token 未过期也无法继续使用:

js 复制代码
const token = authHeader.split(' ')[1]
if (await isBlacklisted(token)) {
  return res.status(401).json({ message: 'Token 已失效,请重新登录' })
}

4. 可选认证模式

社区帖子列表允许游客浏览,但点赞/评论需要登录。我们通过 optionalAuth 中间件实现这一需求:

js 复制代码
async function optionalAuth(req, res, next) {
  try {
    // 尝试解析 Token,失败也不拦截
    const user = await verifyToken(req)
    if (user) req.user = user
  } catch (e) { /* ignore */ }
  next()
}

4.3 其他安全措施

  • bcrypt 密码哈希:salt rounds = 12,抵抗彩虹表攻击
  • 文件上传校验:MIME 类型 + 扩展名双重检查
  • Admin 搜索转义:正则表达式转义,防止 ReDoS
  • 代理路由统一认证:内部 Python 服务不直接暴露,通过 Express 反向代理并校验 Token

五、AI 微服务设计

我们将 AI 能力拆分为 3 个独立的 Flask 微服务,通过 Docker Compose 编排:

服务 端口 职责
image-generator-web 5000 基于 Stable Diffusion / 火山引擎的滑板图像生成
map-server-docker 5001 接收地图坐标与描述,调用豆包大模型进行场地分析
ai-assistant 5002 通用聊天助手,支持滑板知识问答

为什么拆分为微服务?

  1. 技术异构:Python 在 AI 领域生态更成熟(OpenCV、Pillow、requests)
  2. 独立扩展:AI 服务通常计算密集型,可单独扩容 GPU 实例
  3. 故障隔离:AI 服务崩溃不影响主站 API
  4. 团队并行:前后端 + AI 可独立开发部署

Express 侧通过 express-http-proxy 将请求转发到对应服务:

js 复制代码
app.use('/api/ai/*', authMiddleware, proxy('http://ai:5000'))
app.use('/api/map/*', authMiddleware, proxy('http://map:5001'))

六、3D 滑板定制器的技术实现

3D 定制器是项目的亮点功能之一。用户可以在网页上实时更换板面图案、轮子颜色、支架样式,并 360° 旋转查看效果。

技术方案:

  • 模型格式:GLTF/GLB(轻量、支持 PBR 材质)
  • 渲染引擎:Three.js(WebGL 封装,社区生态丰富)
  • 材质替换 :通过 TextureLoader 动态加载用户选择的图案,替换 mesh.material.map
  • 交互控制OrbitControls 实现旋转/缩放/平移

核心流程:

js 复制代码
// 1. 加载 GLTF 模型
const loader = new GLTFLoader()
loader.load('/assets/models/skateboard.gltf', (gltf) => {
  const skateboard = gltf.scene
  scene.add(skateboard)
})

// 2. 用户选择新图案时,动态替换贴图
const texture = new THREE.TextureLoader().load(newPatternUrl)
texture.flipY = false  // GLTF 使用 OpenGL 坐标系
boardMesh.material.map = texture
boardMesh.material.needsUpdate = true

为了兼顾加载速度,我们使用了 Draco 压缩和 WebP 纹理,将模型体积控制在 3MB 以内。


七、智能地图与 AI 场地分析

地图模块基于高德地图 JS API 2.0,集成了以下能力:

  1. 3D 地图视图AMap.Map 开启 viewMode: '3D',展示城市建筑立体效果
  2. 地点搜索与 POI 检索:输入"滑板公园"自动检索周边场地
  3. 路径规划:集成公交/驾车/步行路线规划
  4. 天气查询:显示目标城市的实时天气,辅助用户决定是否出门滑板
  5. AI 场地分析 :用户点击地图上的场地后,前端将坐标和场地名称发送到 map-server-docker,由豆包大模型生成场地评测报告(如"该场地坡度适中,适合练习 Ollie,但周末人流较多")
js 复制代码
// 前端调用示例
const response = await fetch('/api/map/analyze', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}` },
  body: JSON.stringify({
    location: [116.397428, 39.90923],
    name: '朝阳滑板公园'
  })
})
const { analysis } = await response.json()
// analysis: "该场地拥有专业的碗池和街式区,地面平整度高..."

八、Docker 容器化部署

为了让评委老师能"一键跑起来",我们编写了完整的 Docker Compose 配置:

yaml 复制代码
services:
  mongo:
    image: mongo:7
    container_name: skateboard-mongo
    command: ["mongod", "--replSet", "rs0", "--auth", "--keyFile", "/data/configdb/keyfile"]
    volumes:
      - mongo_data:/data/db

  redis:
    image: redis:7-alpine
    container_name: skateboard-redis

  web:
    build: ./server
    container_name: skateboard-web
    ports:
      - "3000:3000"
    volumes:
      - ./server:/app
      - /app/node_modules
      - ./server/public/uploads:/app/public/uploads
      - ./client/dist:/app/dist
    depends_on:
      - mongo
      - redis

  ai:
    build: ./image-generator-web
    container_name: skateboard-ai

  map:
    build:
      context: .
      dockerfile: map-server-docker/Dockerfile
    container_name: skateboard-map

  ai-assistant:
    build: ./ai-assistant
    container_name: skateboard-ai-assistant
    ports:
      - "5002:5002"

关键设计点:

  • 数据持久化 :MongoDB 使用命名卷 mongo_data,容器重启数据不丢失
  • 代码热更新server 目录挂载到容器,开发时无需重新构建镜像
  • 上传文件持久化public/uploads 挂载到宿主机,防止用户头像丢失
  • 前端产物挂载client/dist 直接挂载到容器,实现前后端一体化部署

启动命令极简:

bash 复制代码
chmod +x start.sh
./start.sh
# 等价于 docker compose up -d --build

九、开发过程中的挑战与解决方案

挑战 1:前端静态页面与 React SPA 的融合

项目中既有历史遗留的 HTML 静态页面(如教学页面、资讯页面),又有新的 React SPA 路由。我们采取的方案是:

  • React 构建产物 client/dist 作为首选静态资源根目录
  • 原始 HTML 页面放在 server/public/pages/ 下作为降级方案
  • Express 优先查找 dist/index.html,找不到再回退到 public/
  • 原始页面通过 <a href="/pages/xxx.html"> 跳转,保持 URL 统一

开发时前端跑在 localhost:5173,后端在 localhost:3000。我们配置了动态 CORS 白名单,并开启 credentials: true

js 复制代码
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || ALLOWED_ORIGINS.includes(origin)) {
      callback(null, true)
    } else {
      callback(null, false)
    }
  },
  credentials: true,
}))

挑战 3:MongoDB 副本集与认证

Docker 中的 MongoDB 需要开启认证,而某些 Mongoose 功能(如事务)需要副本集。我们在 docker-compose.yml 中配置了 --replSet rs0--keyFile,并在启动脚本中自动初始化副本集:

bash 复制代码
# start.sh 片段
docker compose exec mongo mongosh --eval "rs.initiate()"

十、总结与收获

这次比赛让我们完整走通了"需求分析 → 技术选型 → 架构设计 → 编码实现 → 容器化部署"的全流程。

技术层面的收获:

  1. React 19 的新特性:实际体验了 React 19 的并发渲染和自动批处理,配合 Zustand 状态管理非常流畅
  2. 安全意识的提升:JWT 黑名单、CORS 白名单、bcrypt 哈希、请求体限制,这些"防御性编程"实践让系统更健壮
  3. 微服务通信:通过 Express 反向代理统一暴露 AI 服务,既保持了前端的简单调用,又实现了后端的安全隔离
  4. Docker 工程化:多容器编排、数据卷管理、环境变量配置,让项目真正做到了"开箱即用"

产品层面的思考:

技术最终要服务于用户。我们在设计 3D 定制器时发现,滑板玩家对"个性化"有极强的诉求------每一块板都是滑手自我表达的延伸。因此我们在 UI 上花了大量时间打磨交互细节,确保图案替换的响应时间在 100ms 以内,旋转视角的帧率稳定在 60fps。


附录:项目地址与技术栈

  • 仓库地址gitee.com/zhang-huair...
  • 技术栈:React 19 + TypeScript + Vite + Express + MongoDB + Redis + Python Flask + Docker
  • 部署方式:Docker Compose 一键启动
  • 许可证:MIT

如果您对项目有任何问题,欢迎在评论区留言交流!🛹


作者:张怀睿

赛事:2026 中国大学生计算机设计大赛

标签:React Node.js MongoDB Docker WebGL AI 全栈开发

相关推荐
网络工程小王2 小时前
【LCEL 链式调用详解】调用篇-2
java·服务器·前端·数据库·人工智能
swipe2 小时前
别把语音 Agent 当成“接两个 API”——用 NestJS 搭一套 ASR + LLM + 流式 TTS 的实时语音助手
前端·后端·llm
GISer_Jing2 小时前
AI Agent中游产业链全景拆解:智能体开发的核心生态与技术版图
前端·人工智能·后端
前端之虎陈随易3 小时前
2年没用Nodejs了,Bun很香
linux·前端·javascript·vue.js·typescript
Hooray3 小时前
用时7天,花费30元,我vibe coding这个网站
前端·agent·ai编程
小小高不懂写代码3 小时前
RAG--检索增强生成--原理及实战
前端·人工智能
空中海3 小时前
04 工程化、质量体系与 React 生态
前端·ubuntu·react.js
好运的阿财4 小时前
OpenClaw工具拆解之host_workspace_write+host_workspace_edit
前端·javascript·人工智能·机器学习·ai编程·openclaw·openclaw工具
ffqws_5 小时前
Spring Boot 接收前端请求的四种参数方式
前端·spring boot·后端