Optimize code. Add kdoc.

This commit is contained in:
2023-12-25 17:27:50 +08:00
parent 31358107c7
commit 15f631971c
27 changed files with 188 additions and 36 deletions

View File

@@ -5,14 +5,12 @@ import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import top.fatweb.api.annotation.EventLogRecord
import top.fatweb.api.entity.system.EventLog
import top.fatweb.api.service.system.IEventLogService
import top.fatweb.api.util.WebUtil
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.RegisterVo
/**
* Event log record aspect
@@ -25,24 +23,21 @@ import top.fatweb.api.vo.permission.LoginVo
class EventLogAspect(
private val eventLogService: IEventLogService
) {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@Pointcut("@annotation(top.fatweb.api.annotation.EventLogRecord)")
fun eventLogPointcut() {
}
@AfterReturning(value = "eventLogPointcut()", returning = "retValue")
fun doAfter(joinPoint: JoinPoint, retValue: Any) {
fun doAfter(joinPoint: JoinPoint, retValue: Any?) {
val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java)
try {
eventLogService.save(EventLog().apply {
this.event = annotation.event
operateUserId = WebUtil.getLoginUserId()
?: if (retValue is LoginVo) retValue.userId else -1
})
} catch (e: Exception) {
logger.error("Cannot record event!!!", e)
val userId = WebUtil.getLoginUserId() ?: when (retValue) {
is LoginVo -> retValue.userId!!
is RegisterVo -> retValue.userId!!
else -> -1
}
eventLogService.saveEvent(annotation, userId)
}
}

View File

@@ -15,6 +15,7 @@ import top.fatweb.api.param.permission.VerifyParam
import top.fatweb.api.service.permission.IAuthenticationService
import top.fatweb.api.util.WebUtil
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.RegisterVo
import top.fatweb.api.vo.permission.TokenVo
/**
@@ -36,11 +37,11 @@ class AuthenticationController(
*/
@Operation(summary = "注册")
@PostMapping("/register")
fun register(@Valid @RequestBody registerParam: RegisterParam): ResponseResult<Nothing> {
authenticationService.register(registerParam)
fun register(@Valid @RequestBody registerParam: RegisterParam): ResponseResult<RegisterVo> = ResponseResult.success(
ResponseCode.PERMISSION_REGISTER_SUCCESS,
data = authenticationService.register(registerParam)
)
return ResponseResult.success(ResponseCode.PERMISSION_REGISTER_SUCCESS)
}
/**
* Send verify email

View File

@@ -7,11 +7,23 @@ import top.fatweb.api.properties.SecurityProperties
import top.fatweb.api.service.system.IStatisticsLogService
import top.fatweb.api.util.RedisUtil
/**
* Statistics scheduled tasks
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Component
class StatisticsCron(
private val redisUtil: RedisUtil,
private val statisticsLogService: IStatisticsLogService
) {
/**
* Auto record number of online users
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Scheduled(cron = "0 * * * * *")
fun onlineUserCount() {
statisticsLogService.save(StatisticsLog().apply {

View File

@@ -18,7 +18,7 @@ import java.time.LocalDateTime
@TableName("t_event_log")
class EventLog : Serializable {
enum class Event(@field:EnumValue @field:JsonValue val code: String) {
LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), API("API")
LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), VERIFY("VERIFY"), API("API")
}
/**

View File

@@ -1,3 +1,9 @@
package top.fatweb.api.exception
/**
* Account need initialize exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class AccountNeedInitException : RuntimeException("Account need initialize")

View File

@@ -1,5 +1,11 @@
package top.fatweb.api.exception
/**
* Email settings not configured exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class NoEmailConfigException(
vararg configs: String
) : RuntimeException("Email settings not configured: ${configs.joinToString(", ")}")

View File

@@ -1,3 +1,9 @@
package top.fatweb.api.exception
/**
* No verification required exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class NoVerificationRequiredException : RuntimeException("No verification required")

View File

@@ -1,3 +1,9 @@
package top.fatweb.api.exception
/**
* Verification code error or expired exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class VerificationCodeErrorOrExpiredException : RuntimeException("Verification code is error or has expired")

View File

@@ -4,5 +4,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.api.entity.system.EventLog
/**
* Event log mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Mapper
interface EventLogMapper : BaseMapper<EventLog>

View File

@@ -4,5 +4,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.api.entity.system.StatisticsLog
/**
* Statistics log mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Mapper
interface StatisticsLogMapper : BaseMapper<StatisticsLog>

View File

@@ -86,6 +86,7 @@ data class UserAddParam(
* @since 1.0.0
*/
@Schema(description = "昵称")
@field:NotBlank(message = "Nickname can not be blank")
val nickname: String?,
/**

View File

@@ -3,6 +3,12 @@ package top.fatweb.api.param.system
import com.baomidou.mybatisplus.annotation.EnumValue
import com.fasterxml.jackson.annotation.JsonValue
/**
* Get active information parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
data class ActiveInfoGetParam(
val scope: Scope = Scope.WEAK
) {

View File

@@ -3,6 +3,12 @@ package top.fatweb.api.param.system
import com.baomidou.mybatisplus.annotation.EnumValue
import com.fasterxml.jackson.annotation.JsonValue
/**
* Get online information parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
data class OnlineInfoGetParam(
val scope: Scope = Scope.WEAK
) {

View File

@@ -6,6 +6,7 @@ import top.fatweb.api.param.permission.LoginParam
import top.fatweb.api.param.permission.RegisterParam
import top.fatweb.api.param.permission.VerifyParam
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.RegisterVo
import top.fatweb.api.vo.permission.TokenVo
/**
@@ -21,7 +22,7 @@ interface IAuthenticationService {
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun register(registerParam: RegisterParam)
fun register(registerParam: RegisterParam): RegisterVo
/**
* Send verify email

View File

@@ -34,6 +34,7 @@ import top.fatweb.api.util.MailUtil
import top.fatweb.api.util.RedisUtil
import top.fatweb.api.util.WebUtil
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.RegisterVo
import top.fatweb.api.vo.permission.TokenVo
import java.io.StringWriter
import java.time.Instant
@@ -63,13 +64,14 @@ class AuthenticationServiceImpl(
) : IAuthenticationService {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@EventLogRecord(EventLog.Event.REGISTER)
@Transactional
override fun register(registerParam: RegisterParam) {
override fun register(registerParam: RegisterParam): RegisterVo {
val user = User().apply {
username = registerParam.username
password = passwordEncoder.encode(registerParam.password)
verify =
"${LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()}-${UUID.randomUUID()}"
"${LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()}-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}"
locking = 0
enable = 1
}
@@ -82,6 +84,8 @@ class AuthenticationServiceImpl(
})
sendVerifyMail(user.username!!, "http://localhost:5173/verify?code=${user.verify!!}", registerParam.email!!)
return RegisterVo(userId = user.id)
}
@Transactional
@@ -91,7 +95,7 @@ class AuthenticationServiceImpl(
user.verify ?: throw NoVerificationRequiredException()
user.verify =
"${LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()}-${UUID.randomUUID()}"
"${LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()}-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}"
user.updateTime = LocalDateTime.now(ZoneOffset.UTC)
userService.updateById(user)
@@ -113,11 +117,12 @@ class AuthenticationServiceImpl(
template.merge(velocityContext, stringWriter)
MailUtil.sendSimpleMail(
"激活您的账号", stringWriter.toString(), true,
"验证您的账号", stringWriter.toString(), true,
email
)
}
@EventLogRecord(EventLog.Event.VERIFY)
@Transactional
override fun verify(verifyParam: VerifyParam) {
val user = userService.getById(WebUtil.getLoginUserId()) ?: throw AccessDeniedException("Access Denied")

View File

@@ -28,6 +28,7 @@ import top.fatweb.api.vo.permission.UserWithPasswordRoleInfoVo
import top.fatweb.api.vo.permission.UserWithRoleInfoVo
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.*
/**
* User service implement
@@ -111,9 +112,12 @@ class UserServiceImpl(
user.apply {
password = passwordEncoder.encode(rawPassword)
verify = if (userAddParam.verified) null else "${
LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()
}-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}"
}
if (baseMapper.insert(user) == 1) {
if (this.save(user)) {
user.userInfo?.let { userInfoService.save(it.apply { userId = user.id }) }
if (!user.roles.isNullOrEmpty()) {
@@ -175,9 +179,16 @@ class UserServiceImpl(
removeGroupIds.removeAll(addGroupIds)
oldGroupList.toSet().let { addGroupIds.removeAll(it) }
baseMapper.updateById(user)
baseMapper.update(
KtUpdateWrapper(User()).eq(User::id, user.id).set(User::expiration, user.expiration)
this.updateById(user)
this.update(
KtUpdateWrapper(User()).eq(User::id, user.id)
.set(
User::verify,
if (userUpdateParam.verified) null else "${
LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()
}-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}"
)
.set(User::expiration, user.expiration)
.set(User::credentialsExpiration, user.credentialsExpiration)
)
@@ -230,7 +241,7 @@ class UserServiceImpl(
throw AccessDeniedException("Access denied")
}
val user = baseMapper.selectById(userUpdatePasswordParam.id)
val user = this.getById(userUpdatePasswordParam.id)
user?.let {
val wrapper = KtUpdateWrapper(User())
wrapper.eq(User::id, user.id)
@@ -265,7 +276,7 @@ class UserServiceImpl(
return
}
baseMapper.deleteBatchIds(ids)
this.removeBatchByIds(ids)
userInfoService.remove(KtQueryWrapper(UserInfo()).`in`(UserInfo::userId, ids))
userRoleService.remove(KtQueryWrapper(UserRole()).`in`(UserRole::userId, ids))
userGroupService.remove(KtQueryWrapper(UserGroup()).`in`(UserGroup::userId, ids))

View File

@@ -1,6 +1,15 @@
package top.fatweb.api.service.system
import com.baomidou.mybatisplus.extension.service.IService
import top.fatweb.api.annotation.EventLogRecord
import top.fatweb.api.entity.system.EventLog
interface IEventLogService : IService<EventLog>
/**
* Event log service interface
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
interface IEventLogService : IService<EventLog> {
fun saveEvent(annotation: EventLogRecord, userId: Long)
}

View File

@@ -3,4 +3,10 @@ package top.fatweb.api.service.system
import com.baomidou.mybatisplus.extension.service.IService
import top.fatweb.api.entity.system.StatisticsLog
/**
* Statistics log service interface
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
interface IStatisticsLogService : IService<StatisticsLog>

View File

@@ -2,11 +2,30 @@ package top.fatweb.api.service.system.impl
import com.baomidou.dynamic.datasource.annotation.DS
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import top.fatweb.api.annotation.EventLogRecord
import top.fatweb.api.entity.system.EventLog
import top.fatweb.api.mapper.system.EventLogMapper
import top.fatweb.api.service.system.IEventLogService
@DS("sqlite")
@Service
class EventLogServiceImpl : ServiceImpl<EventLogMapper, EventLog>(), IEventLogService
class EventLogServiceImpl : ServiceImpl<EventLogMapper, EventLog>(), IEventLogService {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@Transactional(propagation = Propagation.REQUIRES_NEW)
override fun saveEvent(annotation: EventLogRecord, userId: Long) {
try {
this.save(EventLog().apply {
this.event = annotation.event
operateUserId = userId
})
} catch (e: Exception) {
logger.error("Cannot record event!!!", e)
}
}
}

View File

@@ -223,6 +223,6 @@ class StatisticsServiceImpl(
)
}
return ActiveInfoVo(getHistory("REGISTER"), getHistory("LOGIN"))
return ActiveInfoVo(getHistory("REGISTER"), getHistory("LOGIN"), getHistory("VERIFY"))
}
}

View File

@@ -3,6 +3,12 @@ package top.fatweb.api.settings
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonValue
/**
* Type of mail security
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
enum class MailSecurityType(val code: String) {
NONE("None"),
SSL_TLS("SSL/TLS"),

View File

@@ -8,6 +8,12 @@ import top.fatweb.api.settings.MailSettings
import top.fatweb.api.settings.SettingsOperator
import java.util.*
/**
* Mail util
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object MailUtil {
private val mailSender: JavaMailSenderImpl = JavaMailSenderImpl()

View File

@@ -0,0 +1,24 @@
package top.fatweb.api.vo.permission
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import io.swagger.v3.oas.annotations.media.Schema
/**
* Register value object
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "注册返回参数")
data class RegisterVo(
/**
* User ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "User ID", example = "1709986058679975938")
@JsonSerialize(using = ToStringSerializer::class)
val userId: Long?
)

View File

@@ -25,7 +25,15 @@ data class ActiveInfoVo(
* @since 1.0.0
* @see HistoryVo
*/
val loginHistory: List<HistoryVo>
val loginHistory: List<HistoryVo>,
/**
* Verify user number history
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
val verifyHistory: List<HistoryVo>
) {
data class HistoryVo(
/**