Compare commits
13 Commits
master
...
8b20758404
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b20758404 | |||
| 0741d5846b | |||
| 790e55f6fa | |||
| 5dfaa68e0e | |||
| e936250391 | |||
| 017b7edc24 | |||
| 4e32adbe44 | |||
| 44601131a2 | |||
| 6fb1c9fb2e | |||
| e5f4f423f4 | |||
| 77a1690e08 | |||
| 8c99ba1147 | |||
| fcb5285788 |
+3
-3
@@ -1,14 +1,14 @@
|
|||||||
# 使用 Kotlin + JDK 的輕量化映像
|
# 使用 Kotlin + JDK 的輕量化映像
|
||||||
FROM gradle:7.5.1-jdk17-alpine AS builder
|
FROM gradle:9.5.1-jdk25 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN gradle clean shadowJar --no-daemon
|
RUN gradle clean shadowJar --no-daemon
|
||||||
|
|
||||||
# 運行階段
|
# 運行階段
|
||||||
FROM eclipse-temurin:21-jdk-ubi10-minimal
|
FROM eclipse-temurin:25-jdk-ubi10-minimal
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=builder /app/build/libs/*.jar app.jar
|
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 {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.22"
|
alias(libs.plugins.kotlin.jvm)
|
||||||
id("io.ktor.plugin") version "2.3.1"
|
alias(ktorLibs.plugins.ktor)
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.22"
|
alias(libs.plugins.kotlin.serialization)
|
||||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.ray650128.pcredive"
|
group = "club.sakunadaisuki"
|
||||||
version = "0.0.1"
|
version = "1.0.0-SNAPSHOT"
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass.set("io.ktor.server.netty.EngineMain")
|
mainClass = "io.ktor.server.netty.EngineMain"
|
||||||
|
|
||||||
val isDevelopment: Boolean = project.ext.has("development")
|
|
||||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
kotlin {
|
||||||
mavenCentral()
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("io.ktor:ktor-server-cors-jvm:$ktor_version")
|
implementation(ktorLibs.serialization.kotlinx.json)
|
||||||
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
|
implementation(ktorLibs.server.config.yaml)
|
||||||
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
implementation(ktorLibs.server.contentNegotiation)
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
|
implementation(ktorLibs.server.core)
|
||||||
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
implementation(ktorLibs.server.cors)
|
||||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
implementation(ktorLibs.server.netty)
|
||||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
implementation(ktorLibs.server.routingOpenapi)
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
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")
|
testImplementation(kotlin("test"))
|
||||||
implementation("org.litote.kmongo:kmongo-id-serialization:$kmongo_version")
|
testImplementation(ktorLibs.server.testHost)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+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
|
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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright © 2015-2021 the original authors.
|
# Copyright © 2015 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with 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
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# 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:
|
# Important for running:
|
||||||
#
|
#
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
# bash, then to run this script, type that shell name before the whole
|
# bash, then to run this script, type that shell name before the whole
|
||||||
# command line, like:
|
# command line, like:
|
||||||
#
|
#
|
||||||
# ksh Gradle
|
# ksh gradlew
|
||||||
#
|
#
|
||||||
# Busybox and similar reduced shells will NOT work, because this script
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
# requires all of these POSIX shell features:
|
# requires all of these POSIX shell features:
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,13 +82,11 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -133,22 +132,29 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
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 ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | 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" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -165,7 +171,6 @@ fi
|
|||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
@@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
# double quotes to make sure that they get re-expanded; and
|
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# 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 -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
# 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.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# 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 See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem gradlew startup script for Windows
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
setlocal EnableExtensions
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,15 +43,15 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
@@ -56,34 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute gradlew
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
@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
|
:exitWithErrorLevel
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||||
|
|
||||||
: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
|
|
||||||
|
|||||||
+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"
|
||||||
|
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
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("is_dropped") val isDropped: 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package club.sakunadaisuki.pcrediveclanrecordbackend.database.service
|
||||||
|
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.mapper.toEmptyBattleRecordDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecord
|
||||||
|
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>
|
||||||
|
var memberCollection: MongoCollection<Document>
|
||||||
|
|
||||||
|
init {
|
||||||
|
database.createCollection("battle_records")
|
||||||
|
collection = database.getCollection("battle_records")
|
||||||
|
|
||||||
|
database.createCollection("member")
|
||||||
|
memberCollection = database.getCollection("member")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createTable(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
|
||||||
|
val memberList = memberCollection.find(Filters.eq("leave", false)).toList().map(MemberDto::fromDocument)
|
||||||
|
val emptyBattleRecordList = memberList.map { it.toEmptyBattleRecordDto(date) }
|
||||||
|
collection.insertMany(emptyBattleRecordList.map { it.toDocument() })
|
||||||
|
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument)
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
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): MemberDto = withContext(Dispatchers.IO) {
|
||||||
|
data.createAt = System.currentTimeMillis()
|
||||||
|
data.updatedAt = System.currentTimeMillis()
|
||||||
|
data.leave = false
|
||||||
|
val doc = data.toDocument()
|
||||||
|
collection.insertOne(doc)
|
||||||
|
doc.let(MemberDto::fromDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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): MemberDto? = withContext(Dispatchers.IO) {
|
||||||
|
collection.findOneAndReplace(Filters.eq("uid", uid), data.toDocument())
|
||||||
|
collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
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,
|
||||||
|
isDropped = isDropped,
|
||||||
|
createdAt = createdAt,
|
||||||
|
updatedAt = updatedAt,
|
||||||
|
)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package club.sakunadaisuki.pcrediveclanrecordbackend.mapper
|
||||||
|
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.Member
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.MemberRequest
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.UpdateMemberRequest
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UpdateMemberRequest.toDto(uid: String): MemberDto {
|
||||||
|
return MemberDto(
|
||||||
|
uid = uid,
|
||||||
|
playerName = playerName,
|
||||||
|
discordName = discordName,
|
||||||
|
discordID = discordID,
|
||||||
|
leave = leave,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MemberDto.toEmptyBattleRecordDto(date: String): BattleRecordDto {
|
||||||
|
return BattleRecordDto(
|
||||||
|
date = date,
|
||||||
|
uid = uid,
|
||||||
|
playerName = playerName,
|
||||||
|
discordName = discordName,
|
||||||
|
discordID = discordID,
|
||||||
|
attackDetailList = emptyList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
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("is_dropped") val isDropped: Boolean = false, // true: 掉刀, false: 沒掉刀
|
||||||
|
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
|
||||||
|
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
|
||||||
|
)
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
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,
|
||||||
|
@SerialName("is_dropped") val isDropped: 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,
|
||||||
|
)
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package club.sakunadaisuki.pcrediveclanrecordbackend.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateMemberRequest(
|
||||||
|
@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,37 @@
|
|||||||
|
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) {
|
||||||
|
allowHost("localhost:8080")
|
||||||
|
allowHost("table.sakunadaisuki.club")
|
||||||
|
|
||||||
|
allowMethod(HttpMethod.Options)
|
||||||
|
allowMethod(HttpMethod.Put)
|
||||||
|
allowMethod(HttpMethod.Delete)
|
||||||
|
allowMethod(HttpMethod.Patch)
|
||||||
|
allowHeader(HttpHeaders.Authorization)
|
||||||
|
|
||||||
|
allowHeader(HttpHeaders.ContentType) // 處理 JSON 必備
|
||||||
|
allowHeader(HttpHeaders.Authorization) // 傳遞 JWT / 身份驗證權限必備
|
||||||
|
|
||||||
|
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,176 @@
|
|||||||
|
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
|
||||||
|
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.BattleRecordService
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.MemberService
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.mapper.toDto
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.AttackDetail
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecord
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecordRequest
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.MemberRequest
|
||||||
|
import club.sakunadaisuki.pcrediveclanrecordbackend.model.UpdateMemberRequest
|
||||||
|
import com.mongodb.client.MongoClients
|
||||||
|
import com.mongodb.client.MongoDatabase
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.config.*
|
||||||
|
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 existMember = memberService.read(member.uid)
|
||||||
|
if (existMember == null) {
|
||||||
|
val id = memberService.create(member.toDto())
|
||||||
|
call.respond(HttpStatusCode.Created, id)
|
||||||
|
} else {
|
||||||
|
call.respond(HttpStatusCode.Conflict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update member
|
||||||
|
put("/member/{uid}") {
|
||||||
|
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
|
||||||
|
val member = call.receive<UpdateMemberRequest>()
|
||||||
|
memberService.update(uid, member.toDto(uid))?.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/battle_record/{date}/{uid}") {
|
||||||
|
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
|
||||||
|
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
|
||||||
|
battleRecordService.read(date, uid)?.let { battleRecord ->
|
||||||
|
call.respond(HttpStatusCode.OK, battleRecord)
|
||||||
|
} ?: call.respond(HttpStatusCode.NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
put("/battle_record") {
|
||||||
|
val request = call.receive<BattleRecord>()
|
||||||
|
battleRecordService.update(request.date, request.toDto())?.let { battleRecord ->
|
||||||
|
call.respond(HttpStatusCode.OK, battleRecord)
|
||||||
|
} ?: call.respond(HttpStatusCode.NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/battle_record") {
|
||||||
|
val request = call.receive<BattleRecordRequest>()
|
||||||
|
val newAttackDetail = AttackDetail(
|
||||||
|
bossRound = request.round,
|
||||||
|
bossIndex = request.boss,
|
||||||
|
damage = request.damage,
|
||||||
|
isCompensation = request.isCompensation,
|
||||||
|
isDropped = request.isDropped,
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/battle_record/create_table/{date}") {
|
||||||
|
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
|
||||||
|
battleRecordService.createTable(date).let { battleRecord ->
|
||||||
|
call.respond(HttpStatusCode.OK, battleRecord)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="trace">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||||
<logger name="io.netty" level="INFO"/>
|
<logger name="io.netty" level="INFO"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
Reference in New Issue
Block a user