formula project
This commit is contained in:
110
backend/pom.xml
Executable file
110
backend/pom.xml
Executable file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>f1</groupId>
|
||||
<artifactId>f1db</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<spring-boot.version>3.0.2</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>9.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<mainClass>f1.Main</mainClass>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
46
backend/src/main/java/f1/AppConfig.java
Executable file
46
backend/src/main/java/f1/AppConfig.java
Executable file
@@ -0,0 +1,46 @@
|
||||
package f1;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import f1.db.*;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String dbUrl;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String dbUsername;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String dbPassword;
|
||||
|
||||
@Bean
|
||||
public Database database() {
|
||||
Database db = new Database(dbUrl, dbUsername, dbPassword);
|
||||
db.init();
|
||||
return db;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDao userDao(Database db) {
|
||||
return new UserDao(db);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DriverDao driverDao(Database db) {
|
||||
return new DriverDao(db);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TeamDao teamDao(Database db) {
|
||||
return new TeamDao(db);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RaceDao raceDao(Database db) {
|
||||
return new RaceDao(db);
|
||||
}
|
||||
}
|
||||
38
backend/src/main/java/f1/Main.java
Executable file
38
backend/src/main/java/f1/Main.java
Executable file
@@ -0,0 +1,38 @@
|
||||
package f1;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import f1.db.Database;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
@SpringBootApplication
|
||||
@Component
|
||||
public class Main {
|
||||
public static String JDBC_URL;
|
||||
public static String JDBC_USER;
|
||||
public static String JDBC_PASSWORD;
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String dbUrl;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String dbUsername;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String dbPassword;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
JDBC_URL = dbUrl;
|
||||
JDBC_USER = dbUsername;
|
||||
JDBC_PASSWORD = dbPassword;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
var db = new Database(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
|
||||
db.init();
|
||||
}
|
||||
}
|
||||
90
backend/src/main/java/f1/controller/AuthController.java
Executable file
90
backend/src/main/java/f1/controller/AuthController.java
Executable file
@@ -0,0 +1,90 @@
|
||||
package f1.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import f1.Main;
|
||||
import f1.db.Database;
|
||||
import f1.db.UserDao;
|
||||
import f1.entity.User;
|
||||
import f1.security.JwtUtil;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
private Database db;
|
||||
private UserDao userDao;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
public AuthController() {
|
||||
this.db = new Database(Main.JDBC_URL, Main.JDBC_USER, Main.JDBC_PASSWORD);
|
||||
db.init();
|
||||
this.userDao = new UserDao(db);
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(@RequestBody User user) {
|
||||
if (userDao.getUserByUsername(user.getUsername()) != null) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body("Username already exists");
|
||||
}
|
||||
|
||||
String encodedPassword = passwordEncoder.encode(user.getPassword());
|
||||
user.setPassword(encodedPassword);
|
||||
|
||||
boolean success = userDao.createUser(user);
|
||||
if (success) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body("User registered successfully");
|
||||
} else {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Registration failed");
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@RequestBody Map<String, String> credentials, HttpServletResponse response) {
|
||||
String username = credentials.get("username");
|
||||
String password = credentials.get("password");
|
||||
|
||||
User user = userDao.getUserByUsername(username);
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password");
|
||||
}
|
||||
|
||||
if (!passwordEncoder.matches(password, user.getPassword())) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password");
|
||||
}
|
||||
|
||||
String token = jwtUtil.generateToken(user.getUsername());
|
||||
|
||||
Cookie cookie = new Cookie("auth_token", token);
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setSecure(false);
|
||||
cookie.setPath("/"); // 全局有效
|
||||
cookie.setMaxAge(24 * 60 * 60); // 24小时过期
|
||||
|
||||
response.addCookie(cookie);
|
||||
|
||||
return ResponseEntity.ok("Login successful");
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<?> logout(HttpServletResponse response) {
|
||||
Cookie cookie = new Cookie("auth_token", null);
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(0); // 立即过期
|
||||
response.addCookie(cookie);
|
||||
return ResponseEntity.ok("Logged out");
|
||||
}
|
||||
}
|
||||
82
backend/src/main/java/f1/controller/CommentController.java
Executable file
82
backend/src/main/java/f1/controller/CommentController.java
Executable file
@@ -0,0 +1,82 @@
|
||||
package f1.controller;
|
||||
|
||||
import f1.db.UserDao;
|
||||
import f1.entity.CommentItem;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.security.Principal;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/comments")
|
||||
public class CommentController {
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@GetMapping
|
||||
public List<List<CommentItem>> getComments(@RequestParam(defaultValue = "10") int limit,
|
||||
@RequestParam(defaultValue = "0") int offset,
|
||||
@RequestParam(required = false) Integer pageSize) {
|
||||
List<CommentItem> allComments = userDao.getRootComments(limit, offset);
|
||||
List<List<CommentItem>> pages = new ArrayList<>();
|
||||
|
||||
if (allComments.isEmpty()) {
|
||||
return pages;
|
||||
}
|
||||
|
||||
int size = (pageSize == null || pageSize <= 0) ? allComments.size() : pageSize;
|
||||
|
||||
for (int i = 0; i < allComments.size(); i += size) {
|
||||
int end = Math.min(allComments.size(), i + size);
|
||||
pages.add(allComments.subList(i, end));
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public boolean addRootComment(@RequestBody Map<String, Object> payload) {
|
||||
int userId = (int) payload.get("user_id");
|
||||
String content = (String) payload.get("content");
|
||||
return userDao.addComment(userId, null, null, content);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/replies")
|
||||
public Map<String, Object> getChildComments(@PathVariable int id) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("parent", userDao.getCommentById(id));
|
||||
response.put("replies", userDao.getChildComments(id));
|
||||
return response;
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/replies")
|
||||
public boolean addChildComment(@PathVariable int id, @RequestBody Map<String, Object> payload) {
|
||||
int userId = (int) payload.get("user_id");
|
||||
String content = (String) payload.get("content");
|
||||
Integer responseId = payload.containsKey("response_id") ? (Integer) payload.get("response_id") : null;
|
||||
|
||||
if (responseId == null) {
|
||||
responseId = id;
|
||||
}
|
||||
|
||||
return userDao.addComment(userId, responseId, id, content);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public boolean deleteComment(@PathVariable int id, Principal principal) {
|
||||
CommentItem comment = userDao.getCommentById(id);
|
||||
if (comment == null) {
|
||||
return false;
|
||||
}
|
||||
if (!comment.getUsername().equals(principal.getName())) {
|
||||
return false;
|
||||
}
|
||||
return userDao.deleteComment(id);
|
||||
}
|
||||
}
|
||||
115
backend/src/main/java/f1/controller/F1Controller.java
Executable file
115
backend/src/main/java/f1/controller/F1Controller.java
Executable file
@@ -0,0 +1,115 @@
|
||||
package f1.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import f1.db.DriverDao;
|
||||
import f1.db.TeamDao;
|
||||
import f1.entity.Driver;
|
||||
import f1.entity.DriverHistoryItem;
|
||||
import f1.entity.DriverStatistic;
|
||||
import f1.entity.QualifyingResultItem;
|
||||
import f1.entity.RaceResultItem;
|
||||
import f1.entity.SeasonDriver;
|
||||
import f1.entity.SeasonScheduleItem;
|
||||
import f1.entity.Team;
|
||||
import f1.entity.TeamHistoryItem;
|
||||
import f1.entity.TeamStatistic;
|
||||
import f1.db.RaceDao;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class F1Controller {
|
||||
|
||||
@Autowired
|
||||
private DriverDao driverDao;
|
||||
@Autowired
|
||||
private TeamDao teamDao;
|
||||
@Autowired
|
||||
private RaceDao raceDao;
|
||||
|
||||
@GetMapping("/drivers")
|
||||
public List<Driver> getAllDrivers() {
|
||||
return driverDao.getAllDrivers();
|
||||
}
|
||||
|
||||
@GetMapping("/teams")
|
||||
public List<Team> getAllTeams() {
|
||||
return teamDao.getAllTeams();
|
||||
}
|
||||
|
||||
@GetMapping("/season-drivers")
|
||||
public List<SeasonDriver> getDriversBySeason(@RequestParam int season) {
|
||||
return driverDao.getDriversBySeason(season);
|
||||
}
|
||||
|
||||
@GetMapping("/standings/teams")
|
||||
public List<Map<String, Object>> getSeasonTeamStandings(@RequestParam int season) {
|
||||
return teamDao.getSeasonTop5TeamStandings(season);
|
||||
}
|
||||
|
||||
@GetMapping("/standings/drivers")
|
||||
public List<Map<String, Object>> getSeasonDriverStandings(@RequestParam int season) {
|
||||
return driverDao.getSeasonTop5DriverStandings(season);
|
||||
}
|
||||
|
||||
@GetMapping("/prix")
|
||||
public List<SeasonScheduleItem> getSeasonSchedule(@RequestParam int season) {
|
||||
return raceDao.getSeasonSchedule(season);
|
||||
}
|
||||
|
||||
@GetMapping("/teams/{id}/drivers")
|
||||
public List<String> getDriverNames(@PathVariable int id, @RequestParam int season) {
|
||||
return driverDao.getDriverNamesByTeamAndSeason(id, season);
|
||||
}
|
||||
|
||||
@GetMapping("/drivers/{id}/statistics")
|
||||
public Map<String, DriverStatistic> getDriverStatistic(@PathVariable int id, @RequestParam int season) {
|
||||
Map<String, DriverStatistic> result = new HashMap<>();
|
||||
result.put("formal", driverDao.getDriverStatistic(id, season, false));
|
||||
result.put("sprint", driverDao.getDriverStatistic(id, season, true));
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/teams/{id}/statistics")
|
||||
public TeamStatistic getTeamStatistic(@PathVariable int id, @RequestParam int season) {
|
||||
DriverStatistic formal = teamDao.getTeamStatistic(id, season, false);
|
||||
DriverStatistic sprint = teamDao.getTeamStatistic(id, season, true);
|
||||
List<String> drivers = driverDao.getDriverNamesByTeamAndSeason(id, season);
|
||||
|
||||
Map<String, Integer> standing = teamDao.getTeamStanding(id, season);
|
||||
int rank = standing.getOrDefault("ranking", 0);
|
||||
int totalScore = standing.getOrDefault("score", 0);
|
||||
|
||||
return new TeamStatistic(formal, sprint, drivers, rank, totalScore);
|
||||
}
|
||||
|
||||
@GetMapping("/prix/{prixId}/race")
|
||||
public List<RaceResultItem> getRaceResult(@PathVariable int prixId,
|
||||
@RequestParam(defaultValue = "false") boolean isSprint) {
|
||||
return raceDao.getRaceResults(prixId, isSprint);
|
||||
}
|
||||
|
||||
@GetMapping("/prix/{prixId}/qualifying")
|
||||
public List<QualifyingResultItem> getQualifyingResult(@PathVariable int prixId,
|
||||
@RequestParam(defaultValue = "false") boolean isSprint) {
|
||||
return raceDao.getQualifyingResults(prixId, isSprint);
|
||||
}
|
||||
|
||||
@GetMapping("/drivers/{id}/results")
|
||||
public List<DriverHistoryItem> getDriverHistory(@PathVariable int id, @RequestParam int season) {
|
||||
return driverDao.getDriverHistory(id, season);
|
||||
}
|
||||
|
||||
@GetMapping("/teams/{id}/results")
|
||||
public List<TeamHistoryItem> getTeamHistory(@PathVariable int id, @RequestParam int season) {
|
||||
return teamDao.getTeamHistory(id, season);
|
||||
}
|
||||
}
|
||||
35
backend/src/main/java/f1/controller/UserController.java
Executable file
35
backend/src/main/java/f1/controller/UserController.java
Executable file
@@ -0,0 +1,35 @@
|
||||
package f1.controller;
|
||||
|
||||
import f1.db.UserDao;
|
||||
import f1.entity.User;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@GetMapping
|
||||
public User getCurrentUser(Principal principal) {
|
||||
if (principal == null) {
|
||||
return null; // Or throw exception / return 401
|
||||
}
|
||||
String username = principal.getName();
|
||||
// getUserByUsername returns full user, but password is now @JsonIgnore-d
|
||||
return userDao.getUserByUsername(username);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public User getUserById(@PathVariable int id) {
|
||||
return userDao.getUserById(id);
|
||||
}
|
||||
}
|
||||
16
backend/src/main/java/f1/db/BaseDao.java
Executable file
16
backend/src/main/java/f1/db/BaseDao.java
Executable file
@@ -0,0 +1,16 @@
|
||||
package f1.db;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class BaseDao {
|
||||
protected Database db;
|
||||
|
||||
public BaseDao(Database db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
protected Connection getConnection() throws SQLException {
|
||||
return db.getConnection();
|
||||
}
|
||||
}
|
||||
66
backend/src/main/java/f1/db/Database.java
Executable file
66
backend/src/main/java/f1/db/Database.java
Executable file
@@ -0,0 +1,66 @@
|
||||
package f1.db;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Database {
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
public Database(String url, String user, String password) {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl(url);
|
||||
config.setUsername(user);
|
||||
config.setPassword(password);
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("prepStmtCacheSize", "250");
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
||||
config.setMaximumPoolSize(10);
|
||||
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void init() {
|
||||
try (Connection con = getConnection()) {
|
||||
executeSqlFile(con, "/init.sql");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSqlFile(Connection con, String resourcePath) {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(getClass().getResourceAsStream(resourcePath)));
|
||||
var stmt = con.createStatement()) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.startsWith("--") || line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
sql.append(line).append(" ");
|
||||
if (line.endsWith(";")) {
|
||||
stmt.executeUpdate(sql.toString().trim());
|
||||
sql.setLength(0);
|
||||
}
|
||||
}
|
||||
} catch (IOException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
191
backend/src/main/java/f1/db/DriverDao.java
Executable file
191
backend/src/main/java/f1/db/DriverDao.java
Executable file
@@ -0,0 +1,191 @@
|
||||
package f1.db;
|
||||
|
||||
import f1.entity.Driver;
|
||||
import f1.entity.DriverHistoryItem;
|
||||
import f1.entity.DriverStatistic;
|
||||
import f1.entity.SeasonDriver;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DriverDao extends BaseDao {
|
||||
|
||||
public DriverDao(Database db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
public ArrayList<Driver> getAllDrivers() {
|
||||
ArrayList<Driver> drivers = new ArrayList<>();
|
||||
try (var con = getConnection();
|
||||
var stmt = con.createStatement();
|
||||
var rs = stmt.executeQuery("SELECT * FROM driver")) {
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
String country = rs.getString("country");
|
||||
java.sql.Date birthday = rs.getDate("birthday");
|
||||
drivers.add(new Driver(id, name, country, birthday));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return drivers;
|
||||
}
|
||||
|
||||
public ArrayList<SeasonDriver> getDriversBySeason(int season) {
|
||||
ArrayList<SeasonDriver> seasonDrivers = new ArrayList<>();
|
||||
try (var con = getConnection();
|
||||
var stmt = con
|
||||
.prepareStatement("SELECT * FROM season_driver WHERE season = ? ORDER BY team, car_num")) {
|
||||
stmt.setInt(1, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
String team = rs.getString("team");
|
||||
String country = rs.getString("country");
|
||||
java.sql.Date birthday = rs.getDate("birthday");
|
||||
int carNum = rs.getInt("car_num");
|
||||
int seasonVal = rs.getInt("season");
|
||||
seasonDrivers.add(new SeasonDriver(id, name, team, country, birthday, carNum, seasonVal));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return seasonDrivers;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getSeasonTop5DriverStandings(int season) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
String sql = "SELECT sd.name, sd.team, standings.total_score, standings.ranking " +
|
||||
"FROM season_driver sd " +
|
||||
"JOIN ( " +
|
||||
" SELECT rr.driver_id, SUM(rr.score) AS total_score, " +
|
||||
" RANK() OVER (ORDER BY SUM(rr.score) DESC) AS ranking " +
|
||||
" FROM race_result rr " +
|
||||
" JOIN prix p ON rr.prix_id = p.id " +
|
||||
" WHERE p.season = ? " +
|
||||
" GROUP BY rr.driver_id " +
|
||||
") standings ON sd.id = standings.driver_id " +
|
||||
"WHERE sd.season = ? " +
|
||||
"ORDER BY standings.ranking ASC " +
|
||||
"LIMIT 5";
|
||||
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, season);
|
||||
stmt.setInt(2, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("driver_name", rs.getString("name"));
|
||||
item.put("team_name", rs.getString("team"));
|
||||
item.put("total_score", rs.getInt("total_score"));
|
||||
item.put("ranking", rs.getInt("ranking"));
|
||||
result.add(item);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ArrayList<String> getDriverNamesByTeamAndSeason(int teamId, int season) {
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
String sql = "SELECT d.name FROM driver d JOIN contract c ON d.id = c.driver_id WHERE c.season = ? AND c.team_id = ?";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, season);
|
||||
stmt.setInt(2, teamId);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
names.add(rs.getString("name"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public DriverStatistic getDriverStatistic(int driverId, int season, boolean isSprint) {
|
||||
return getStatistic("rr.driver_id = ?", driverId, season, isSprint);
|
||||
}
|
||||
|
||||
private DriverStatistic getStatistic(String whereClause, int id, int season, boolean isSprint) {
|
||||
DriverStatistic statistic = null;
|
||||
String sql = """
|
||||
SELECT
|
||||
COUNT(DISTINCT prix_id) AS total_cnt,
|
||||
SUM(score) AS score_sum,
|
||||
SUM(end_position <= 3) AS medal,
|
||||
SUM(end_position = 1) AS gold,
|
||||
SUM(start_position = 1) AS pole,
|
||||
SUM(end_position <= 10) AS top_ten,
|
||||
SUM(finish_time IN ('DNF', 'DNS', 'DSQ')) AS unfinished,
|
||||
SUM(fastest_time = fastest.fastest) AS fastest_lap
|
||||
FROM
|
||||
race_result rr
|
||||
NATURAL JOIN (
|
||||
SELECT
|
||||
rr.prix_id ,
|
||||
MIN(rr.fastest_time) AS fastest
|
||||
FROM
|
||||
race_result rr
|
||||
WHERE
|
||||
rr.fastest_time NOT IN ('DNF', 'DNS', 'DSQ')
|
||||
AND rr.is_sprint = ?
|
||||
AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
|
||||
GROUP BY
|
||||
rr.prix_id) fastest
|
||||
WHERE
|
||||
%s
|
||||
AND rr.is_sprint = ?
|
||||
AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
|
||||
""".formatted(whereClause);
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setBoolean(1, isSprint);
|
||||
stmt.setInt(2, season);
|
||||
stmt.setInt(3, id);
|
||||
stmt.setBoolean(4, isSprint);
|
||||
stmt.setInt(5, season);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
int totalCnt = rs.getInt("total_cnt");
|
||||
int scoreSum = rs.getInt("score_sum");
|
||||
int medal = rs.getInt("medal");
|
||||
int gold = rs.getInt("gold");
|
||||
int pole = rs.getInt("pole");
|
||||
int topTen = rs.getInt("top_ten");
|
||||
int unfinished = rs.getInt("unfinished");
|
||||
int fastestLap = rs.getInt("fastest_lap");
|
||||
statistic = new DriverStatistic(totalCnt, scoreSum, medal, gold, pole, topTen, unfinished,
|
||||
fastestLap);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return statistic;
|
||||
}
|
||||
|
||||
public ArrayList<DriverHistoryItem> getDriverHistory(int driverId, int season) {
|
||||
ArrayList<DriverHistoryItem> history = new ArrayList<>();
|
||||
String sql = "SELECT prix_name, team_name, pos, score, is_sprint FROM prix_result WHERE driver_id = ? AND season = ? ORDER BY round, is_sprint";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, driverId);
|
||||
stmt.setInt(2, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
history.add(new DriverHistoryItem(
|
||||
rs.getString("prix_name"),
|
||||
rs.getString("team_name"),
|
||||
rs.getInt("pos"),
|
||||
rs.getInt("score"),
|
||||
rs.getBoolean("is_sprint")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return history;
|
||||
}
|
||||
}
|
||||
120
backend/src/main/java/f1/db/RaceDao.java
Executable file
120
backend/src/main/java/f1/db/RaceDao.java
Executable file
@@ -0,0 +1,120 @@
|
||||
package f1.db;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import f1.entity.QualifyingResultItem;
|
||||
import f1.entity.RaceResultItem;
|
||||
import f1.entity.SeasonScheduleItem;
|
||||
|
||||
public class RaceDao extends BaseDao {
|
||||
|
||||
public RaceDao(Database db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
public ArrayList<RaceResultItem> getRaceResults(int prixId, boolean isSprint) {
|
||||
ArrayList<RaceResultItem> results = new ArrayList<>();
|
||||
String sql = "SELECT pos, car_num, driver_name, team_name, finish_time, score FROM prix_result WHERE prix_id = ? AND is_sprint = ? ORDER BY pos";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, prixId);
|
||||
stmt.setBoolean(2, isSprint);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
results.add(new RaceResultItem(
|
||||
rs.getInt("pos"),
|
||||
rs.getInt("car_num"),
|
||||
rs.getString("driver_name"),
|
||||
rs.getString("team_name"),
|
||||
rs.getString("finish_time"),
|
||||
rs.getInt("score")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public ArrayList<QualifyingResultItem> getQualifyingResults(int prixId, boolean isSprint) {
|
||||
ArrayList<QualifyingResultItem> results = new ArrayList<>();
|
||||
String sql = """
|
||||
SELECT
|
||||
qr.position,
|
||||
sd.car_num,
|
||||
sd.name,
|
||||
sd.team,
|
||||
MAX(CASE WHEN section = 1 THEN fastest_time END) AS q1_time,
|
||||
MAX(CASE WHEN section = 2 THEN fastest_time END) AS q2_time,
|
||||
MAX(CASE WHEN section = 3 THEN fastest_time END) AS q3_time
|
||||
FROM
|
||||
qualifying_result qr
|
||||
JOIN
|
||||
prix p ON qr.prix_id = p.id
|
||||
JOIN
|
||||
season_driver sd ON qr.driver_id = sd.id AND sd.season = p.season
|
||||
WHERE
|
||||
qr.prix_id = ?
|
||||
AND qr.is_sprint = ?
|
||||
GROUP BY
|
||||
qr.driver_id, qr.position, sd.car_num, sd.name, sd.team
|
||||
ORDER BY
|
||||
qr.position
|
||||
""";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, prixId);
|
||||
stmt.setBoolean(2, isSprint);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
results.add(new QualifyingResultItem(
|
||||
rs.getInt("position"),
|
||||
rs.getInt("car_num"),
|
||||
rs.getString("name"),
|
||||
rs.getString("team"),
|
||||
rs.getString("q1_time"),
|
||||
rs.getString("q2_time"),
|
||||
rs.getString("q3_time")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public ArrayList<SeasonScheduleItem> getSeasonSchedule(int season) {
|
||||
ArrayList<SeasonScheduleItem> schedule = new ArrayList<>();
|
||||
String sql = """
|
||||
SELECT
|
||||
p.id,
|
||||
p.round,
|
||||
p.name,
|
||||
p.have_sprint,
|
||||
p.circuit_id,
|
||||
c.country,
|
||||
c.location
|
||||
FROM
|
||||
prix p
|
||||
JOIN circuit c ON c.id = p.circuit_id
|
||||
WHERE
|
||||
p.season = ?
|
||||
ORDER BY
|
||||
p.round
|
||||
""";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
schedule.add(new SeasonScheduleItem(
|
||||
rs.getInt("id"),
|
||||
rs.getInt("round"),
|
||||
rs.getString("name"),
|
||||
rs.getBoolean("have_sprint"),
|
||||
rs.getInt("circuit_id"),
|
||||
rs.getString("country"),
|
||||
rs.getString("location")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
}
|
||||
158
backend/src/main/java/f1/db/TeamDao.java
Executable file
158
backend/src/main/java/f1/db/TeamDao.java
Executable file
@@ -0,0 +1,158 @@
|
||||
package f1.db;
|
||||
|
||||
import f1.entity.DriverStatistic;
|
||||
import f1.entity.Team;
|
||||
import f1.entity.TeamHistoryItem;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class TeamDao extends BaseDao {
|
||||
|
||||
public TeamDao(Database db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
public ArrayList<Team> getAllTeams() {
|
||||
ArrayList<Team> teams = new ArrayList<>();
|
||||
try (var con = getConnection();
|
||||
var stmt = con.createStatement();
|
||||
var rs = stmt.executeQuery("SELECT * FROM team")) {
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
String country = rs.getString("country");
|
||||
String engineSupplier = rs.getString("engine_supplier");
|
||||
int setupYear = rs.getInt("setup_time");
|
||||
java.sql.Date setupTime = java.sql.Date.valueOf(LocalDate.of(setupYear, 1, 1));
|
||||
teams.add(new Team(id, name, country, engineSupplier, setupTime));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return teams;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getTeamStanding(int teamId, int season) {
|
||||
Map<String, Integer> result = new HashMap<>();
|
||||
String sql = "SELECT total_score, ranking FROM season_team_standings WHERE season = ? AND team_id = ?";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, season);
|
||||
stmt.setInt(2, teamId);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
result.put("score", rs.getInt("total_score"));
|
||||
result.put("ranking", rs.getInt("ranking"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getSeasonTop5TeamStandings(int season) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
String sql = "SELECT t.name, s.total_score, s.ranking " +
|
||||
"FROM season_team_standings s " +
|
||||
"JOIN team t ON s.team_id = t.id " +
|
||||
"WHERE s.season = ? " +
|
||||
"ORDER BY s.ranking ASC " +
|
||||
"LIMIT 5";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("team_name", rs.getString("name"));
|
||||
item.put("total_score", rs.getInt("total_score"));
|
||||
item.put("ranking", rs.getInt("ranking"));
|
||||
result.add(item);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public DriverStatistic getTeamStatistic(int teamId, int season, boolean isSprint) {
|
||||
return getStatistic("rr.team_id = ?", teamId, season, isSprint);
|
||||
}
|
||||
|
||||
private DriverStatistic getStatistic(String whereClause, int id, int season, boolean isSprint) {
|
||||
DriverStatistic statistic = null;
|
||||
String sql = """
|
||||
SELECT
|
||||
COUNT(DISTINCT prix_id) AS total_cnt,
|
||||
SUM(score) AS score_sum,
|
||||
SUM(end_position <= 3) AS medal,
|
||||
SUM(end_position = 1) AS gold,
|
||||
SUM(start_position = 1) AS pole,
|
||||
SUM(end_position <= 10) AS top_ten,
|
||||
SUM(finish_time IN ('DNF', 'DNS', 'DSQ')) AS unfinished,
|
||||
SUM(fastest_time = fastest.fastest) AS fastest_lap
|
||||
FROM
|
||||
race_result rr
|
||||
NATURAL JOIN (
|
||||
SELECT
|
||||
rr.prix_id ,
|
||||
MIN(rr.fastest_time) AS fastest
|
||||
FROM
|
||||
race_result rr
|
||||
WHERE
|
||||
rr.fastest_time NOT IN ('DNF', 'DNS', 'DSQ')
|
||||
AND rr.is_sprint = ?
|
||||
AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
|
||||
GROUP BY
|
||||
rr.prix_id) fastest
|
||||
WHERE
|
||||
%s
|
||||
AND rr.is_sprint = ?
|
||||
AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
|
||||
""".formatted(whereClause);
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setBoolean(1, isSprint);
|
||||
stmt.setInt(2, season);
|
||||
stmt.setInt(3, id);
|
||||
stmt.setBoolean(4, isSprint);
|
||||
stmt.setInt(5, season);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
int totalCnt = rs.getInt("total_cnt");
|
||||
int scoreSum = rs.getInt("score_sum");
|
||||
int medal = rs.getInt("medal");
|
||||
int gold = rs.getInt("gold");
|
||||
int pole = rs.getInt("pole");
|
||||
int topTen = rs.getInt("top_ten");
|
||||
int unfinished = rs.getInt("unfinished");
|
||||
int fastestLap = rs.getInt("fastest_lap");
|
||||
statistic = new DriverStatistic(totalCnt, scoreSum, medal, gold, pole, topTen, unfinished,
|
||||
fastestLap);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return statistic;
|
||||
}
|
||||
|
||||
public ArrayList<TeamHistoryItem> getTeamHistory(int teamId, int season) {
|
||||
ArrayList<TeamHistoryItem> history = new ArrayList<>();
|
||||
String sql = "SELECT prix_name, SUM(score) as total_score FROM prix_result WHERE team_id = ? AND season = ? GROUP BY prix_id, prix_name, round ORDER BY round";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, teamId);
|
||||
stmt.setInt(2, season);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
history.add(new TeamHistoryItem(
|
||||
rs.getString("prix_name"),
|
||||
rs.getInt("total_score")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return history;
|
||||
}
|
||||
}
|
||||
200
backend/src/main/java/f1/db/UserDao.java
Executable file
200
backend/src/main/java/f1/db/UserDao.java
Executable file
@@ -0,0 +1,200 @@
|
||||
package f1.db;
|
||||
|
||||
import f1.entity.CommentItem;
|
||||
import f1.entity.User;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UserDao extends BaseDao {
|
||||
|
||||
public UserDao(Database db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
public boolean createUser(User user) {
|
||||
String sql = "INSERT INTO user (username, password, email, country, avatar) VALUES (?, ?, ?, ?, ?)";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setString(1, user.getUsername());
|
||||
stmt.setString(2, user.getPassword());
|
||||
stmt.setString(3, user.getEmail());
|
||||
stmt.setString(4, user.getCountry());
|
||||
stmt.setString(5, user.getAvatar());
|
||||
int affectedRows = stmt.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public User getUserByUsername(String username) {
|
||||
String sql = "SELECT * FROM user WHERE username = ?";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setString(1, username);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
return new User(
|
||||
rs.getInt("id"),
|
||||
rs.getString("username"),
|
||||
rs.getString("password"),
|
||||
rs.getString("email"),
|
||||
rs.getString("country"),
|
||||
rs.getString("avatar"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public User getUserById(int id) {
|
||||
String sql = "SELECT username, email, country, avatar FROM user WHERE id = ?";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, id);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
// Password is not selected, so we pass null
|
||||
return new User(
|
||||
id,
|
||||
rs.getString("username"),
|
||||
null, // password
|
||||
rs.getString("email"),
|
||||
rs.getString("country"),
|
||||
rs.getString("avatar"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean addComment(int userId, Integer responseId, Integer rootId, String content) {
|
||||
String sql = "INSERT INTO comment (user_id, response_id, root_id, content) VALUES (?, ?, ?, ?)";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, userId);
|
||||
|
||||
if (responseId == null) {
|
||||
stmt.setNull(2, java.sql.Types.INTEGER);
|
||||
} else {
|
||||
stmt.setInt(2, responseId);
|
||||
}
|
||||
|
||||
if (rootId == null) {
|
||||
stmt.setNull(3, java.sql.Types.INTEGER);
|
||||
} else {
|
||||
stmt.setInt(3, rootId);
|
||||
}
|
||||
|
||||
stmt.setString(4, content);
|
||||
int affectedRows = stmt.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean deleteComment(int commentId) {
|
||||
String sql = "DELETE FROM comment WHERE id = ?";
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, commentId);
|
||||
int affectedRows = stmt.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<CommentItem> getRootComments(int limit, int offset) {
|
||||
List<CommentItem> comments = new ArrayList<>();
|
||||
String sql = "SELECT c.id, c.user_id, u.username, c.content, COUNT(replies.id) AS reply_count " +
|
||||
"FROM comment c " +
|
||||
"JOIN user u ON c.user_id = u.id " +
|
||||
"LEFT JOIN comment replies ON replies.root_id = c.id " +
|
||||
"WHERE c.root_id IS NULL " +
|
||||
"GROUP BY c.id " +
|
||||
"ORDER BY c.id DESC " +
|
||||
"LIMIT ? OFFSET ?";
|
||||
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, limit);
|
||||
stmt.setInt(2, offset);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
comments.add(new CommentItem(
|
||||
rs.getInt("id"),
|
||||
rs.getInt("user_id"),
|
||||
rs.getString("username"),
|
||||
null, // response_id is null for root
|
||||
null, // root_id is null for root
|
||||
rs.getString("content"),
|
||||
rs.getInt("reply_count")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
||||
public List<CommentItem> getChildComments(int rootId) {
|
||||
List<CommentItem> comments = new ArrayList<>();
|
||||
String sql = "SELECT c.id, c.user_id, u.username, c.response_id, c.content, " +
|
||||
"parent_c.user_id AS reply_to_user_id, parent_u.username AS reply_to_username " +
|
||||
"FROM comment c " +
|
||||
"JOIN user u ON c.user_id = u.id " +
|
||||
"LEFT JOIN comment parent_c ON c.response_id = parent_c.id " +
|
||||
"LEFT JOIN user parent_u ON parent_c.user_id = parent_u.id " +
|
||||
"WHERE c.root_id = ? " +
|
||||
"ORDER BY c.id ASC";
|
||||
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, rootId);
|
||||
var rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
comments.add(new CommentItem(
|
||||
rs.getInt("id"),
|
||||
rs.getInt("user_id"),
|
||||
rs.getString("username"),
|
||||
(Integer) rs.getObject("response_id"),
|
||||
null, // root_id is known and not needed in response
|
||||
rs.getString("content"),
|
||||
(Integer) rs.getObject("reply_to_user_id"),
|
||||
rs.getString("reply_to_username")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
||||
public CommentItem getCommentById(int id) {
|
||||
String sql = "SELECT c.id, c.user_id, u.username, c.content, COUNT(replies.id) AS reply_count " +
|
||||
"FROM comment c " +
|
||||
"JOIN user u ON c.user_id = u.id " +
|
||||
"LEFT JOIN comment replies ON replies.root_id = c.id " +
|
||||
"WHERE c.id = ? " +
|
||||
"GROUP BY c.id";
|
||||
|
||||
try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
|
||||
stmt.setInt(1, id);
|
||||
var rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
return new CommentItem(
|
||||
rs.getInt("id"),
|
||||
rs.getInt("user_id"),
|
||||
rs.getString("username"),
|
||||
null,
|
||||
null,
|
||||
rs.getString("content"),
|
||||
rs.getInt("reply_count")
|
||||
);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
90
backend/src/main/java/f1/entity/CommentItem.java
Executable file
90
backend/src/main/java/f1/entity/CommentItem.java
Executable file
@@ -0,0 +1,90 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class CommentItem {
|
||||
private int id;
|
||||
|
||||
@JsonProperty("user_id")
|
||||
private int userId;
|
||||
|
||||
private String username;
|
||||
|
||||
@JsonProperty("response_id")
|
||||
private Integer responseId;
|
||||
|
||||
@JsonProperty("root_id")
|
||||
private Integer rootId;
|
||||
|
||||
private String content;
|
||||
|
||||
@JsonProperty("reply_count")
|
||||
private int replyCount;
|
||||
|
||||
@JsonProperty("reply_to_user_id")
|
||||
private Integer replyToUserId;
|
||||
|
||||
@JsonProperty("reply_to_username")
|
||||
private String replyToUsername;
|
||||
|
||||
public CommentItem(int id, int userId, String username, Integer responseId, Integer rootId, String content,
|
||||
int replyCount) {
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.responseId = responseId;
|
||||
this.rootId = rootId;
|
||||
this.content = content;
|
||||
this.replyCount = replyCount;
|
||||
}
|
||||
|
||||
public CommentItem(int id, int userId, String username, Integer responseId, Integer rootId, String content,
|
||||
Integer replyToUserId, String replyToUsername) {
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.responseId = responseId;
|
||||
this.rootId = rootId;
|
||||
this.content = content;
|
||||
this.replyToUserId = replyToUserId;
|
||||
this.replyToUsername = replyToUsername;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public Integer getResponseId() {
|
||||
return responseId;
|
||||
}
|
||||
|
||||
public Integer getRootId() {
|
||||
return rootId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public int getReplyCount() {
|
||||
return replyCount;
|
||||
}
|
||||
|
||||
public Integer getReplyToUserId() {
|
||||
return replyToUserId;
|
||||
}
|
||||
|
||||
public String getReplyToUsername() {
|
||||
return replyToUsername;
|
||||
}
|
||||
}
|
||||
49
backend/src/main/java/f1/entity/Driver.java
Executable file
49
backend/src/main/java/f1/entity/Driver.java
Executable file
@@ -0,0 +1,49 @@
|
||||
package f1.entity;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
public class Driver {
|
||||
private int id;
|
||||
private String name;
|
||||
private String country;
|
||||
private Date birthday;
|
||||
|
||||
public Driver(int id, String name, String country, Date birthday) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.country = country;
|
||||
this.birthday = birthday;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public Date getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public void setBirthday(Date birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
}
|
||||
45
backend/src/main/java/f1/entity/DriverHistoryItem.java
Executable file
45
backend/src/main/java/f1/entity/DriverHistoryItem.java
Executable file
@@ -0,0 +1,45 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DriverHistoryItem {
|
||||
@JsonProperty("prix_name")
|
||||
private String prixName;
|
||||
|
||||
@JsonProperty("team_name")
|
||||
private String teamName;
|
||||
|
||||
private int position;
|
||||
private int score;
|
||||
|
||||
@JsonProperty("is_sprint")
|
||||
private boolean isSprint;
|
||||
|
||||
public DriverHistoryItem(String prixName, String teamName, int position, int score, boolean isSprint) {
|
||||
this.prixName = prixName;
|
||||
this.teamName = teamName;
|
||||
this.position = position;
|
||||
this.score = score;
|
||||
this.isSprint = isSprint;
|
||||
}
|
||||
|
||||
public String getPrixName() {
|
||||
return prixName;
|
||||
}
|
||||
|
||||
public String getTeamName() {
|
||||
return teamName;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public boolean isSprint() {
|
||||
return isSprint;
|
||||
}
|
||||
}
|
||||
88
backend/src/main/java/f1/entity/DriverStatistic.java
Executable file
88
backend/src/main/java/f1/entity/DriverStatistic.java
Executable file
@@ -0,0 +1,88 @@
|
||||
package f1.entity;
|
||||
|
||||
public class DriverStatistic {
|
||||
private int totalCnt;
|
||||
private int scoreSum;
|
||||
private int medal;
|
||||
private int gold;
|
||||
private int pole;
|
||||
private int topTen;
|
||||
private int unfinished;
|
||||
private int fastestLap;
|
||||
|
||||
public DriverStatistic(int totalCnt, int scoreSum, int medal, int gold, int pole, int topTen, int unfinished,
|
||||
int fastestLap) {
|
||||
this.totalCnt = totalCnt;
|
||||
this.scoreSum = scoreSum;
|
||||
this.medal = medal;
|
||||
this.gold = gold;
|
||||
this.pole = pole;
|
||||
this.topTen = topTen;
|
||||
this.unfinished = unfinished;
|
||||
this.fastestLap = fastestLap;
|
||||
}
|
||||
|
||||
public int getTotalCnt() {
|
||||
return totalCnt;
|
||||
}
|
||||
|
||||
public int getScoreSum() {
|
||||
return scoreSum;
|
||||
}
|
||||
|
||||
public int getMedal() {
|
||||
return medal;
|
||||
}
|
||||
|
||||
public int getGold() {
|
||||
return gold;
|
||||
}
|
||||
|
||||
public int getPole() {
|
||||
return pole;
|
||||
}
|
||||
|
||||
public int getTopTen() {
|
||||
return topTen;
|
||||
}
|
||||
|
||||
public int getUnfinished() {
|
||||
return unfinished;
|
||||
}
|
||||
|
||||
public int getFastestLap() {
|
||||
return fastestLap;
|
||||
}
|
||||
|
||||
public void setTotalCnt(int totalCnt) {
|
||||
this.totalCnt = totalCnt;
|
||||
}
|
||||
|
||||
public void setScoreSum(int scoreSum) {
|
||||
this.scoreSum = scoreSum;
|
||||
}
|
||||
|
||||
public void setMedal(int medal) {
|
||||
this.medal = medal;
|
||||
}
|
||||
|
||||
public void setGold(int gold) {
|
||||
this.gold = gold;
|
||||
}
|
||||
|
||||
public void setPole(int pole) {
|
||||
this.pole = pole;
|
||||
}
|
||||
|
||||
public void setTopTen(int topTen) {
|
||||
this.topTen = topTen;
|
||||
}
|
||||
|
||||
public void setUnfinished(int unfinished) {
|
||||
this.unfinished = unfinished;
|
||||
}
|
||||
|
||||
public void setFastestLap(int fastestLap) {
|
||||
this.fastestLap = fastestLap;
|
||||
}
|
||||
}
|
||||
89
backend/src/main/java/f1/entity/QualifyingResultItem.java
Executable file
89
backend/src/main/java/f1/entity/QualifyingResultItem.java
Executable file
@@ -0,0 +1,89 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class QualifyingResultItem {
|
||||
private int position;
|
||||
|
||||
@JsonProperty("car_num")
|
||||
private int carNum;
|
||||
|
||||
private String name;
|
||||
private String team;
|
||||
|
||||
@JsonProperty("q1")
|
||||
private String q1Time;
|
||||
|
||||
@JsonProperty("q2")
|
||||
private String q2Time;
|
||||
|
||||
@JsonProperty("q3")
|
||||
private String q3Time;
|
||||
|
||||
public QualifyingResultItem(int position, int carNum, String name, String team, String q1Time, String q2Time,
|
||||
String q3Time) {
|
||||
this.position = position;
|
||||
this.carNum = carNum;
|
||||
this.name = name;
|
||||
this.team = team;
|
||||
this.q1Time = q1Time;
|
||||
this.q2Time = q2Time;
|
||||
this.q3Time = q3Time;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public int getCarNum() {
|
||||
return carNum;
|
||||
}
|
||||
|
||||
public void setCarNum(int carNum) {
|
||||
this.carNum = carNum;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
public void setTeam(String team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public String getQ1Time() {
|
||||
return q1Time;
|
||||
}
|
||||
|
||||
public void setQ1Time(String q1Time) {
|
||||
this.q1Time = q1Time;
|
||||
}
|
||||
|
||||
public String getQ2Time() {
|
||||
return q2Time;
|
||||
}
|
||||
|
||||
public void setQ2Time(String q2Time) {
|
||||
this.q2Time = q2Time;
|
||||
}
|
||||
|
||||
public String getQ3Time() {
|
||||
return q3Time;
|
||||
}
|
||||
|
||||
public void setQ3Time(String q3Time) {
|
||||
this.q3Time = q3Time;
|
||||
}
|
||||
}
|
||||
75
backend/src/main/java/f1/entity/RaceResultItem.java
Executable file
75
backend/src/main/java/f1/entity/RaceResultItem.java
Executable file
@@ -0,0 +1,75 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class RaceResultItem {
|
||||
private int pos;
|
||||
|
||||
@JsonProperty("car_num")
|
||||
private int carNum;
|
||||
|
||||
private String name;
|
||||
private String team;
|
||||
|
||||
@JsonProperty("finish_time")
|
||||
private String finishTime;
|
||||
|
||||
private int score;
|
||||
|
||||
public RaceResultItem(int pos, int carNum, String name, String team, String finishTime, int score) {
|
||||
this.pos = pos;
|
||||
this.carNum = carNum;
|
||||
this.name = name;
|
||||
this.team = team;
|
||||
this.finishTime = finishTime;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public void setPos(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public int getCarNum() {
|
||||
return carNum;
|
||||
}
|
||||
|
||||
public void setCarNum(int carNum) {
|
||||
this.carNum = carNum;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
public void setTeam(String team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public String getFinishTime() {
|
||||
return finishTime;
|
||||
}
|
||||
|
||||
public void setFinishTime(String finishTime) {
|
||||
this.finishTime = finishTime;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
}
|
||||
40
backend/src/main/java/f1/entity/SeasonDriver.java
Executable file
40
backend/src/main/java/f1/entity/SeasonDriver.java
Executable file
@@ -0,0 +1,40 @@
|
||||
package f1.entity;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
public class SeasonDriver extends Driver {
|
||||
private String team;
|
||||
private int carNum;
|
||||
private int season;
|
||||
|
||||
public SeasonDriver(int id, String name, String team, String country, Date birthday, int carNum, int season) {
|
||||
super(id, name, country, birthday);
|
||||
this.team = team;
|
||||
this.carNum = carNum;
|
||||
this.season = season;
|
||||
}
|
||||
|
||||
public String getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
public int getCarNum() {
|
||||
return carNum;
|
||||
}
|
||||
|
||||
public int getSeason() {
|
||||
return season;
|
||||
}
|
||||
|
||||
public void setTeam(String team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public void setCarNum(int carNum) {
|
||||
this.carNum = carNum;
|
||||
}
|
||||
|
||||
public void setSeason(int season) {
|
||||
this.season = season;
|
||||
}
|
||||
}
|
||||
85
backend/src/main/java/f1/entity/SeasonScheduleItem.java
Executable file
85
backend/src/main/java/f1/entity/SeasonScheduleItem.java
Executable file
@@ -0,0 +1,85 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class SeasonScheduleItem {
|
||||
private int id;
|
||||
private int round;
|
||||
private String name;
|
||||
|
||||
@JsonProperty("have_sprint")
|
||||
private boolean haveSprint;
|
||||
|
||||
@JsonProperty("circuit_id")
|
||||
private int circuitId;
|
||||
|
||||
private String country;
|
||||
private String location;
|
||||
|
||||
public SeasonScheduleItem(int id, int round, String name, boolean haveSprint, int circuitId, String country,
|
||||
String location) {
|
||||
this.id = id;
|
||||
this.round = round;
|
||||
this.name = name;
|
||||
this.haveSprint = haveSprint;
|
||||
this.circuitId = circuitId;
|
||||
this.country = country;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
|
||||
public void setRound(int round) {
|
||||
this.round = round;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isHaveSprint() {
|
||||
return haveSprint;
|
||||
}
|
||||
|
||||
public void setHaveSprint(boolean haveSprint) {
|
||||
this.haveSprint = haveSprint;
|
||||
}
|
||||
|
||||
public int getCircuitId() {
|
||||
return circuitId;
|
||||
}
|
||||
|
||||
public void setCircuitId(int circuitId) {
|
||||
this.circuitId = circuitId;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
59
backend/src/main/java/f1/entity/Team.java
Executable file
59
backend/src/main/java/f1/entity/Team.java
Executable file
@@ -0,0 +1,59 @@
|
||||
package f1.entity;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
public class Team {
|
||||
private int id;
|
||||
private String name;
|
||||
private String country;
|
||||
private String engineSupplier;
|
||||
private Date setupTime;
|
||||
|
||||
public Team(int id, String name, String country, String engineSupplier, Date setupTime) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.country = country;
|
||||
this.engineSupplier = engineSupplier;
|
||||
this.setupTime = setupTime;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public String getEngineSupplier() {
|
||||
return engineSupplier;
|
||||
}
|
||||
|
||||
public Date getSetupTime() {
|
||||
return setupTime;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public void setEngineSupplier(String engineSupplier) {
|
||||
this.engineSupplier = engineSupplier;
|
||||
}
|
||||
|
||||
public void setSetupTime(Date setupTime) {
|
||||
this.setupTime = setupTime;
|
||||
}
|
||||
}
|
||||
24
backend/src/main/java/f1/entity/TeamHistoryItem.java
Executable file
24
backend/src/main/java/f1/entity/TeamHistoryItem.java
Executable file
@@ -0,0 +1,24 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class TeamHistoryItem {
|
||||
@JsonProperty("prix_name")
|
||||
private String prixName;
|
||||
|
||||
@JsonProperty("total_score")
|
||||
private int totalScore;
|
||||
|
||||
public TeamHistoryItem(String prixName, int totalScore) {
|
||||
this.prixName = prixName;
|
||||
this.totalScore = totalScore;
|
||||
}
|
||||
|
||||
public String getPrixName() {
|
||||
return prixName;
|
||||
}
|
||||
|
||||
public int getTotalScore() {
|
||||
return totalScore;
|
||||
}
|
||||
}
|
||||
64
backend/src/main/java/f1/entity/TeamStatistic.java
Executable file
64
backend/src/main/java/f1/entity/TeamStatistic.java
Executable file
@@ -0,0 +1,64 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TeamStatistic {
|
||||
private DriverStatistic formal;
|
||||
private DriverStatistic sprint;
|
||||
private List<String> drivers;
|
||||
private int rank;
|
||||
|
||||
@JsonProperty("total_score")
|
||||
private int totalScore;
|
||||
|
||||
public TeamStatistic(DriverStatistic formal, DriverStatistic sprint, List<String> drivers, int rank,
|
||||
int totalScore) {
|
||||
this.formal = formal;
|
||||
this.sprint = sprint;
|
||||
this.drivers = drivers;
|
||||
this.rank = rank;
|
||||
this.totalScore = totalScore;
|
||||
}
|
||||
|
||||
public DriverStatistic getFormal() {
|
||||
return formal;
|
||||
}
|
||||
|
||||
public void setFormal(DriverStatistic formal) {
|
||||
this.formal = formal;
|
||||
}
|
||||
|
||||
public DriverStatistic getSprint() {
|
||||
return sprint;
|
||||
}
|
||||
|
||||
public void setSprint(DriverStatistic sprint) {
|
||||
this.sprint = sprint;
|
||||
}
|
||||
|
||||
public List<String> getDrivers() {
|
||||
return drivers;
|
||||
}
|
||||
|
||||
public void setDrivers(List<String> drivers) {
|
||||
this.drivers = drivers;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public void setRank(int rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
public int getTotalScore() {
|
||||
return totalScore;
|
||||
}
|
||||
|
||||
public void setTotalScore(int totalScore) {
|
||||
this.totalScore = totalScore;
|
||||
}
|
||||
}
|
||||
73
backend/src/main/java/f1/entity/User.java
Executable file
73
backend/src/main/java/f1/entity/User.java
Executable file
@@ -0,0 +1,73 @@
|
||||
package f1.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
public class User {
|
||||
private int id;
|
||||
private String username;
|
||||
@JsonIgnore
|
||||
private String password;
|
||||
private String email;
|
||||
private String country;
|
||||
private String avatar;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(int id, String username, String password, String email, String country, String avatar) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.email = email;
|
||||
this.country = country;
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
}
|
||||
50
backend/src/main/java/f1/security/JwtFilter.java
Executable file
50
backend/src/main/java/f1/security/JwtFilter.java
Executable file
@@ -0,0 +1,50 @@
|
||||
package f1.security;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Component
|
||||
public class JwtFilter extends OncePerRequestFilter {
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String token = null;
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if ("auth_token".equals(cookie.getName())) {
|
||||
token = cookie.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token != null && jwtUtil.validateToken(token)) {
|
||||
String username = jwtUtil.getUsernameFromToken(token);
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
username, null, new ArrayList<>());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
56
backend/src/main/java/f1/security/JwtUtil.java
Executable file
56
backend/src/main/java/f1/security/JwtUtil.java
Executable file
@@ -0,0 +1,56 @@
|
||||
package f1.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
@Value("${jwt.secret}")
|
||||
private String secretKeyString;
|
||||
|
||||
private Key secretKey;
|
||||
private static final long EXPIRATION_TIME = 86400000; // 24小时 (毫秒)
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
byte[] keyBytes = Decoders.BASE64.decode(secretKeyString);
|
||||
this.secretKey = Keys.hmacShaKeyFor(keyBytes);
|
||||
}
|
||||
|
||||
public String generateToken(String username) {
|
||||
return Jwts.builder()
|
||||
.setSubject(username)
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String getUsernameFromToken(String token) {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.getSubject();
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
backend/src/main/java/f1/security/SecurityConfig.java
Executable file
39
backend/src/main/java/f1/security/SecurityConfig.java
Executable file
@@ -0,0 +1,39 @@
|
||||
package f1.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
private JwtFilter jwtFilter;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf().disable()
|
||||
.authorizeHttpRequests()
|
||||
// Blacklist: Protect specific endpoints
|
||||
.requestMatchers(HttpMethod.GET, "/api/user").authenticated()
|
||||
.requestMatchers(HttpMethod.POST, "/api/comments/**").authenticated()
|
||||
.requestMatchers(HttpMethod.DELETE, "/api/comments/**").authenticated()
|
||||
// Whitelist: Allow everything else (static resources, other APIs)
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
4
backend/src/main/resources/application.properties
Executable file
4
backend/src/main/resources/application.properties
Executable file
@@ -0,0 +1,4 @@
|
||||
spring.datasource.url=jdbc:mysql://124.70.86.207:3306/h_db23373332
|
||||
spring.datasource.username=u23373332
|
||||
spring.datasource.password=
|
||||
jwt.secret=
|
||||
6
backend/src/main/resources/application.properties.example
Executable file
6
backend/src/main/resources/application.properties.example
Executable file
@@ -0,0 +1,6 @@
|
||||
spring.datasource.url=jdbc:mysql://your-host:3306/your-db
|
||||
spring.datasource.username=your-username
|
||||
spring.datasource.password=your-password
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
jwt.secret=24VSSNj6bDX7mwjIsidnJQYg3Lk6Ht9YvqYZYj7aDTc=
|
||||
jwt.secret=your-base64-encoded-secret-key-min-32-bytes
|
||||
148
backend/src/main/resources/init.sql
Executable file
148
backend/src/main/resources/init.sql
Executable file
@@ -0,0 +1,148 @@
|
||||
-- 车手表
|
||||
CREATE TABLE IF NOT EXISTS driver(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
birthday DATE
|
||||
);
|
||||
|
||||
-- 车队表
|
||||
CREATE TABLE IF NOT EXISTS team(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
engine_supplier VARCHAR(100),
|
||||
setup_time YEAR
|
||||
);
|
||||
|
||||
-- 合同表
|
||||
CREATE TABLE IF NOT EXISTS contract(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
season YEAR,
|
||||
car_num INT,
|
||||
is_reserve BOOLEAN,
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 赛道表
|
||||
CREATE TABLE IF NOT EXISTS circuit(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
location VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
drs_zones INT
|
||||
);
|
||||
|
||||
-- 赛事表
|
||||
CREATE TABLE IF NOT EXISTS prix(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
season YEAR,
|
||||
round INT,
|
||||
name VARCHAR(100),
|
||||
circuit_id INT,
|
||||
have_sprint BOOLEAN,
|
||||
FOREIGN KEY(circuit_id) REFERENCES circuit(id)
|
||||
);
|
||||
|
||||
-- 排位赛结果表
|
||||
CREATE TABLE IF NOT EXISTS qualifying_result(
|
||||
prix_id INT,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
fastest_time VARCHAR(20),
|
||||
position INT,
|
||||
is_sprint BOOLEAN,
|
||||
section INT,
|
||||
FOREIGN KEY(prix_id) REFERENCES prix(id),
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 正赛结果表
|
||||
CREATE TABLE IF NOT EXISTS race_result(
|
||||
prix_id INT,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
start_position INT,
|
||||
end_position INT,
|
||||
finish_time VARCHAR(20),
|
||||
fastest_time VARCHAR(20),
|
||||
score INT,
|
||||
is_sprint BOOLEAN,
|
||||
FOREIGN KEY(prix_id) REFERENCES prix(id),
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS user(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(100) UNIQUE,
|
||||
password VARCHAR(100),
|
||||
email VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
avatar VARCHAR(500)
|
||||
);
|
||||
|
||||
-- 评论表
|
||||
CREATE TABLE IF NOT EXISTS comment(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT,
|
||||
response_id INT,
|
||||
root_id INT,
|
||||
content VARCHAR(500),
|
||||
FOREIGN KEY(user_id) REFERENCES user(id),
|
||||
FOREIGN KEY(response_id) REFERENCES comment(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(root_id) REFERENCES comment(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 赛季车手信息视图
|
||||
CREATE OR REPLACE VIEW season_driver(
|
||||
id, name, team, country, birthday, car_num, season
|
||||
) AS
|
||||
SELECT D.id,
|
||||
D.name,
|
||||
T.name,
|
||||
D.country,
|
||||
D.birthday,
|
||||
C.car_num,
|
||||
C.season
|
||||
FROM contract C
|
||||
JOIN team T ON T.id = C.team_id
|
||||
JOIN driver D ON C.driver_id = D.id;
|
||||
|
||||
-- 赛季车队积分榜视图
|
||||
CREATE OR REPLACE VIEW season_team_standings AS
|
||||
SELECT
|
||||
p.season,
|
||||
rr.team_id,
|
||||
SUM(rr.score) AS total_score,
|
||||
RANK() OVER (PARTITION BY p.season ORDER BY SUM(rr.score) DESC) AS ranking
|
||||
FROM race_result rr
|
||||
JOIN prix p ON rr.prix_id = p.id
|
||||
GROUP BY p.season, rr.team_id;
|
||||
|
||||
CREATE OR REPLACE VIEW prix_result AS
|
||||
SELECT
|
||||
p.season,
|
||||
p.id AS prix_id,
|
||||
rr.driver_id,
|
||||
rr.team_id,
|
||||
p.name AS prix_name,
|
||||
p.round,
|
||||
sd.name AS driver_name,
|
||||
sd.car_num,
|
||||
sd.team AS team_name,
|
||||
rr.end_position AS pos,
|
||||
rr.finish_time,
|
||||
rr.score,
|
||||
rr.is_sprint
|
||||
FROM
|
||||
race_result rr
|
||||
JOIN
|
||||
prix p ON rr.prix_id = p.id
|
||||
JOIN
|
||||
season_driver sd ON rr.driver_id = sd.id AND sd.season = p.season;
|
||||
4
backend/target/classes/application.properties
Executable file
4
backend/target/classes/application.properties
Executable file
@@ -0,0 +1,4 @@
|
||||
spring.datasource.url=jdbc:mysql://124.70.86.207:3306/h_db23373332
|
||||
spring.datasource.username=u23373332
|
||||
spring.datasource.password=
|
||||
jwt.secret=
|
||||
6
backend/target/classes/application.properties.example
Executable file
6
backend/target/classes/application.properties.example
Executable file
@@ -0,0 +1,6 @@
|
||||
spring.datasource.url=jdbc:mysql://your-host:3306/your-db
|
||||
spring.datasource.username=your-username
|
||||
spring.datasource.password=your-password
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
jwt.secret=24VSSNj6bDX7mwjIsidnJQYg3Lk6Ht9YvqYZYj7aDTc=
|
||||
jwt.secret=your-base64-encoded-secret-key-min-32-bytes
|
||||
BIN
backend/target/classes/f1/AppConfig.class
Normal file
BIN
backend/target/classes/f1/AppConfig.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/Main.class
Normal file
BIN
backend/target/classes/f1/Main.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/controller/AuthController.class
Normal file
BIN
backend/target/classes/f1/controller/AuthController.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/controller/CommentController.class
Normal file
BIN
backend/target/classes/f1/controller/CommentController.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/controller/F1Controller.class
Normal file
BIN
backend/target/classes/f1/controller/F1Controller.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/controller/UserController.class
Normal file
BIN
backend/target/classes/f1/controller/UserController.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/BaseDao.class
Normal file
BIN
backend/target/classes/f1/db/BaseDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/Database.class
Normal file
BIN
backend/target/classes/f1/db/Database.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/DriverDao.class
Normal file
BIN
backend/target/classes/f1/db/DriverDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/RaceDao.class
Normal file
BIN
backend/target/classes/f1/db/RaceDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/TeamDao.class
Normal file
BIN
backend/target/classes/f1/db/TeamDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/db/UserDao.class
Normal file
BIN
backend/target/classes/f1/db/UserDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/CommentItem.class
Normal file
BIN
backend/target/classes/f1/entity/CommentItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/Driver.class
Normal file
BIN
backend/target/classes/f1/entity/Driver.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/DriverHistoryItem.class
Normal file
BIN
backend/target/classes/f1/entity/DriverHistoryItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/DriverStatistic.class
Normal file
BIN
backend/target/classes/f1/entity/DriverStatistic.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/QualifyingResultItem.class
Normal file
BIN
backend/target/classes/f1/entity/QualifyingResultItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/RaceResultItem.class
Normal file
BIN
backend/target/classes/f1/entity/RaceResultItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/SeasonDriver.class
Normal file
BIN
backend/target/classes/f1/entity/SeasonDriver.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/SeasonScheduleItem.class
Normal file
BIN
backend/target/classes/f1/entity/SeasonScheduleItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/Team.class
Normal file
BIN
backend/target/classes/f1/entity/Team.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/TeamHistoryItem.class
Normal file
BIN
backend/target/classes/f1/entity/TeamHistoryItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/TeamStatistic.class
Normal file
BIN
backend/target/classes/f1/entity/TeamStatistic.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/entity/User.class
Normal file
BIN
backend/target/classes/f1/entity/User.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/security/JwtFilter.class
Normal file
BIN
backend/target/classes/f1/security/JwtFilter.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/security/JwtUtil.class
Normal file
BIN
backend/target/classes/f1/security/JwtUtil.class
Normal file
Binary file not shown.
BIN
backend/target/classes/f1/security/SecurityConfig.class
Normal file
BIN
backend/target/classes/f1/security/SecurityConfig.class
Normal file
Binary file not shown.
148
backend/target/classes/init.sql
Executable file
148
backend/target/classes/init.sql
Executable file
@@ -0,0 +1,148 @@
|
||||
-- 车手表
|
||||
CREATE TABLE IF NOT EXISTS driver(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
birthday DATE
|
||||
);
|
||||
|
||||
-- 车队表
|
||||
CREATE TABLE IF NOT EXISTS team(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
engine_supplier VARCHAR(100),
|
||||
setup_time YEAR
|
||||
);
|
||||
|
||||
-- 合同表
|
||||
CREATE TABLE IF NOT EXISTS contract(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
season YEAR,
|
||||
car_num INT,
|
||||
is_reserve BOOLEAN,
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 赛道表
|
||||
CREATE TABLE IF NOT EXISTS circuit(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
location VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
drs_zones INT
|
||||
);
|
||||
|
||||
-- 赛事表
|
||||
CREATE TABLE IF NOT EXISTS prix(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
season YEAR,
|
||||
round INT,
|
||||
name VARCHAR(100),
|
||||
circuit_id INT,
|
||||
have_sprint BOOLEAN,
|
||||
FOREIGN KEY(circuit_id) REFERENCES circuit(id)
|
||||
);
|
||||
|
||||
-- 排位赛结果表
|
||||
CREATE TABLE IF NOT EXISTS qualifying_result(
|
||||
prix_id INT,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
fastest_time VARCHAR(20),
|
||||
position INT,
|
||||
is_sprint BOOLEAN,
|
||||
section INT,
|
||||
FOREIGN KEY(prix_id) REFERENCES prix(id),
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 正赛结果表
|
||||
CREATE TABLE IF NOT EXISTS race_result(
|
||||
prix_id INT,
|
||||
driver_id INT,
|
||||
team_id INT,
|
||||
start_position INT,
|
||||
end_position INT,
|
||||
finish_time VARCHAR(20),
|
||||
fastest_time VARCHAR(20),
|
||||
score INT,
|
||||
is_sprint BOOLEAN,
|
||||
FOREIGN KEY(prix_id) REFERENCES prix(id),
|
||||
FOREIGN KEY(driver_id) REFERENCES driver(id),
|
||||
FOREIGN KEY(team_id) REFERENCES team(id)
|
||||
);
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS user(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(100) UNIQUE,
|
||||
password VARCHAR(100),
|
||||
email VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
avatar VARCHAR(500)
|
||||
);
|
||||
|
||||
-- 评论表
|
||||
CREATE TABLE IF NOT EXISTS comment(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT,
|
||||
response_id INT,
|
||||
root_id INT,
|
||||
content VARCHAR(500),
|
||||
FOREIGN KEY(user_id) REFERENCES user(id),
|
||||
FOREIGN KEY(response_id) REFERENCES comment(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(root_id) REFERENCES comment(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 赛季车手信息视图
|
||||
CREATE OR REPLACE VIEW season_driver(
|
||||
id, name, team, country, birthday, car_num, season
|
||||
) AS
|
||||
SELECT D.id,
|
||||
D.name,
|
||||
T.name,
|
||||
D.country,
|
||||
D.birthday,
|
||||
C.car_num,
|
||||
C.season
|
||||
FROM contract C
|
||||
JOIN team T ON T.id = C.team_id
|
||||
JOIN driver D ON C.driver_id = D.id;
|
||||
|
||||
-- 赛季车队积分榜视图
|
||||
CREATE OR REPLACE VIEW season_team_standings AS
|
||||
SELECT
|
||||
p.season,
|
||||
rr.team_id,
|
||||
SUM(rr.score) AS total_score,
|
||||
RANK() OVER (PARTITION BY p.season ORDER BY SUM(rr.score) DESC) AS ranking
|
||||
FROM race_result rr
|
||||
JOIN prix p ON rr.prix_id = p.id
|
||||
GROUP BY p.season, rr.team_id;
|
||||
|
||||
CREATE OR REPLACE VIEW prix_result AS
|
||||
SELECT
|
||||
p.season,
|
||||
p.id AS prix_id,
|
||||
rr.driver_id,
|
||||
rr.team_id,
|
||||
p.name AS prix_name,
|
||||
p.round,
|
||||
sd.name AS driver_name,
|
||||
sd.car_num,
|
||||
sd.team AS team_name,
|
||||
rr.end_position AS pos,
|
||||
rr.finish_time,
|
||||
rr.score,
|
||||
rr.is_sprint
|
||||
FROM
|
||||
race_result rr
|
||||
JOIN
|
||||
prix p ON rr.prix_id = p.id
|
||||
JOIN
|
||||
season_driver sd ON rr.driver_id = sd.id AND sd.season = p.season;
|
||||
@@ -0,0 +1,27 @@
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/Driver.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/QualifyingResultItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/controller/UserController.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/Database.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/security/SecurityConfig.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/security/JwtFilter.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/Main.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/TeamDao.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/User.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/AppConfig.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/SeasonDriver.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/security/JwtUtil.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/CommentItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/RaceResultItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/controller/AuthController.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/DriverStatistic.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/UserDao.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/TeamHistoryItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/Team.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/DriverDao.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/TeamStatistic.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/SeasonScheduleItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/entity/DriverHistoryItem.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/controller/CommentController.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/RaceDao.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/controller/F1Controller.java
|
||||
/Users/colden/编程/web/formula1/backend/src/main/java/f1/db/BaseDao.java
|
||||
Reference in New Issue
Block a user