Refactor database structure and improve player handling.

Reorganized database-related code into a dedicated module, added mappings for UUID handling, and updated SQL queries for clarity. Enhanced team members API to use player data directly, ensuring consistency and better handling of UUIDs. Introduced new database table for connection settings and adjusted Gradle configurations for modularization.
This commit is contained in:
Teriuihi 2025-04-10 01:22:19 +02:00
parent 67a3162ae3
commit d5daa8458b
17 changed files with 191 additions and 37 deletions

2
.gitignore vendored
View File

@ -88,3 +88,5 @@ package-lock.json
### MINE ###
frontend/src/api
generated
liquibase*.properties

View File

@ -2,6 +2,7 @@ plugins {
java
id("org.springframework.boot") version "3.4.4"
id("io.spring.dependency-management") version "1.1.7"
// id("com.github.johnrengelman.shadow") version "8.1.1"
}
group = "com.alttd.altitudeweb"
@ -25,16 +26,62 @@ repositories {
dependencies {
implementation(project(":open_api"))
implementation(project(":database"))
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("com.mysql:mysql-connector-j:8.0.32")
implementation("org.mybatis:mybatis:3.5.13")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.springframework.boot:spring-boot-configuration-processor")
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.bootJar {
mainClass.set("com.alttd.altitudeweb.AltitudeWebApplication")
archiveBaseName.set("altitudeweb")
archiveClassifier.set("")
}
//tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
// mergeServiceFiles()
// dependencies {
// include(dependency("com.mysql:mysql-connector-j"))
// }
//}
//tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
// manifest {
// attributes["Main-Class"] = "com.alttd.altitudeweb.AltitudeWebApplication"
// }
// archiveBaseName.set("altitudeweb")
// archiveClassifier.set("")
// mergeServiceFiles()
//
// // Include everything
// from(sourceSets.main.get().output)
//
// // Include all project dependencies
// configurations = listOf(project.configurations.runtimeClasspath.get())
//
// // Ensure MySQL is included (even though it should be part of runtimeClasspath already)
// dependencies {
// include(dependency("com.mysql:mysql-connector-j"))
// }
//
// // Enable zip64 mode for large JARs
// isZip64 = true
//}
//
//// Make the shadowJar task the default jar task
//tasks.named("jar") {
// enabled = false
//}
//
//tasks.named("assemble") {
// dependsOn("shadowJar")
//}

View File

@ -1,18 +1,39 @@
package com.alttd.altitudeweb.controllers;
import com.alttd.altitudeweb.api.TeamApi;
import com.alttd.altitudeweb.database.Connection;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.luckperms.Player;
import com.alttd.altitudeweb.database.luckperms.TeamMemberMapper;
import com.alttd.altitudeweb.model.PlayerDto;
import com.alttd.altitudeweb.model.TeamMemberDto;
import com.alttd.altitudeweb.model.TeamMembersDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Slf4j
@RestController
public class TeamApiController implements TeamApi {
@Override
public ResponseEntity<TeamMembersDto> getTeamMembers(String group) {
TeamMembersDto teamMemberDtos = new TeamMembersDto();
teamMemberDtos.add(new TeamMemberDto("test", "good"));
CompletableFuture<List<Player>> playerGroupFuture = new CompletableFuture<>();
Connection.getConnection(Databases.LUCK_PERMS, configuration -> configuration.addMapper(TeamMemberMapper.class))
.thenApply(connection -> {
connection.runQuery(sqlSession -> {
log.info("Loading team members for group {}", group);
List<Player> players = sqlSession.getMapper(TeamMemberMapper.class).getTeamMembers("group." + group);
playerGroupFuture.complete(players);
});
return connection;
});
List<Player> join = playerGroupFuture.join();
join.forEach(player -> teamMemberDtos.add(new PlayerDto(player.username(), player.uuid().toString())));
return ResponseEntity.ok().body(teamMemberDtos);
}
}

View File

@ -1,3 +0,0 @@
package com.alttd.altitudeweb.database.luckperms;
public record PlayerGroup(String username, String groupName) {}

View File

@ -1,14 +0,0 @@
package com.alttd.altitudeweb.database.luckperms;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface TeamMemberMapper {
@Select("""
SELECT players.username, #{groupName} AS group_name
FROM luckperms_user_permissions AS permissions
INNER JOIN luckperms_players AS players ON players.uuid = permissions.uuid
WHERE permission = 'group.'#{groupName}""")
List<PlayerGroup> getTeamMembers(String groupName);
}

23
database/build.gradle.kts Normal file
View File

@ -0,0 +1,23 @@
plugins {
id("java")
}
group = "com.alttd.altitudeweb"
version = "0.0.1-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation(project(":open_api"))
compileOnly("org.projectlombok:lombok:1.18.38")
annotationProcessor("org.projectlombok:lombok:1.18.38")
implementation("org.mybatis:mybatis:3.5.13")
compileOnly("org.slf4j:slf4j-api:2.0.17")
compileOnly("org.slf4j:slf4j-simple:2.0.17")
}
tasks.test {
useJUnitPlatform()
}

View File

@ -0,0 +1,6 @@
package com.alttd.altitudeweb;
public class Main {
public static void main(String[] args) {
}
}

View File

@ -41,7 +41,7 @@ public class Connection {
return loadDefaultDatabase(addMappers);
}
CompletableFuture<DatabaseSettings> settingsFuture = new CompletableFuture<>();
getConnection(Databases.DEFAULT, (mapper -> mapper.addMapper(DatabaseSettings.class))).thenApply(connection -> {
getConnection(Databases.DEFAULT, (mapper -> mapper.addMapper(SettingsMapper.class))).thenApply(connection -> {
connection.runQuery(session -> {
DatabaseSettings loadedSettings = session.getMapper(SettingsMapper.class).getSettings(database.getInternalName());
settingsFuture.complete(loadedSettings);
@ -76,7 +76,7 @@ public class Connection {
try (SqlSession session = sqlSessionFactory.openSession()) {
consumer.accept(session);
} catch (Exception e) {
log.error("Failed to run discord query", e);
log.error("Failed to run query", e);
}
}).start();
}

View File

@ -0,0 +1,7 @@
package com.alttd.altitudeweb.database.luckperms;
import java.util.UUID;
public record Player(String username, UUID uuid) {
}

View File

@ -0,0 +1,21 @@
package com.alttd.altitudeweb.database.luckperms;
import com.alttd.altitudeweb.type_handler.UUIDTypeHandler;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.UUID;
public interface TeamMemberMapper {
@ConstructorArgs({
@Arg(column = "username", javaType = String.class),
@Arg(column = "uuid", javaType = UUID.class, typeHandler = UUIDTypeHandler.class)
})
@Select("""
SELECT players.username, players.uuid
FROM luckperms_user_permissions AS permissions
INNER JOIN luckperms_players AS players ON players.uuid = permissions.uuid
WHERE permission = #{groupPermission}
""")
List<Player> getTeamMembers(@Param("groupPermission") String groupPermission);
}

View File

@ -3,6 +3,6 @@ package com.alttd.altitudeweb.database.web_db;
import org.apache.ibatis.annotations.Select;
public interface SettingsMapper {
@Select("SELECT * FROM database_settings WHERE name = #{database}")
@Select("SELECT host, port, name, username, password FROM db_connection_settings WHERE name = #{database}")
DatabaseSettings getSettings(String database);
}

View File

@ -0,0 +1,38 @@
package com.alttd.altitudeweb.type_handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
@MappedTypes(UUID.class)
public class UUIDTypeHandler extends BaseTypeHandler<UUID> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.toString());
}
@Override
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
String uuid = rs.getString(columnName);
return uuid == null ? null : UUID.fromString(uuid);
}
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String uuid = rs.getString(columnIndex);
return uuid == null ? null : UUID.fromString(uuid);
}
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String uuid = cs.getString(columnIndex);
return uuid == null ? null : UUID.fromString(uuid);
}
}

View File

@ -0,0 +1,10 @@
CREATE TABLE db_connection_settings
(
internal_name VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
host VARCHAR(255) NOT NULL,
port INT NOT NULL,
CONSTRAINT pk_internal_name PRIMARY KEY (internal_name)
);

View File

@ -65,12 +65,6 @@ paths:
$ref: "#/components/schemas/Error"
components:
schemas:
Player:
type: object
properties:
uuid:
type: string
example: 0c35e520-927e-4c6a-87ad-ff0739c22e9d
PlayerHistory:
type: object
properties:
@ -89,19 +83,21 @@ components:
TeamMembers:
type: array
items:
$ref: '#/components/schemas/TeamMember'
TeamMember:
$ref: '#/components/schemas/Player'
Player:
type: object
properties:
name:
type: string
description: The name of the team member
group:
description: The name of the player
example: a_name
uuid:
type: string
description: The group to which the team member belongs
example: 0c35e520-927e-4c6a-87ad-ff0739c22e9d
description: The uuid of the team player
required:
- name
- group
- uuid
Error:
type: object
properties:

View File

@ -1,2 +1,2 @@
rootProject.name = "AltitudeWeb"
include("open_api", "backend", "frontend")
include("open_api", "backend", "frontend", "database")