From e71f84a9432d44d52e5f0bdf1421675f0b1fdbcc Mon Sep 17 00:00:00 2001 From: SOR <3272194354@qq.com> Date: Tue, 18 Aug 2026 18:41:39 +0800 Subject: [PATCH] fix: DB connect --- .../sor2171/backend/config/SecurityConfig.kt | 6 +-- .../backend/config/SerializationConfig.kt | 6 +++ .../backend/controller/TestController.kt | 6 +-- .../controller/v1/AuthorizeController.kt | 17 ++----- .../sor2171/backend/entity/ApiResponse.kt | 19 +++---- .../sor2171/backend/filter/FlowLimitFilter.kt | 10 ++-- .../mapper/typehandler/TimeTypeHandler.kt | 49 +++++++++++++++++++ .../backend/utils/BigDecimalSerializer.kt | 29 +++++++++++ .../com/github/sor2171/backend/utils/Const.kt | 17 +++++-- .../github/sor2171/backend/utils/JwtUtils.kt | 5 +- .../main/resources/application-example.yaml | 2 +- 11 files changed, 125 insertions(+), 41 deletions(-) create mode 100644 backend/src/main/kotlin/com/github/sor2171/backend/mapper/typehandler/TimeTypeHandler.kt create mode 100644 backend/src/main/kotlin/com/github/sor2171/backend/utils/BigDecimalSerializer.kt diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/config/SecurityConfig.kt b/backend/src/main/kotlin/com/github/sor2171/backend/config/SecurityConfig.kt index 2db5d28..9170d96 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/config/SecurityConfig.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/config/SecurityConfig.kt @@ -67,14 +67,14 @@ class SecurityConfig( writeJsonResponse( exchange, HttpStatus.UNAUTHORIZED, - ApiResponse.unauthenticated(ex.message) + ApiResponse.unauthenticated(ex.message) ) } .accessDeniedHandler { exchange, ex -> writeJsonResponse( exchange, HttpStatus.FORBIDDEN, - ApiResponse.forbidden(ex.message) + ApiResponse.forbidden(ex.message) ) } } @@ -112,7 +112,7 @@ class SecurityConfig( writeJsonResponse( webFilterExchange.exchange, HttpStatus.UNAUTHORIZED, - ApiResponse.unauthenticated(exception.message) + ApiResponse.unauthenticated(exception.message) ) } return filter diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/config/SerializationConfig.kt b/backend/src/main/kotlin/com/github/sor2171/backend/config/SerializationConfig.kt index 04a782f..38746bb 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/config/SerializationConfig.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/config/SerializationConfig.kt @@ -1,6 +1,9 @@ package com.github.sor2171.backend.config +import com.github.sor2171.backend.utils.BigDecimalSerializer import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -9,6 +12,9 @@ class SerializationConfig { @Bean fun kotlinxSerializationJson(): Json = Json { + serializersModule = SerializersModule { + contextual(BigDecimalSerializer) + } ignoreUnknownKeys = true isLenient = true encodeDefaults = false diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/controller/TestController.kt b/backend/src/main/kotlin/com/github/sor2171/backend/controller/TestController.kt index bbd0ddb..5c973cc 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/controller/TestController.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/controller/TestController.kt @@ -17,12 +17,12 @@ class TestController( fun test(): Mono = Mono.just("Hello Kotlin WebFlux!") @GetMapping("/wstoken") - fun testToken(): Mono> = + fun testToken(): Mono> = wsTokenService.registerToken(ExampleHandler::class) .map { token -> - ApiResponse.success(data = token) + ApiResponse.success(data = token) } .onErrorResume { e -> - Mono.just(ApiResponse.innerError(e.message)) + Mono.just(ApiResponse.failure(500, null, e.message)) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt b/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt index c26d5b6..f5bd0a9 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt @@ -1,10 +1,11 @@ -package com.github.sor2171.backend.controller.v1 +package com.github.sor2171.backend.controller.v1 import com.github.sor2171.backend.entity.ApiResponse import com.github.sor2171.backend.entity.enums.EmailType import com.github.sor2171.backend.entity.vo.request.EmailRegisterVO import com.github.sor2171.backend.entity.vo.request.PasswordResetVO import com.github.sor2171.backend.service.v1.AuthorizationService +import com.github.sor2171.backend.utils.Const import jakarta.validation.Valid import jakarta.validation.constraints.Email import jakarta.validation.constraints.NotBlank @@ -31,21 +32,21 @@ class AuthorizeController( logger.info("Trying to ask code") val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown" return service.askEmailVerifyCode(type, email, ip) - .map(::messageHandler) + .map(Const::messageHandler) } @PostMapping("/register") fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono> { logger.info("Trying to register") return service.registerEmailAccount(vo) - .map(::messageHandler) + .map(Const::messageHandler) } @PostMapping("/reset") fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono> { logger.info("Trying to reset password") return service.resetEmailAccountPassword(vo) - .map(::messageHandler) + .map(Const::messageHandler) } @GetMapping("/logout") @@ -68,12 +69,4 @@ class AuthorizeController( ?.map { ApiResponse.success(it) } ?: Mono.just(ApiResponse.failure(401, null, "Please login with password.")) } - - private fun messageHandler(wrongMessage: String): ApiResponse { - return if (wrongMessage.isBlank()) { - ApiResponse.success() - } else { - ApiResponse.failure(400, null, wrongMessage) - } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/entity/ApiResponse.kt b/backend/src/main/kotlin/com/github/sor2171/backend/entity/ApiResponse.kt index 0f5d289..8602b3d 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/entity/ApiResponse.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/entity/ApiResponse.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.json.Json @Serializable data class ApiResponse( val code: Int, - val data: T?, + val data: T, val message: String ) { companion object { @@ -15,24 +15,21 @@ data class ApiResponse( } // 针对无 data 返回的情况,统一使用 Any? - fun success(message: String = "Success"): ApiResponse { + fun success(message: String = "Success"): ApiResponse { return ApiResponse(200, null, message) } - fun failure(code: Int, data: T?, message: String?): ApiResponse { + fun failure(code: Int = 401, data: T, message: String?): ApiResponse { return ApiResponse(code, data, message ?: "Failure") } - fun unauthenticated(message: String?) = - failure(401, null, message) + fun unauthenticated(message: String?) = + failure(401, null, message) - fun forbidden(message: String?) = - failure(403, null, message) + fun forbidden(message: String?) = + failure(403, null, message) - fun innerError(message: String?) = - failure(500, null, message) - - fun logoutFailed(message: String = ""): ApiResponse { + fun logoutFailed(message: String = ""): ApiResponse { val formatMessage = if (message.isBlank()) "" else ": $message" return failure(400, null, "Logout Failed$formatMessage") } diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt b/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt index 945c6b1..eb340de 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt @@ -23,9 +23,9 @@ class FlowLimitFilter( @param:Value($$"${app.flow-limit.count}") private val limitCount: Int, @param:Value($$"${app.flow-limit.limit-time}") - private val limitTime: Long, + private val limitTime: Int, @param:Value($$"${app.flow-limit.ban-time}") - private val expireTime: Long, + private val expireTime: Int, ) : WebFilter { override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { @@ -45,7 +45,7 @@ class FlowLimitFilter( response.statusCode = HttpStatus.FORBIDDEN response.headers.contentType = MediaType.APPLICATION_JSON val buffer = response.bufferFactory() - .wrap(ApiResponse.forbidden("Too many requests").toJsonString().toByteArray()) + .wrap(ApiResponse.forbidden("Too many requests").toJsonString().toByteArray()) return response.writeWith(Mono.just(buffer)) } @@ -61,14 +61,14 @@ class FlowLimitFilter( if (count == 1L) { template.expire( counterKey, - Duration.ofSeconds(limitTime) + Duration.ofSeconds(limitTime.toLong()) ).thenReturn(true) } else if (count > limitCount) { template.opsForValue() .set( blockKey, "1", - Duration.ofSeconds(expireTime) + Duration.ofSeconds(expireTime.toLong()) ) .thenReturn(false) } else { diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/mapper/typehandler/TimeTypeHandler.kt b/backend/src/main/kotlin/com/github/sor2171/backend/mapper/typehandler/TimeTypeHandler.kt new file mode 100644 index 0000000..c033386 --- /dev/null +++ b/backend/src/main/kotlin/com/github/sor2171/backend/mapper/typehandler/TimeTypeHandler.kt @@ -0,0 +1,49 @@ +package com.github.sor2171.backend.mapper.typehandler + +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.datetime.toKotlinLocalDateTime +import org.apache.ibatis.type.BaseTypeHandler +import org.apache.ibatis.type.JdbcType +import org.apache.ibatis.type.MappedJdbcTypes +import org.apache.ibatis.type.MappedTypes +import java.sql.CallableStatement +import java.sql.PreparedStatement +import java.sql.ResultSet + +@MappedTypes(LocalDateTime::class) +@MappedJdbcTypes(JdbcType.TIMESTAMP) +class TimeTypeHandler : BaseTypeHandler() { + override fun setNonNullParameter( + ps: PreparedStatement, + i: Int, + parameter: LocalDateTime, + jdbcType: JdbcType? + ) { + ps.setObject(i, parameter.toJavaLocalDateTime()) + } + + override fun getNullableResult( + rs: ResultSet, + columnName: String + ): LocalDateTime? = + rs.getTimestamp(columnName) + ?.toLocalDateTime() + ?.toKotlinLocalDateTime() + + override fun getNullableResult( + rs: ResultSet, + columnIndex: Int + ): LocalDateTime? = + rs.getTimestamp(columnIndex) + ?.toLocalDateTime() + ?.toKotlinLocalDateTime() + + override fun getNullableResult( + cs: CallableStatement, + columnIndex: Int + ): LocalDateTime? = + cs.getTimestamp(columnIndex) + ?.toLocalDateTime() + ?.toKotlinLocalDateTime() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/utils/BigDecimalSerializer.kt b/backend/src/main/kotlin/com/github/sor2171/backend/utils/BigDecimalSerializer.kt new file mode 100644 index 0000000..d2b839f --- /dev/null +++ b/backend/src/main/kotlin/com/github/sor2171/backend/utils/BigDecimalSerializer.kt @@ -0,0 +1,29 @@ +package com.github.sor2171.backend.utils + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonPrimitive +import java.math.BigDecimal + +object BigDecimalSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: BigDecimal) { + encoder.encodeString(value.toPlainString()) + } + + override fun deserialize(decoder: Decoder): BigDecimal { + val element = (decoder as JsonDecoder).decodeJsonElement() + if (element !is JsonPrimitive) { + throw SerializationException("Expected JSON primitive, got $element") + } + return element.content.toBigDecimal() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/utils/Const.kt b/backend/src/main/kotlin/com/github/sor2171/backend/utils/Const.kt index aa7ed7e..f935bf8 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/utils/Const.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/utils/Const.kt @@ -1,18 +1,29 @@ package com.github.sor2171.backend.utils +import com.github.sor2171.backend.entity.ApiResponse import reactor.core.publisher.Mono object Const { const val FLOW_LIMIT_ORDER = -101 - + const val JWT_BLACK_LIST = "jwt:blacklist:" const val VERIFY_EMAIL_LIMIT = "verify:email:limit:" const val VERIFY_EMAIL_DATA = "verify:email:data:" const val FLOW_LIMIT_COUNTER = "flow:counter:" const val FLOW_LIMIT_BLOCK = "flow:block:" - + const val WS_REQUEST_TOKEN = "ws:token:" - val INTERNAL_ERROR_MONO = Mono.just("something went wrong. Please contact the administrator.") + const val INTERNAL_ERROR_STR = "something went wrong. Please contact the administrator." + val INTERNAL_ERROR_MONO = Mono.just(INTERNAL_ERROR_STR) + + // for Controllers + fun messageHandler(wrongMessage: String): ApiResponse { + return if (wrongMessage.isBlank()) { + ApiResponse.success() + } else { + ApiResponse.failure(400, null, wrongMessage) + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/utils/JwtUtils.kt b/backend/src/main/kotlin/com/github/sor2171/backend/utils/JwtUtils.kt index 197de01..fe285b0 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/utils/JwtUtils.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/utils/JwtUtils.kt @@ -10,8 +10,8 @@ import org.springframework.data.redis.core.StringRedisTemplate import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetails import org.springframework.stereotype.Component +import java.time.Duration import java.util.* -import java.util.concurrent.TimeUnit @Component class JwtUtils( @@ -44,8 +44,7 @@ class JwtUtils( template.opsForValue().set( Const.JWT_BLACK_LIST + uuid, "deleted", - expire, - TimeUnit.MILLISECONDS + Duration.ofMillis(expire) ) return true } diff --git a/backend/src/main/resources/application-example.yaml b/backend/src/main/resources/application-example.yaml index d84cc68..01b9367 100644 --- a/backend/src/main/resources/application-example.yaml +++ b/backend/src/main/resources/application-example.yaml @@ -35,7 +35,7 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: - type-handlers-package: com.github.sor2171.backend.config + type-handlers-package: club.itstudio.itmanager.mapper.typehandler app: flow-limit: