Compare commits
6 Commits
master
...
44601131a2
| Author | SHA1 | Date | |
|---|---|---|---|
| 44601131a2 | |||
| 6fb1c9fb2e | |||
| e5f4f423f4 | |||
| 77a1690e08 | |||
| 8c99ba1147 | |||
| fcb5285788 |
+3
-3
@@ -1,14 +1,14 @@
|
||||
# 使用 Kotlin + JDK 的輕量化映像
|
||||
FROM gradle:7.5.1-jdk17-alpine AS builder
|
||||
FROM gradle:9.5.1-jdk25 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN gradle clean shadowJar --no-daemon
|
||||
|
||||
# 運行階段
|
||||
FROM eclipse-temurin:21-jdk-ubi10-minimal
|
||||
FROM eclipse-temurin:25-jdk-ubi10-minimal
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /app/build/libs/*.jar app.jar
|
||||
|
||||
CMD ["java", "-jar", "app.jar", "com.ray650128.pcredive.ApplicationKt"]
|
||||
CMD ["java", "-jar", "app.jar", "club.sakunadaisuki.pcrediveclanrecordbackend.mainKt"]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# PcReDiveClanRecordBackend2
|
||||
|
||||
This project was created using the [Ktor Project Generator](https://start.ktor.io).
|
||||
|
||||
Here are some useful links to get you started:
|
||||
|
||||
* [Ktor Documentation](https://ktor.io/docs/home.html)
|
||||
* [Ktor GitHub page](https://github.com/ktorio/ktor)
|
||||
* [Ktor Slack chat](https://app.slack.com/client/T09229ZC6/C0A974TJ9). [Request an invite](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up).
|
||||
|
||||
## Features
|
||||
|
||||
Here's a list of features included in this project:
|
||||
|
||||
| Name | Description |
|
||||
|---------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||
| [CORS](https://start.ktor.io/p/io.ktor/server-cors) | Enables Cross-Origin Resource Sharing (CORS) |
|
||||
| [Swagger](https://start.ktor.io/p/io.ktor/server-swagger) | Serves Swagger UI for your project |
|
||||
| [Content Negotiation](https://start.ktor.io/p/io.ktor/server-content-negotiation) | Provides automatic content conversion according to Content-Type and Accept headers |
|
||||
| [kotlinx.serialization](https://start.ktor.io/p/io.ktor/server-kotlinx-serialization) | Handles JSON serialization using kotlinx.serialization library |
|
||||
| [MongoDB](https://start.ktor.io/p/org.jetbrains/server-mongodb) | Adds MongoDB database support |
|
||||
|
||||
## Building & Running
|
||||
|
||||
To build or run the project, use one of the following tasks:
|
||||
|
||||
| Task | Description |
|
||||
|-------------------|-------------------|
|
||||
| `./gradlew test` | Run the tests |
|
||||
| `./gradlew build` | Build the project |
|
||||
| `./gradlew run` | Run the server |
|
||||
|
||||
If the server starts successfully, you'll see the following output:
|
||||
|
||||
```
|
||||
2024-12-04 14:32:45.584 [main] INFO Application - Application started in 0.303 seconds.
|
||||
2024-12-04 14:32:45.682 [main] INFO Application - Responding at http://0.0.0.0:8080
|
||||
```
|
||||
+23
-45
@@ -1,55 +1,33 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
val ktor_version: String by project
|
||||
val kotlin_version: String by project
|
||||
val logback_version: String by project
|
||||
val kmongo_version: String by project
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.8.22"
|
||||
id("io.ktor.plugin") version "2.3.1"
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.22"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(ktorLibs.plugins.ktor)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
group = "com.ray650128.pcredive"
|
||||
version = "0.0.1"
|
||||
group = "club.sakunadaisuki"
|
||||
version = "1.0.0-SNAPSHOT"
|
||||
|
||||
application {
|
||||
mainClass.set("io.ktor.server.netty.EngineMain")
|
||||
|
||||
val isDevelopment: Boolean = project.ext.has("development")
|
||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||
mainClass = "io.ktor.server.netty.EngineMain"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-server-cors-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||
implementation(ktorLibs.serialization.kotlinx.json)
|
||||
implementation(ktorLibs.server.config.yaml)
|
||||
implementation(ktorLibs.server.contentNegotiation)
|
||||
implementation(ktorLibs.server.core)
|
||||
implementation(ktorLibs.server.cors)
|
||||
implementation(ktorLibs.server.netty)
|
||||
implementation(ktorLibs.server.routingOpenapi)
|
||||
implementation(ktorLibs.server.swagger)
|
||||
implementation(libs.logback.classic)
|
||||
implementation(libs.mongodb.bson)
|
||||
implementation(libs.mongodb.driverCore)
|
||||
implementation(libs.mongodb.driverSync)
|
||||
|
||||
implementation("org.litote.kmongo:kmongo:$kmongo_version")
|
||||
implementation("org.litote.kmongo:kmongo-id-serialization:$kmongo_version")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes["Main-Class"] = "com.ray650128.bot.MainKt"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
dependsOn(tasks.shadowJar)
|
||||
testImplementation(kotlin("test"))
|
||||
testImplementation(ktorLibs.server.testHost)
|
||||
}
|
||||
|
||||
+1
-4
@@ -1,5 +1,2 @@
|
||||
ktor_version=2.3.1
|
||||
kotlin_version=1.8.22
|
||||
logback_version=1.2.11
|
||||
kotlin.code.style=official
|
||||
kmongo_version=4.11.0
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[versions]
|
||||
kotlin = "2.4.0"
|
||||
logback = "1.5.35"
|
||||
mongodb = "5.8.0"
|
||||
|
||||
[libraries]
|
||||
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||
mongodb-bson = { module = "org.mongodb:bson", version.ref = "mongodb" }
|
||||
mongodb-driverCore = { module = "org.mongodb:mongodb-driver-core", version.ref = "mongodb" }
|
||||
mongodb-driverSync = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongodb" }
|
||||
|
||||
[plugins]
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
Vendored
BIN
Binary file not shown.
+5
-1
@@ -1,5 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
# gradlew start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
@@ -27,7 +29,7 @@
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
# ksh gradlew
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +82,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -133,22 +132,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -165,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
Vendored
+28
-35
@@ -13,19 +13,22 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem gradlew startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,15 +43,15 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
@@ -56,34 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
@rem Execute gradlew
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
+22
-1
@@ -1 +1,22 @@
|
||||
rootProject.name = "PcReDivePveRecordBackend"
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("ktorLibs").from("io.ktor:ktor-version-catalog:3.5.0")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "PcReDiveClanRecordBackend"
|
||||
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.database.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.bson.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class BattleRecordDto(
|
||||
val date: String, // yyyy-MM-dd
|
||||
var uid: String,
|
||||
@SerialName("player_name") val playerName: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
@SerialName("attack_detail_list") val attackDetailList: List<AttackDetailDto>
|
||||
) {
|
||||
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
|
||||
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromDocument(document: Document): BattleRecordDto = json.decodeFromString(document.toJson())
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AttackDetailDto(
|
||||
@SerialName("boss_round") val bossRound: Int, // 周目
|
||||
@SerialName("boss_index") val bossIndex: Int, // 1~5 號王
|
||||
val damage: Long, // 傷害
|
||||
@SerialName("is_compensation") val isCompensation: Boolean = false, // true: 補償刀, false: 正刀
|
||||
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
|
||||
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
|
||||
) {
|
||||
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
|
||||
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromDocument(document: Document): AttackDetailDto = json.decodeFromString(document.toJson())
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.database.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.bson.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class MemberDto(
|
||||
var uid: String,
|
||||
@SerialName("player_name") val playerName: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
var leave: Boolean = false,
|
||||
@SerialName("created_at") var createAt: Long? = null,
|
||||
@SerialName("updated_at") var updatedAt: Long? = null
|
||||
) {
|
||||
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
|
||||
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromDocument(document: Document): MemberDto = json.decodeFromString(document.toJson())
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.database.service
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||
import com.mongodb.client.MongoCollection
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import com.mongodb.client.model.Filters
|
||||
import com.mongodb.client.model.Filters.and
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.bson.Document
|
||||
|
||||
class BattleRecordService(private val database: MongoDatabase) {
|
||||
var collection: MongoCollection<Document>
|
||||
|
||||
init {
|
||||
database.createCollection("battle_records")
|
||||
collection = database.getCollection("battle_records")
|
||||
}
|
||||
|
||||
// Create new car
|
||||
suspend fun create(data: BattleRecordDto): BattleRecordDto = withContext(Dispatchers.IO) {
|
||||
val doc = data.toDocument()
|
||||
collection.insertOne(doc)
|
||||
doc.let(BattleRecordDto::fromDocument)
|
||||
}
|
||||
|
||||
// Read a car
|
||||
suspend fun read(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
|
||||
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument)
|
||||
}
|
||||
|
||||
// Read a car
|
||||
suspend fun read(date: String, uid: String): BattleRecordDto? = withContext(Dispatchers.IO) {
|
||||
val filter = and(
|
||||
Filters.eq("date", date),
|
||||
Filters.eq("uid", uid)
|
||||
)
|
||||
collection.find(filter).first()?.let(BattleRecordDto::fromDocument)
|
||||
}
|
||||
|
||||
// Update a car
|
||||
suspend fun update(date: String, data: BattleRecordDto): BattleRecordDto? = withContext(Dispatchers.IO) {
|
||||
val filter = and(
|
||||
Filters.eq("date", date),
|
||||
Filters.eq("uid", data.uid)
|
||||
)
|
||||
collection.findOneAndReplace(filter, data.toDocument())
|
||||
collection.find(filter).first()?.let(BattleRecordDto::fromDocument)
|
||||
}
|
||||
|
||||
// Delete a car
|
||||
suspend fun delete(date: String, uid: String): Document? = withContext(Dispatchers.IO) {
|
||||
val filter = and(
|
||||
Filters.eq("date", date),
|
||||
Filters.eq("uid", uid)
|
||||
)
|
||||
collection.findOneAndDelete(filter)
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.database.service
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
|
||||
import com.mongodb.client.MongoCollection
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import com.mongodb.client.model.Filters
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.bson.Document
|
||||
|
||||
class MemberService(private val database: MongoDatabase) {
|
||||
var collection: MongoCollection<Document>
|
||||
|
||||
init {
|
||||
database.createCollection("member")
|
||||
collection = database.getCollection("member")
|
||||
}
|
||||
|
||||
// Create new member
|
||||
suspend fun create(data: MemberDto): String = withContext(Dispatchers.IO) {
|
||||
data.createAt = System.currentTimeMillis()
|
||||
data.updatedAt = System.currentTimeMillis()
|
||||
data.leave = false
|
||||
val doc = data.toDocument()
|
||||
collection.insertOne(doc)
|
||||
doc["uid"].toString()
|
||||
}
|
||||
|
||||
// Read a member
|
||||
suspend fun read(): List<MemberDto> = withContext(Dispatchers.IO) {
|
||||
collection.find().toList().map(MemberDto::fromDocument)
|
||||
}
|
||||
|
||||
// Read a member
|
||||
suspend fun read(uid: String): MemberDto? = withContext(Dispatchers.IO) {
|
||||
collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)
|
||||
}
|
||||
|
||||
// Update a member
|
||||
suspend fun update(uid: String, data: MemberDto): Document? = withContext(Dispatchers.IO) {
|
||||
collection.findOneAndReplace(Filters.eq("uid", uid), data.toDocument())
|
||||
}
|
||||
|
||||
// Delete a member
|
||||
suspend fun delete(uid: String): Document? = withContext(Dispatchers.IO) {
|
||||
collection.findOneAndDelete(Filters.eq("uid", uid))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend
|
||||
|
||||
import io.ktor.server.netty.EngineMain
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
EngineMain.main(args)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.mapper
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.AttackDetailDto
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.AttackDetail
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecord
|
||||
|
||||
fun BattleRecord.toDto(): BattleRecordDto = BattleRecordDto(
|
||||
date = date,
|
||||
uid = uid,
|
||||
playerName = playerName,
|
||||
discordName = discordName,
|
||||
discordID = discordID,
|
||||
attackDetailList = attackDetailList.map { it.toDto() },
|
||||
)
|
||||
|
||||
fun AttackDetail.toDto(): AttackDetailDto = AttackDetailDto(
|
||||
bossRound = bossRound,
|
||||
bossIndex = bossIndex,
|
||||
damage = damage,
|
||||
isCompensation = isCompensation,
|
||||
createdAt = createdAt,
|
||||
updatedAt = updatedAt,
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.mapper
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.Member
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.MemberRequest
|
||||
|
||||
fun Member.toDto(): MemberDto {
|
||||
return MemberDto(
|
||||
uid = uid,
|
||||
playerName = playerName,
|
||||
discordName = discordName,
|
||||
discordID = discordID,
|
||||
leave = leave,
|
||||
createAt = createAt,
|
||||
updatedAt = updatedAt,
|
||||
)
|
||||
}
|
||||
|
||||
fun MemberRequest.toDto(): MemberDto {
|
||||
return MemberDto(
|
||||
uid = uid,
|
||||
playerName = playerName,
|
||||
discordName = discordName,
|
||||
discordID = discordID,
|
||||
leave = leave,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.AttackDetailDto
|
||||
import kotlinx.serialization.SerialName
|
||||
import java.time.Instant
|
||||
|
||||
data class BattleRecord(
|
||||
val date: String, // yyyy-MM-dd
|
||||
var uid: String,
|
||||
@SerialName("player_name") val playerName: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
@SerialName("attack_detail_list") val attackDetailList: List<AttackDetail>
|
||||
)
|
||||
|
||||
data class AttackDetail(
|
||||
@SerialName("boss_round") val bossRound: Int, // 周目
|
||||
@SerialName("boss_index") val bossIndex: Int, // 1~5 號王
|
||||
val damage: Long, // 傷害
|
||||
@SerialName("is_compensation") val isCompensation: Boolean = false, // true: 補償刀, false: 正刀
|
||||
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
|
||||
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BattleRecordRequest(
|
||||
val date: String,
|
||||
val uid: String,
|
||||
@SerialName("player_name") val name: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
val round: Int,
|
||||
val boss: Int,
|
||||
val damage: Long,
|
||||
@SerialName("is_compensation") val isCompensation: Boolean = false,
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Member(
|
||||
var uid: String,
|
||||
@SerialName("player_name") val playerName: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
var leave: Boolean = false,
|
||||
@SerialName("created_at") var createAt: Long? = null,
|
||||
@SerialName("updated_at") var updatedAt: Long? = null
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MemberRequest(
|
||||
var uid: String,
|
||||
@SerialName("player_name") val playerName: String,
|
||||
@SerialName("discord_name") val discordName: String,
|
||||
@SerialName("discord_id") val discordID: String,
|
||||
var leave: Boolean = false,
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.plugins.swagger.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureHttp() {
|
||||
install(CORS) {
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Put)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
allowMethod(HttpMethod.Patch)
|
||||
allowHeader(HttpHeaders.Authorization)
|
||||
allowHeader("MyCustomHeader")
|
||||
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
||||
}
|
||||
routing {
|
||||
swaggerUI(path = "openapi") {
|
||||
/*
|
||||
Documentation source configuration goes here.
|
||||
|
||||
This can be from file (documentation.yaml), or it can be served dynamically from your sources using the
|
||||
`describe {}` API on routes. When `openApi` enabled in Gradle, these calls will be automatically injected
|
||||
based on your code and comments.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
|
||||
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.BattleRecordService
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.Member
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.MemberService
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.mapper.toDto
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.AttackDetail
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecordRequest
|
||||
import club.sakunadaisuki.pcrediveclanrecordbackend.model.MemberRequest
|
||||
import com.mongodb.client.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.config.tryGetString
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureMongo() {
|
||||
// Connect to your mongo instance
|
||||
val mongoDatabase = connectToMongoDB()
|
||||
val memberService = runCatching {
|
||||
MemberService(mongoDatabase)
|
||||
}.getOrNull() ?: return
|
||||
val battleRecordService = runCatching {
|
||||
BattleRecordService(mongoDatabase)
|
||||
}.getOrNull() ?: return
|
||||
|
||||
routing {
|
||||
route("/api") {
|
||||
// Read member
|
||||
get("/member") {
|
||||
memberService.read().let { member ->
|
||||
call.respond(HttpStatusCode.OK, member)
|
||||
}
|
||||
}
|
||||
// Read member
|
||||
get("/member/{uid}") {
|
||||
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
|
||||
memberService.read(uid)?.let { member ->
|
||||
call.respond(HttpStatusCode.OK, member)
|
||||
} ?: call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
// Create member
|
||||
post("/member") {
|
||||
val member = call.receive<MemberRequest>()
|
||||
val id = memberService.create(member.toDto())
|
||||
call.respond(HttpStatusCode.Created, id)
|
||||
}
|
||||
// Update member
|
||||
put("/member/{uid}") {
|
||||
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
|
||||
val member = call.receive<Member>()
|
||||
memberService.update(uid, member.toDto())?.let {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
} ?: call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
// Delete member
|
||||
delete("/member/{uid}") {
|
||||
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
|
||||
memberService.delete(uid)?.let {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
} ?: call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
|
||||
// TODO Battle Record
|
||||
get("/battle_record/{date}") {
|
||||
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
|
||||
battleRecordService.read(date).let { battleRecord ->
|
||||
call.respond(HttpStatusCode.OK, battleRecord)
|
||||
}
|
||||
}
|
||||
|
||||
post("/battle_record") {
|
||||
val request = call.receive<BattleRecordRequest>()
|
||||
val newAttackDetail = AttackDetail(
|
||||
bossRound = request.round,
|
||||
bossIndex = request.boss,
|
||||
damage = request.damage,
|
||||
isCompensation = request.isCompensation,
|
||||
)
|
||||
val existData = battleRecordService.read(request.date, request.uid)
|
||||
if (existData != null) {
|
||||
val attackDetails = existData.attackDetailList.toMutableList()
|
||||
attackDetails.add(newAttackDetail.toDto())
|
||||
val newData = existData.copy(
|
||||
attackDetailList = attackDetails
|
||||
)
|
||||
battleRecordService.update(request.date, newData)?.let {
|
||||
call.respond(HttpStatusCode.OK, it)
|
||||
} ?: call.respond(HttpStatusCode.NotFound)
|
||||
} else {
|
||||
val newData = BattleRecordDto(
|
||||
date = request.date,
|
||||
uid = request.uid,
|
||||
playerName = request.name,
|
||||
discordName = request.discordName,
|
||||
discordID = request.discordID,
|
||||
attackDetailList = listOf(newAttackDetail.toDto())
|
||||
)
|
||||
val id = battleRecordService.create(newData)
|
||||
call.respond(HttpStatusCode.Created, id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes connection with a MongoDB database.
|
||||
*
|
||||
* The following configuration properties (in application.yaml/application.conf) can be specified:
|
||||
* * `db.mongo.user` username for your database
|
||||
* * `db.mongo.password` password for the user
|
||||
* * `db.mongo.host` host that will be used for the database connection
|
||||
* * `db.mongo.port` port that will be used for the database connection
|
||||
* * `db.mongo.maxPoolSize` maximum number of connections to a MongoDB server
|
||||
* * `db.mongo.database.name` name of the database
|
||||
*
|
||||
* IMPORTANT NOTE: in order to make MongoDB connection working, you have to start a MongoDB server first.
|
||||
* See the instructions here: https://www.mongodb.com/docs/manual/administration/install-community/
|
||||
* all the paramaters above
|
||||
*
|
||||
* @returns [MongoDatabase] instance
|
||||
* */
|
||||
fun Application.connectToMongoDB(): MongoDatabase {
|
||||
val user = environment.config.tryGetString("db.mongo.user")
|
||||
val password = environment.config.tryGetString("db.mongo.password")
|
||||
val host = environment.config.tryGetString("db.mongo.host") ?: "127.0.0.1"
|
||||
val port = environment.config.tryGetString("db.mongo.port") ?: "27017"
|
||||
val maxPoolSize = environment.config.tryGetString("db.mongo.maxPoolSize")?.toInt() ?: 20
|
||||
val databaseName = environment.config.tryGetString("db.mongo.database.name") ?: "pc_re_dive_clan_battle"
|
||||
|
||||
val credentials = user?.let { userVal -> password?.let { passwordVal -> "$userVal:$passwordVal@" } }.orEmpty()
|
||||
val uri = "mongodb://$credentials$host:$port/?maxPoolSize=$maxPoolSize&w=majority"
|
||||
|
||||
val mongoClient = MongoClients.create(uri)
|
||||
val database = mongoClient.getDatabase(databaseName)
|
||||
|
||||
monitor.subscribe(ApplicationStopped) {
|
||||
mongoClient.close()
|
||||
}
|
||||
|
||||
return database
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureRouting() {
|
||||
routing {
|
||||
get("/") {
|
||||
call.respondText("Hello, World!")
|
||||
}
|
||||
get("/json/kotlinx-serialization") {
|
||||
call.respond(mapOf("hello" to "world"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.ray650128.pcredive
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import ch.qos.logback.classic.LoggerContext
|
||||
import io.ktor.server.application.*
|
||||
import com.ray650128.pcredive.plugins.*
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
|
||||
val rootLogger = loggerContext.getLogger("org.mongodb.driver")
|
||||
rootLogger.level = Level.OFF
|
||||
io.ktor.server.netty.EngineMain.main(args)
|
||||
}
|
||||
|
||||
@Suppress("unused") // application.conf references the main function. This annotation prevents the IDE from marking it as unused.
|
||||
fun Application.module() {
|
||||
configureUpdateTimer()
|
||||
configureHTTP()
|
||||
configureSerialization()
|
||||
configureMemberRouting()
|
||||
configureMemberRecordRouting()
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.ray650128.pcredive.database.dto
|
||||
|
||||
import com.ray650128.pcredive.database.model.Member
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MemberDto(
|
||||
var _id: String? = null,
|
||||
var playerName: String,
|
||||
var nickName: String,
|
||||
var discordID: String,
|
||||
var uid: String,
|
||||
var leave: Boolean = false,
|
||||
var createAt: Long? = null,
|
||||
var updatedAt: Long? = null
|
||||
)
|
||||
|
||||
fun Member.toDto(): MemberDto {
|
||||
return MemberDto(
|
||||
_id = this._id.toString(),
|
||||
playerName = this.playerName,
|
||||
nickName = this.nickName,
|
||||
discordID = this.discordID,
|
||||
uid = this.uid,
|
||||
leave = this.leave,
|
||||
createAt = this.createAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
|
||||
fun MemberDto.toMember(): Member =
|
||||
Member(
|
||||
playerName = this.playerName,
|
||||
nickName = this.nickName,
|
||||
discordID = this.discordID,
|
||||
uid = this.uid,
|
||||
leave = this.leave,
|
||||
createAt = this.createAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.ray650128.pcredive.database.dto
|
||||
|
||||
import com.ray650128.pcredive.database.model.Member
|
||||
import com.ray650128.pcredive.database.model.MemberRecord
|
||||
import com.ray650128.pcredive.database.model.Record
|
||||
import com.ray650128.pcredive.database.service.MemberService
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MemberRecordDto(
|
||||
var _id: String? = null,
|
||||
var member: Member? = null,
|
||||
var record1: Record,
|
||||
var record1c: Record,
|
||||
var record2: Record,
|
||||
var record2c: Record,
|
||||
var record3: Record,
|
||||
var record3c: Record,
|
||||
var createdAt: Long?,
|
||||
var updatedAt: Long?
|
||||
)
|
||||
|
||||
fun MemberRecord.toDto(): MemberRecordDto {
|
||||
return MemberRecordDto(
|
||||
_id = this._id.toString(),
|
||||
member = MemberService.findById(this.memberId.toString()),
|
||||
record1 = this.record1,
|
||||
record1c = this.record1c,
|
||||
record2 = this.record2,
|
||||
record2c = this.record2c,
|
||||
record3 = this.record3,
|
||||
record3c = this.record3c,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
|
||||
fun MemberRecordDto.toMemberRecord(): MemberRecord =
|
||||
MemberRecord(
|
||||
memberId = this.member?._id,
|
||||
record1 = this.record1,
|
||||
record1c = this.record1c,
|
||||
record2 = this.record2,
|
||||
record2c = this.record2c,
|
||||
record3 = this.record3,
|
||||
record3c = this.record3c,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.ray650128.pcredive.database.model
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.litote.kmongo.Id
|
||||
import org.litote.kmongo.newId
|
||||
|
||||
@Serializable
|
||||
data class Member(
|
||||
@Contextual var _id: Id<Member> = newId(),
|
||||
var playerName: String,
|
||||
var nickName: String,
|
||||
var discordID: String,
|
||||
var uid: String,
|
||||
var leave: Boolean = false,
|
||||
var createAt: Long? = null,
|
||||
var updatedAt: Long? = null
|
||||
)
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.ray650128.pcredive.database.model
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.litote.kmongo.Id
|
||||
import org.litote.kmongo.newId
|
||||
|
||||
@Serializable
|
||||
data class MemberRecord(
|
||||
@Contextual var _id: Id<MemberRecord> = newId(),
|
||||
@Contextual var memberId: Id<Member>? = null,
|
||||
var record1: Record = Record(),
|
||||
var record1c: Record = Record(),
|
||||
var record2: Record = Record(),
|
||||
var record2c: Record = Record(),
|
||||
var record3: Record = Record(),
|
||||
var record3c: Record = Record(),
|
||||
var createdAt: Long? = null,
|
||||
var updatedAt: Long? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Record(
|
||||
var boss: String? = null,
|
||||
var damage: Int? = null
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.ray650128.pcredive.database.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RecordData(
|
||||
val member: Member,
|
||||
var record: MemberRecord?
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.ray650128.pcredive.database.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdateMemberRecord(
|
||||
var memberId: String,
|
||||
var record1: Record = Record("未出"),
|
||||
var record1c: Record = Record("未出"),
|
||||
var record2: Record = Record("未出"),
|
||||
var record2c: Record = Record("未出"),
|
||||
var record3: Record = Record("未出"),
|
||||
var record3c: Record = Record("未出"),
|
||||
var createdAt: Long? = null,
|
||||
var updatedAt: Long? = null
|
||||
)
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.ray650128.pcredive.database.service
|
||||
|
||||
import com.ray650128.pcredive.database.model.Member
|
||||
import com.ray650128.pcredive.database.model.MemberRecord
|
||||
import org.bson.types.ObjectId
|
||||
import org.litote.kmongo.*
|
||||
import org.litote.kmongo.id.toId
|
||||
|
||||
object MemberRecordService {
|
||||
private val client = KMongo.createClient("mongodb://ray650128:Zx0987062271!@192.168.50.128:27017")
|
||||
private val database = client.getDatabase("pc_re_dive_clan_battle")
|
||||
private val recordCollection = database.getCollection<MemberRecord>()
|
||||
|
||||
fun create(record: MemberRecord): Id<MemberRecord> {
|
||||
recordCollection.insertOne(record)
|
||||
return record._id
|
||||
}
|
||||
|
||||
fun findById(id: String): MemberRecord? {
|
||||
val bsonId: Id<MemberRecord> = ObjectId(id).toId()
|
||||
return recordCollection.findOne(MemberRecord::_id eq bsonId)
|
||||
}
|
||||
|
||||
fun findByMemberIdBetweenDate(id: String, start: Long, end: Long): MemberRecord? {
|
||||
val bsonId: Id<Member> = ObjectId(id).toId()
|
||||
return recordCollection.findOne(MemberRecord::memberId eq bsonId, MemberRecord::createdAt gte start, MemberRecord::createdAt lt end)
|
||||
}
|
||||
|
||||
fun findByTimeBetween(start: Long, end: Long): List<MemberRecord> {
|
||||
return recordCollection.find(MemberRecord::createdAt gte start, MemberRecord::createdAt lt end).toList()
|
||||
}
|
||||
|
||||
fun findByOwnerId(id: String): List<MemberRecord> {
|
||||
val bsonId: Id<Member> = ObjectId(id).toId()
|
||||
return recordCollection.find(MemberRecord::memberId eq bsonId).toList()
|
||||
}
|
||||
|
||||
fun updateById(id: String, request: MemberRecord): Boolean =
|
||||
findById(id)?.let { record ->
|
||||
val updateResult = recordCollection.replaceOne(
|
||||
record.copy(
|
||||
record1 = request.record1,
|
||||
record1c = request.record1c,
|
||||
record2 = request.record2,
|
||||
record2c = request.record2c,
|
||||
record3 = request.record3,
|
||||
record3c = request.record3c,
|
||||
updatedAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
updateResult.modifiedCount == 1L
|
||||
} ?: false
|
||||
|
||||
fun deleteById(id: String): Boolean {
|
||||
val deleteResult = recordCollection.deleteOneById(ObjectId(id))
|
||||
return deleteResult.deletedCount == 1L
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.ray650128.pcredive.database.service
|
||||
|
||||
import com.ray650128.pcredive.database.model.Member
|
||||
import org.bson.types.ObjectId
|
||||
import org.litote.kmongo.*
|
||||
import org.litote.kmongo.id.toId
|
||||
|
||||
object MemberService {
|
||||
private val client = KMongo.createClient("mongodb://ray650128:Zx0987062271!@192.168.50.128:27017")
|
||||
private val database = client.getDatabase("pc_re_dive_clan_battle")
|
||||
private val memberCollection = database.getCollection<Member>()
|
||||
|
||||
fun create(member: Member): Id<Member> {
|
||||
memberCollection.insertOne(member)
|
||||
return member._id
|
||||
}
|
||||
|
||||
//fun findAll(): List<Member> = memberCollection.find().toList()
|
||||
|
||||
fun findAll(isLeave: Boolean? = null): List<Member> {
|
||||
return if (isLeave == null) {
|
||||
memberCollection.find().toList()
|
||||
} else {
|
||||
memberCollection.find(Member::leave eq isLeave).toList()
|
||||
}
|
||||
}
|
||||
|
||||
fun findById(id: String): Member? {
|
||||
val bsonId: Id<Member> = ObjectId(id).toId()
|
||||
return memberCollection.findOne(Member::_id eq bsonId)
|
||||
}
|
||||
|
||||
fun updateById(id: String, request: Member): Boolean =
|
||||
findById(id)?.let { member ->
|
||||
val updateResult = memberCollection.replaceOne(
|
||||
member.copy(
|
||||
playerName = request.playerName,
|
||||
nickName = request.nickName,
|
||||
discordID = request.discordID,
|
||||
uid = request.uid,
|
||||
leave = request.leave,
|
||||
updatedAt = request.updatedAt
|
||||
)
|
||||
)
|
||||
updateResult.modifiedCount == 1L
|
||||
} ?: false
|
||||
|
||||
fun deleteById(id: String): Boolean {
|
||||
val deleteResult = memberCollection.deleteOneById(ObjectId(id))
|
||||
return deleteResult.deletedCount == 1L
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.ray650128.pcredive.plugins
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.http.content.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.util.*
|
||||
import java.io.File
|
||||
|
||||
fun Application.configureHTTP() {
|
||||
install(CORS) {
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Get)
|
||||
allowMethod(HttpMethod.Put)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
allowMethod(HttpMethod.Post)
|
||||
allowHeader(HttpHeaders.AccessControlAllowOrigin)
|
||||
allowHeader(HttpHeaders.Accept)
|
||||
allowHeader(HttpHeaders.ContentType)
|
||||
anyHost()
|
||||
allowCredentials = true
|
||||
allowNonSimpleContentTypes = true
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package com.ray650128.pcredive.plugins
|
||||
|
||||
import com.ray650128.pcredive.database.dto.toDto
|
||||
import com.ray650128.pcredive.database.model.MemberRecord
|
||||
import com.ray650128.pcredive.database.model.Record
|
||||
import com.ray650128.pcredive.database.model.RecordData
|
||||
import com.ray650128.pcredive.database.model.UpdateMemberRecord
|
||||
import com.ray650128.pcredive.database.service.MemberRecordService
|
||||
import com.ray650128.pcredive.database.service.MemberService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.util.*
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.litote.kmongo.toId
|
||||
import java.util.*
|
||||
|
||||
fun Application.configureMemberRecordRouting() {
|
||||
routing {
|
||||
options("{...}") {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
route("/api") {
|
||||
route("/record") { // 查詢所有紀錄
|
||||
get {
|
||||
val calendar = Calendar.getInstance().apply {
|
||||
timeInMillis = System.currentTimeMillis() - (5 * 60 * 60 * 1000) // 由於公連換日為每日早上5:00,因此減去時差
|
||||
}
|
||||
val records = getData(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH))
|
||||
call.respond(HttpStatusCode.OK, records)
|
||||
}
|
||||
post("/{id}") { // 新增成員紀錄
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
val record = call.receive<MemberRecord>()
|
||||
val member = MemberService.findById(id) ?: run {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
return@post
|
||||
}
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
val startTime = getDayStartLong(now[Calendar.YEAR], now[Calendar.MONTH], now[Calendar.DAY_OF_MONTH])
|
||||
val endTime = getDayEndLong(now[Calendar.YEAR], now[Calendar.MONTH], now[Calendar.DAY_OF_MONTH])
|
||||
val existData = MemberRecordService.findByMemberIdBetweenDate(id, startTime, endTime) ?: run {
|
||||
record.apply {
|
||||
memberId = member._id
|
||||
createdAt = System.currentTimeMillis()
|
||||
updatedAt = System.currentTimeMillis()
|
||||
}
|
||||
MemberRecordService.create(record).let {
|
||||
call.respond(HttpStatusCode.OK, record)
|
||||
}
|
||||
return@post
|
||||
}
|
||||
existData.apply {
|
||||
memberId = member._id
|
||||
record1 = record.record1
|
||||
record1c = record.record1c
|
||||
record2 = record.record2
|
||||
record2c = record.record2c
|
||||
record3 = record.record3
|
||||
record3c = record.record3c
|
||||
updatedAt = System.currentTimeMillis()
|
||||
}
|
||||
MemberRecordService.updateById(existData._id.toString(), existData).let {
|
||||
call.respond(HttpStatusCode.OK, existData)
|
||||
}
|
||||
}
|
||||
get("/{year}/{month}/{day}") { // 取得特定日期
|
||||
val year = call.parameters.getOrFail<Int>("year").toInt()
|
||||
val month = call.parameters.getOrFail<Int>("month").toInt() - 1
|
||||
val day = call.parameters.getOrFail<Int>("day").toInt()
|
||||
val records = getData(year, month, day)
|
||||
call.respond(HttpStatusCode.OK, records)
|
||||
}
|
||||
get("/{id}") {
|
||||
// Show an article with a specific id
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
val record = MemberRecordService.findById(id)?.toDto() ?: run {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
return@get
|
||||
}
|
||||
call.respond(HttpStatusCode.OK, record)
|
||||
}
|
||||
put("/{id}") {
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
val record = call.receive<UpdateMemberRecord>()
|
||||
val oldData = MemberRecordService.findById(id) ?: run {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
return@put
|
||||
}
|
||||
oldData.apply {
|
||||
record1 = record.record1
|
||||
record1c = record.record1c
|
||||
record2 = record.record2
|
||||
record2c = record.record2c
|
||||
record3 = record.record3
|
||||
record3c = record.record3c
|
||||
updatedAt = System.currentTimeMillis()
|
||||
}
|
||||
MemberRecordService.updateById(id, oldData)
|
||||
call.respond(HttpStatusCode.OK, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getDayStartLong(year: Int, month: Int, day: Int): Long {
|
||||
val calendar = Calendar.getInstance().apply {
|
||||
set(Calendar.YEAR, year)
|
||||
set(Calendar.MONTH, month)
|
||||
set(Calendar.DAY_OF_MONTH, day)
|
||||
}
|
||||
return calendar.apply {
|
||||
set(Calendar.HOUR_OF_DAY, 5)
|
||||
set(Calendar.MINUTE, 0)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
}.timeInMillis
|
||||
}
|
||||
|
||||
fun getDayEndLong(year: Int, month: Int, day: Int): Long {
|
||||
val startTime = getDayStartLong(year, month, day)
|
||||
return startTime + 86399000L
|
||||
}
|
||||
|
||||
fun getData(year: Int, month: Int, day: Int): List<RecordData> {
|
||||
val startTime = getDayStartLong(year, month, day)
|
||||
val endTime = getDayEndLong(year, month, day)
|
||||
val records = ArrayList<RecordData>()
|
||||
val memberList = MemberService.findAll()
|
||||
memberList.forEach { member ->
|
||||
if (member.leave && member.updatedAt!! < startTime) {
|
||||
//println("成員離開 ${member.nickName}")
|
||||
return@forEach
|
||||
}
|
||||
if (member.createAt!! > startTime) {
|
||||
//println("成員不在當期開戰期間 ${member.nickName}")
|
||||
return@forEach
|
||||
}
|
||||
records.add(
|
||||
RecordData(member = member, null)
|
||||
)
|
||||
}
|
||||
val recordList = MemberRecordService.findByTimeBetween(startTime, endTime)
|
||||
recordList.forEach { record ->
|
||||
val player = MemberService.findById(record.memberId.toString())
|
||||
records.firstOrNull { it.member == player }?.let { playerRecord ->
|
||||
playerRecord.record = record
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.ray650128.pcredive.plugins
|
||||
|
||||
import com.ray650128.pcredive.database.model.Member
|
||||
import com.ray650128.pcredive.database.service.MemberService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.util.*
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
fun Application.configureMemberRouting() {
|
||||
routing {
|
||||
options("{...}") {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
route("/api") {
|
||||
route("/member") {
|
||||
get {
|
||||
// 顯示所有登錄的成員資料
|
||||
coroutineScope {
|
||||
val memberList = MemberService.findAll()
|
||||
call.respond(HttpStatusCode.OK, memberList)
|
||||
}
|
||||
}
|
||||
get("/{id}") {
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
val member = MemberService.findById(id) ?: run {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
return@get
|
||||
}
|
||||
call.respond(HttpStatusCode.OK, member)
|
||||
}
|
||||
post {
|
||||
val member = call.receive<Member>()
|
||||
member.apply {
|
||||
createAt = System.currentTimeMillis()
|
||||
updatedAt = System.currentTimeMillis()
|
||||
}
|
||||
MemberService.create(member).let {
|
||||
call.respond(HttpStatusCode.OK, member)
|
||||
}
|
||||
}
|
||||
put("/{id}") {
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
val member = call.receive<Member>()
|
||||
val oldData = MemberService.findById(id) ?: run {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
return@put
|
||||
}
|
||||
oldData.apply {
|
||||
playerName = member.playerName
|
||||
nickName = member.nickName
|
||||
discordID = member.discordID
|
||||
uid = member.uid
|
||||
leave = member.leave
|
||||
updatedAt = System.currentTimeMillis()
|
||||
}
|
||||
MemberService.updateById(id, oldData)
|
||||
call.respond(HttpStatusCode.OK, oldData)
|
||||
}
|
||||
delete("/{id}") {
|
||||
val id = call.parameters.getOrFail<String>("id")
|
||||
if (MemberService.deleteById(id)) {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.ray650128.pcredive.plugins
|
||||
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.litote.kmongo.id.serialization.IdKotlinXSerializationModule
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json { serializersModule = IdKotlinXSerializationModule }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ray650128.pcredive.plugins
|
||||
|
||||
import com.ray650128.pcredive.database.model.MemberRecord
|
||||
import com.ray650128.pcredive.database.model.Record
|
||||
import com.ray650128.pcredive.database.service.MemberRecordService
|
||||
import com.ray650128.pcredive.database.service.MemberService
|
||||
import io.ktor.server.application.*
|
||||
import java.util.*
|
||||
|
||||
fun Application.configureUpdateTimer() {
|
||||
val date = Date()
|
||||
val timer = Timer()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
val c = Calendar.getInstance()
|
||||
val hour = c[Calendar.HOUR_OF_DAY]
|
||||
val minute = c[Calendar.MINUTE]
|
||||
val second = c[Calendar.SECOND]
|
||||
val day = c[Calendar.DAY_OF_MONTH]
|
||||
val lastDay = c.getActualMaximum(Calendar.DAY_OF_MONTH)
|
||||
//println("Time: $day, ${String.format("%2d:%2d:%2d", hour, minute, second)}")
|
||||
if ((day in (lastDay - 5)..lastDay) && (hour == 5 && minute == 0 && second == 0)) {
|
||||
val members = MemberService.findAll(false)
|
||||
members.forEach { member ->
|
||||
MemberRecordService.create(
|
||||
MemberRecord(
|
||||
memberId = member._id,
|
||||
record1 = Record(""),
|
||||
record1c = Record(""),
|
||||
record2 = Record(""),
|
||||
record2c = Record(""),
|
||||
record3 = Record(""),
|
||||
record3c = Record(""),
|
||||
createdAt = System.currentTimeMillis(),
|
||||
updatedAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, date, 1000L)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
ktor {
|
||||
deployment {
|
||||
port = 10001
|
||||
port = ${?PORT}
|
||||
}
|
||||
application {
|
||||
modules = [ com.ray650128.pcredive.ApplicationKt.module ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
ktor:
|
||||
deployment:
|
||||
port: 10001
|
||||
application:
|
||||
modules:
|
||||
- club.sakunadaisuki.pcrediveclanrecordbackend.plugins.HttpKt.configureHttp
|
||||
- club.sakunadaisuki.pcrediveclanrecordbackend.plugins.SerializationKt.configureSerialization
|
||||
- club.sakunadaisuki.pcrediveclanrecordbackend.plugins.MongoKt.configureMongo
|
||||
- club.sakunadaisuki.pcrediveclanrecordbackend.plugins.RoutingKt.configureRouting
|
||||
db:
|
||||
mongo:
|
||||
user: ray650128
|
||||
password: Zx0987062271!
|
||||
host: 192.168.50.128
|
||||
@@ -0,0 +1,23 @@
|
||||
openapi: "3.0.3"
|
||||
info:
|
||||
title: "Application API"
|
||||
description: "Application API"
|
||||
version: "1.0.0"
|
||||
servers:
|
||||
- url: "http://0.0.0.0:8080"
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
description: "Hello World!"
|
||||
responses:
|
||||
"200":
|
||||
description: "OK"
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: "string"
|
||||
examples:
|
||||
Example#1:
|
||||
value: "Hello World!"
|
||||
components:
|
||||
schemas: { }
|
||||
@@ -4,9 +4,9 @@
|
||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="trace">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||
<logger name="io.netty" level="INFO"/>
|
||||
</configuration>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user