用SpringBoot和vue写一个2048小游戏 之用户登录和积分榜

8 篇文章 0 订阅
7 篇文章 0 订阅

一、数据库表设计

1.MySQL 数据库脚本

CREATE DATABASE IF NOT EXISTS game2048 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE game2048;

CREATE TABLE IF NOT EXISTS users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    highest_score INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    highest_score INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS game_records (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    score INT NOT NULL,
    played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

2.数据库设计说明

  • users: 存储用户的基本信息,包括用户名、密码(需加密处理)、电子邮件(可选)和用户的最高得分。

    • id: 主键,用户的唯一标识。
    • username: 用户名,必须唯一。
    • password: 用户密码(需要在应用程序层进行加密)。
    • email: 用户电子邮件,可选项,允许为空。
    • highest_score: 用户的最高分,用于排行榜展示。
    • created_atupdated_at: 用户创建和更新的时间戳。
  • game_records: 存储每次游戏的记录,包括用户ID、得分和游戏的时间。

    • id: 主键,游戏记录的唯一标识。
    • user_id: 外键,关联到 users 表,指向该游戏记录所属的用户。
    • score: 游戏得分。
    • played_at: 游戏记录时间戳,默认是记录创建的时间。

3. 数据库索引与优化

  • users 表上,我们为 username 字段添加了一个唯一索引,以确保用户名的唯一性。
  • user_idgame_records 表的外键,它与 users 表的 id 关联,设置了级联删除,以便在删除用户时自动删除其所有游戏记录。

4. 额外建议

  • 密码加密: 在创建用户时,确保在应用程序层对用户的密码进行加密(例如使用 bcrypt),并在数据库中存储加密后的密码。
  • 数据备份: 定期备份数据库,确保数据安全。
  • 优化查询: 根据应用程序的查询需求,可以考虑添加额外的索引来优化查询性能,例如根据用户ID和得分进行排序的索引。

二、代码实现

1. 项目结构

我们将项目分成前后端两个部分:

  • 后端 (Spring Boot): 负责用户管理、游戏记录存储、积分榜和排行榜等功能。
  • 前端 (Vue): 实现 2048 游戏逻辑、用户界面、用户登录注册等。

2. 后端 (Spring Boot)

2.1 创建 Spring Boot 项目

使用 Spring Initializr 创建一个 Spring Boot 项目,选择以下依赖:

  • Spring Web
  • Spring Data JPA
  • Spring Security
  • MySQL
  • Lombok

2.2 创建后端实体类

用户实体
package com.example.game.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    private int score;
}
游戏记录实体
package com.example.game.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GameRecord {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    private int score;
    private LocalDateTime playedAt;
}

2.3 创建 Repository 接口

 

package com.example.game.repository;

import com.example.game.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}
package com.example.game.repository;

import com.example.game.entity.GameRecord;
import com.example.game.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface GameRecordRepository extends JpaRepository<GameRecord, Long> {
    List<GameRecord> findByUser(User user);
}

2.4 创建服务层

package com.example.game.service;

import com.example.game.entity.User;
import com.example.game.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public User register(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepository.save(user);
    }

    public User findByUsername(String username) {
        return userRepository.findByUsername(username).orElse(null);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
}
package com.example.game.service;

import com.example.game.entity.GameRecord;
import com.example.game.entity.User;
import com.example.game.repository.GameRecordRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class GameRecordService {

    @Autowired
    private GameRecordRepository gameRecordRepository;

    public GameRecord saveGameRecord(User user, int score) {
        GameRecord gameRecord = new GameRecord();
        gameRecord.setUser(user);
        gameRecord.setScore(score);
        gameRecord.setPlayedAt(LocalDateTime.now());
        return gameRecordRepository.save(gameRecord);
    }

    public List<GameRecord> getGameRecordsByUser(User user) {
        return gameRecordRepository.findByUser(user);
    }

    public List<GameRecord> getAllGameRecords() {
        return gameRecordRepository.findAll();
    }
}

2.5 创建控制器

package com.example.game.controller;

import com.example.game.entity.User;
import com.example.game.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public User register(@RequestBody User user) {
        return userService.register(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
}
package com.example.game.controller;

import com.example.game.entity.GameRecord;
import com.example.game.entity.User;
import com.example.game.service.GameRecordService;
import com.example.game.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/games")
public class GameRecordController {

    @Autowired
    private GameRecordService gameRecordService;

    @Autowired
    private UserService userService;

    @PostMapping("/save")
    public GameRecord saveGameRecord(@RequestParam String username, @RequestParam int score) {
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new RuntimeException("User not found");
        }
        return gameRecordService.saveGameRecord(user, score);
    }

    @GetMapping("/records")
    public List<GameRecord> getAllGameRecords() {
        return gameRecordService.getAllGameRecords();
    }
}

2.6 配置安全性 (Spring Security)

创建一个简单的安全配置,允许所有用户注册和登录。

package com.example.game.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/users/register").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().permitAll()
            .and()
            .logout().permitAll();
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

3. 前端 (Vue)

3.1 创建 Vue 项目

使用 Vue CLI 创建一个 Vue 3 项目。

vue create 2048-game

选择 Vue 3 和 TypeScript 支持。

3.2 安装依赖

安装 Vue Router、Axios 和 Element Plus(UI 组件库)。

npm install vue-router@4 axios element-plus

3.3 配置路由

src/router/index.ts 中配置路由:

import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import Login from '../views/Login.vue';
import Register from '../views/Register.vue';
import Game from '../views/Game.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login },
  { path: '/register', component: Register },
  { path: '/game', component: Game },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

3.4 创建登录和注册页面

Login.vue
<template>
  <div>
    <el-form :model="loginForm" @submit.native.prevent="login">
      <el-form-item label="用户名">
        <el-input v-model="loginForm.username" autocomplete="off" />
      </el-form-item>
      <el-form-item label="密码">
        <el-input type="password" v-model="loginForm.password" autocomplete="off" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="login">登录</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue';
import axios from 'axios';

export default defineComponent({
  name: 'Login',
  setup() {
    const loginForm = reactive({
      username: '',
      password: ''
    });

    const login = async () => {
      try {
        const response = await axios.post('/api/users/login', loginForm);
        console.log('登录成功', response.data);
      } catch (error) {
        console.error('登录失败', error);
      }
    };

    return { loginForm, login };
  }
});
</script>
Register.vue
<template>
  <div>
    <el-form :model="registerForm" @submit.native.prevent="register">
      <el-form-item label="用户名">
        <el-input v-model="registerForm.username" autocomplete="off" />
      </el-form-item>
      <el-form-item label="密码">
        <el-input type="password" v-model="registerForm.password" autocomplete="off" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="register">注册</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue';
import axios from 'axios';

export default defineComponent({
  name: 'Register',
  setup() {
    const registerForm = reactive({
      username: '',
      password: ''
    });

    const register = async () => {
      try {
        const response = await axios.post('/api/users/register', registerForm);
        console.log('注册成功', response.data);
      } catch (error) {
        console.error('注册失败', error);
      }
    };

    return { registerForm, register };
  }
});
</script>

3.5 实现 2048 游戏逻辑

Game.vue
<template>
  <div>
    <h2>2048 游戏</h2>
    <el-button @click="startGame">开始游戏</el-button>
    <!-- 游戏网格 -->
    <div class="grid">
      <div class="row" v-for="row in grid" :key="row">
        <div class="cell" v-for="cell in row" :key="cell">{{ cell }}</div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue';

export default defineComponent({
  name: 'Game',
  setup() {
    const grid = reactive([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]);
    
    const startGame = () => {
      // 初始化游戏逻辑
      console.log('游戏开始');
    };

    return { grid, startGame };
  }
});
</script>

<style scoped>
.grid {
  display: grid;
  grid-template-columns: repeat(4, 100px);
  grid-gap: 10px;
}

.cell {
  width: 100px;
  height: 100px;
  background-color: #f0f0f0;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 24px;
}
</style>

4. 启动项目

  • 后端:使用 Maven 或 Gradle 构建并运行 Spring Boot 项目。
  • 前端:在前端项目目录下运行 npm run serve
  • 17
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当然,我可以帮你。首先,你需要确保已经安装了Java和Node.js环境以及相关的开发工具。接下来,你可以按照以下步骤进行操作: 1. 创建一个Spring Boot项目: - 打开你的IDE(如IntelliJ IDEA)并创建一个新的Spring Boot项目。 - 选择Maven项目并添加Web和Security依赖。 2. 创建一个登录页面: - 在src/main/resources/static目录下创建一个名为login.html的文件。 - 在login.html文件中编登录页面的HTML代码,包括表单和提交按钮。 3. 创建一个登录接口: - 在src/main/java目录下创建一个名为controller的包。 - 在controller包中创建一个名为LoginController的类。 - 在LoginController类中添加一个用于处理登录请求的方法,并使用@RequestMapping注解指定请求路径和请求方法。 - 在该方法中,获取用户提交的用户名和密码,并进行验证。 4. 创建一个Vue项目: - 打开终端,并进入到你想要创建Vue项目的目录中。 - 运行以下命令来创建一个名为frontend的Vue项目:`vue create frontend`。 - 根据提示选择默认配置或者自定义配置。 5. 修改Vue项目配置: - 进入frontend目录并打开src/main.js文件。 - 在main.js文件中添加以下代码来引入axios库:`import axios from 'axios'`。 - 在Vue实例(new Vue({...}))的created钩子函数中添加以下代码来发送登录请求:`axios.post('/login', {username: 'your_username', password: 'your_password'})`。 6. 启动应用: - 在IDE中运行Spring Boot项目。 - 进入frontend目录并运行`npm run serve`命令来启动Vue项目。 现在,你可以在浏览器中访问http://localhost:8080/login.html来查看登录页面,并尝试进行登录操作。请确保将"your_username"和"your_password"替换为实际的用户名和密码。 希望以上步骤对你有所帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战族狼魂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值