From bc07b17f20a13d171c2f31fa901503b11113f402 Mon Sep 17 00:00:00 2001
From: SOR <3272194354@qq.com>
Date: Thu, 25 Jun 2026 20:48:54 +0800
Subject: [PATCH] refactor: simplify
---
.idea/misc.xml | 2 +-
.idea/modules.xml | 1 +
.idea/vcs.xml | 1 -
.../controller/v1/AuthorizeController.kt | 17 +--
.../entity/dto/EmailVerificationMsg.kt | 3 +-
.../sor2171/backend/entity/enums/EmailType.kt | 13 ++
.../sor2171/backend/filter/FlowLimitFilter.kt | 39 +++---
.../backend/listener/MailQueueListener.kt | 19 +--
.../sor2171/backend/service/AccountService.kt | 2 +-
.../service/impl/AccountServiceImpl.kt | 113 ++++++++++--------
.../service/v1/AuthorizationService.kt | 91 +++++++-------
.../main/resources/application-example.yaml | 11 +-
frontend/frontend.iml | 9 --
13 files changed, 176 insertions(+), 145 deletions(-)
create mode 100644 backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt
delete mode 100644 frontend/frontend.iml
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 99e7ccd..603e70a 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,7 +4,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 9f72987..34dd4d7 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,6 +3,7 @@
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index b35f037..35eb1dd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,5 @@
-
\ 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 1afd7c3..3f16a6c 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,21 +1,16 @@
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 jakarta.validation.Valid
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank
-import jakarta.validation.constraints.Pattern
import org.slf4j.LoggerFactory
import org.springframework.validation.annotation.Validated
-import org.springframework.web.bind.annotation.GetMapping
-import org.springframework.web.bind.annotation.PostMapping
-import org.springframework.web.bind.annotation.RequestBody
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
@@ -30,27 +25,27 @@ class AuthorizeController(
@GetMapping("/ask-code")
fun askVerifyCode(
@RequestParam @NotBlank @Email email: String,
- @RequestParam @Pattern(regexp = "(register|reset)") type: String,
+ @RequestParam type: EmailType,
exchange: ServerWebExchange
): Mono> {
logger.info("Trying to ask code")
val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown"
return service.askEmailVerifyCode(type, email, ip)
- .map { messageHandler(it) }
+ .map(::messageHandler)
}
@PostMapping("/register")
fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono> {
logger.info("Trying to register")
return service.registerEmailAccount(vo)
- .map { messageHandler(it) }
+ .map(::messageHandler)
}
@PostMapping("/reset")
fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono> {
logger.info("Trying to reset password")
return service.resetEmailAccountPassword(vo)
- .map { messageHandler(it) }
+ .map(::messageHandler)
}
@GetMapping("/logout")
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt b/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt
index 38cb443..9de63da 100644
--- a/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt
@@ -1,10 +1,11 @@
package com.github.sor2171.backend.entity.dto
+import com.github.sor2171.backend.entity.enums.EmailType
import kotlinx.serialization.Serializable
@Serializable
data class EmailVerificationMsg(
val email: String,
- val type: String,
+ val type: EmailType,
val code: String
)
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt b/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt
new file mode 100644
index 0000000..20560d7
--- /dev/null
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt
@@ -0,0 +1,13 @@
+package com.github.sor2171.backend.entity.enums
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class EmailType {
+ @SerialName("register")
+ REGISTER,
+
+ @SerialName("reset")
+ RESET,
+}
\ No newline at end of file
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 7cdcdf7..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
@@ -3,6 +3,7 @@ package com.github.sor2171.backend.filter
import com.github.sor2171.backend.entity.ApiResponse
import com.github.sor2171.backend.entity.toJsonString
import com.github.sor2171.backend.utils.Const
+import org.springframework.beans.factory.annotation.Value
import org.springframework.core.annotation.Order
import org.springframework.data.redis.core.ReactiveStringRedisTemplate
import org.springframework.http.HttpStatus
@@ -17,7 +18,14 @@ import java.time.Duration
@Component
@Order(Const.FLOW_LIMIT_ORDER)
class FlowLimitFilter(
- private val template: ReactiveStringRedisTemplate
+ private val template: ReactiveStringRedisTemplate,
+
+ @param:Value($$"${app.flow-limit.count}")
+ private val limitCount: Int,
+ @param:Value($$"${app.flow-limit.limit-time}")
+ private val limitTime: Int,
+ @param:Value($$"${app.flow-limit.ban-time}")
+ private val expireTime: Int,
) : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono {
@@ -49,21 +57,22 @@ class FlowLimitFilter(
if (isBlocked) {
Mono.just(false)
} else {
- template.hasKey(counterKey).flatMap { hasCounter ->
- if (hasCounter) {
- template.opsForValue().increment(counterKey).flatMap { count ->
- if (count > 10) {
- template.opsForValue()
- .set(blockKey, "1", Duration.ofMinutes(1))
- .thenReturn(false)
- } else {
- Mono.just(true)
- }
- }
- } else {
+ template.opsForValue().increment(counterKey).flatMap { count ->
+ if (count == 1L) {
+ template.expire(
+ counterKey,
+ Duration.ofSeconds(limitTime.toLong())
+ ).thenReturn(true)
+ } else if (count > limitCount) {
template.opsForValue()
- .set(counterKey, "1", Duration.ofSeconds(3))
- .thenReturn(true)
+ .set(
+ blockKey,
+ "1",
+ Duration.ofSeconds(expireTime.toLong())
+ )
+ .thenReturn(false)
+ } else {
+ Mono.just(true)
}
}
}
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt b/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt
index 36320f7..f442c98 100644
--- a/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt
@@ -1,13 +1,14 @@
package com.github.sor2171.backend.listener
import com.github.sor2171.backend.entity.dto.EmailVerificationMsg
+import com.github.sor2171.backend.entity.enums.EmailType
import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory
import org.springframework.amqp.rabbit.annotation.RabbitHandler
import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.beans.factory.annotation.Value
-import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
+import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Component
@Component
@@ -15,9 +16,8 @@ import org.springframework.stereotype.Component
class MailQueueListener(
@param:Value($$"${spring.mail.username}")
private val username: String,
-
private val json: Json,
- private val sender: MailSender
+ private val sender: JavaMailSender
) {
private val logger = LoggerFactory.getLogger(this::class.java)
@@ -28,27 +28,30 @@ class MailQueueListener(
val (email, type, code) = data
val message = when (type) {
- "register" -> createMailMessage(
+ EmailType.REGISTER -> createMailMessage(
"Welcome to Our Service",
"Thank you for registering! Your verification code is: $code"
+ "\nThis code is valid for 3 minutes.",
email
)
- "reset" -> createMailMessage(
+ EmailType.RESET -> createMailMessage(
"Password Reset Request",
"You requested a password reset. Your verification code is: $code"
+ "\nThis code is valid for 3 minutes.",
email
)
-
- else -> return
}
+
sender.send(message)
logger.info("Email sent to $email.")
}
- fun createMailMessage(title: String, content: String, to: String): SimpleMailMessage {
+ private fun createMailMessage(
+ title: String,
+ content: String,
+ to: String
+ ): SimpleMailMessage {
val message = SimpleMailMessage()
message.subject = title
message.text = content
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt
index a498ab3..693570d 100644
--- a/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt
@@ -11,5 +11,5 @@ interface AccountService : IService, ReactiveUserDetailsService {
fun findAccountByEmail(email: String): Mono
fun existAccountByName(username: String): Mono
fun existAccountByEmail(email: String): Mono
- fun resetPasswordByEmail(email: String, encodedPassword: String): Mono
+ fun resetPasswordByEmail(email: String, encodedPassword: String): Mono
}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt
index d6cbd79..2dbf893 100644
--- a/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt
@@ -27,70 +27,87 @@ class AccountServiceImpl : ServiceImpl(), AccountService
}
}
- override fun findAccountByName(username: String): Mono {
- return Mono.fromCallable {
- logger.info("Find account with name $username")
- this.ktQuery()
+ override fun findAccountByName(username: String): Mono =
+ findAccount("name", username) {
+ ktQuery()
.eq(Account::username, username)
.one()
- }.flatMap { account ->
- Mono.just(account)
- }.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with name $username not found")))
- }
+ }
- override fun findAccountByEmail(email: String): Mono {
- return Mono.fromCallable {
- logger.info("Find account with email $email")
- this.ktQuery()
+ override fun findAccountByEmail(email: String): Mono =
+ findAccount("email", email) {
+ ktQuery()
.eq(Account::email, email)
.one()
- }.flatMap { account ->
- Mono.just(account)
- }.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with email $email not found")))
- }
+ }
- override fun findAccountByNameOrEmail(text: String): Mono {
- return findAccountByEmail(text)
- .onErrorResume { findAccountByName(text) }
- }
+ private fun findAccount(
+ fieldName: String,
+ value: String,
+ query: () -> Account?
+ ): Mono =
+ Mono.fromCallable {
+ logger.info("Find account with {} {}", fieldName, value)
+ query()
+ }.switchIfEmpty(
+ Mono.error(
+ UsernameNotFoundException("Account with $fieldName $value not found")
+ )
+ )
- override fun existAccountByName(username: String): Mono {
- return Mono.fromCallable {
- logger.info("Determine if name $username exists")
- this.ktQuery()
+ override fun findAccountByNameOrEmail(text: String): Mono =
+ findAccountByEmail(text).onErrorResume(
+ UsernameNotFoundException::class.java
+ ) {
+ findAccountByName(text)
+ }
+
+ override fun existAccountByName(username: String): Mono =
+ Mono.fromCallable {
+ logger.info("Determine if name {} exists", username)
+ ktQuery()
.eq(Account::username, username)
.exists()
}
- }
- override fun existAccountByEmail(email: String): Mono {
- return Mono.fromCallable {
- logger.info("Determine if email $email exists")
- this.ktQuery()
+ override fun existAccountByEmail(email: String): Mono =
+ Mono.fromCallable {
+ logger.info("Determine if email {} exists", email)
+ ktQuery()
.eq(Account::email, email)
.exists()
}
- }
- override fun resetPasswordByEmail(email: String, encodedPassword: String): Mono {
- return existAccountByEmail(email).flatMap { exists ->
- if (!exists) {
- return@flatMap Mono.just("account with the email does not exist.")
- }
+ override fun resetPasswordByEmail(
+ email: String,
+ encodedPassword: String
+ ): Mono =
+ existAccountByEmail(email)
+ .flatMap { exists ->
+ if (!exists) {
+ return@flatMap Mono.error(
+ UsernameNotFoundException(
+ "Account with email $email not found"
+ )
+ )
+ }
- Mono.fromCallable {
- logger.info("Reset password by email $email")
- this.ktUpdate()
- .eq(Account::email, email)
- .set(Account::password, encodedPassword)
- .update()
- }.flatMap { success ->
- if (success) {
- Mono.just("")
- } else {
- Mono.just("something went wrong. Please contact the administrator.")
+ Mono.fromCallable {
+ ktUpdate()
+ .eq(Account::email, email)
+ .set(Account::password, encodedPassword)
+ .update()
+ }
+ }
+ .flatMap { success ->
+ if (success) {
+ Mono.empty()
+ } else {
+ Mono.error(
+ IllegalStateException(
+ "Failed to reset password"
+ )
+ )
}
}
- }
- }
}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt
index 3559611..005f3e5 100644
--- a/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt
+++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt
@@ -2,6 +2,7 @@ package com.github.sor2171.backend.service.v1
import com.github.sor2171.backend.entity.dto.Account
import com.github.sor2171.backend.entity.dto.EmailVerificationMsg
+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.entity.vo.response.ReloginVO
@@ -27,7 +28,7 @@ class AuthorizationService(
private val stringRedisTemplate: ReactiveStringRedisTemplate,
private val encoder: PasswordEncoder
) {
- fun askEmailVerifyCode(type: String, email: String, ip: String): Mono {
+ fun askEmailVerifyCode(type: EmailType, email: String, ip: String): Mono {
return verifyLimit(ip).flatMap { allowed ->
if (allowed) {
val code = (100000..999999).random().toString()
@@ -52,32 +53,27 @@ class AuthorizationService(
fun registerEmailAccount(vo: EmailRegisterVO): Mono {
val (email, code, username, password) = vo
- return verifyCode(email, code).flatMap { error ->
- if (!error.isBlank()) {
- return@flatMap Mono.just(error)
+ return verifyCode(email, code)
+ .filter { it.isBlank() }
+ .switchIfEmpty(Mono.just("wrong code"))
+ .flatMap {
+ accountService.existAccountByEmail(email)
}
-
- accountService
- .existAccountByEmail(email)
- .flatMap { emailExists ->
- if (emailExists) {
- return@flatMap Mono.just("account with the same email already exists.")
- }
- accountService
- .existAccountByName(username)
- .flatMap { usernameExists ->
- if (usernameExists) {
- return@flatMap Mono.just("username already exists.")
- }
-
- val encodedPassword = encoder.encode(password)
+ .flatMap { emailExists ->
+ if (emailExists) {
+ Mono.just("account with the same email already exists.")
+ } else {
+ accountService.existAccountByName(username).flatMap { usernameExists ->
+ if (usernameExists) {
+ Mono.just("username already exists.")
+ } else {
val account = Account(
- null,
- username,
- encodedPassword!!,
- email,
- "user",
- getCurrentDateTime()
+ id = null,
+ username = username,
+ password = encoder.encode(password)!!,
+ email = email,
+ role = "user",
+ registerTime = getCurrentDateTime()
)
Mono.fromCallable { accountService.save(account) }
@@ -90,31 +86,31 @@ class AuthorizationService(
}
}
}
+ }
}
- }
+ }
}
fun resetEmailAccountPassword(vo: PasswordResetVO): Mono {
val (email, code, password) = vo
- return verifyCode(email, code).flatMap { error ->
- if (!error.isBlank()) {
- return@flatMap Mono.just(error)
- } else {
- val encodedPassword = encoder.encode(password)
- ?: return@flatMap Const.INTERNAL_ERROR_MONO
+ val encodedPassword =
+ encoder.encode(password)
+ ?: return Const.INTERNAL_ERROR_MONO
- return@flatMap accountService
- .resetPasswordByEmail(email, encodedPassword)
- .flatMap { text ->
- if (text.isBlank()) {
- stringRedisTemplate
- .delete(Const.VERIFY_EMAIL_DATA + email)
- .then(Mono.just(text))
- } else Mono.just(text)
- }
- }
- }
+ return verifyCode(email, code)
+ .then(
+ accountService.resetPasswordByEmail(
+ email,
+ encodedPassword
+ )
+ )
+ .then(
+ stringRedisTemplate.delete(
+ Const.VERIFY_EMAIL_DATA + email
+ )
+ )
+ .thenReturn("Password reset successfully")
}
fun jwtTokenRelogin(headerToken: String?): Mono? {
@@ -144,12 +140,9 @@ class AuthorizationService(
return stringRedisTemplate.opsForValue()
.get(Const.VERIFY_EMAIL_DATA + email)
.flatMap { code ->
- when {
- receivedCode == null || code != receivedCode ->
- Mono.just("verify code is wrong.")
-
- else -> Mono.just("")
- }
+ if (receivedCode == null || code != receivedCode)
+ Mono.just("verify code is wrong.")
+ else Mono.just("")
}
.switchIfEmpty(Mono.just("verify code has not been sent"))
}
diff --git a/backend/src/main/resources/application-example.yaml b/backend/src/main/resources/application-example.yaml
index fa18fbf..fe1d774 100644
--- a/backend/src/main/resources/application-example.yaml
+++ b/backend/src/main/resources/application-example.yaml
@@ -32,4 +32,13 @@ spring:
url: jdbc:mysql://localhost:3306/kotlin_springboot
#username: root
#password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver
\ No newline at end of file
+ driver-class-name: com.mysql.cj.jdbc.Driver
+
+mybatis-plus:
+ type-handlers-package: com.github.sor2171.backend.config
+
+app:
+ flow-limit:
+ count: 1000
+ limit-time: 3
+ ban-time: 60
\ No newline at end of file
diff --git a/frontend/frontend.iml b/frontend/frontend.iml
deleted file mode 100644
index 36f463a..0000000
--- a/frontend/frontend.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file