docs(core): add KDoc documentation to controllers, services, and entities

Add descriptive KDoc comments to all REST controllers, service interfaces,
entity classes, and mappers to improve code readability and maintainability.
Include annotations for controller-level API documentation.
This commit is contained in:
2026-05-21 18:47:23 +08:00
parent 5bb836eafc
commit 20660b91dc
24 changed files with 92 additions and 33 deletions
@@ -3,6 +3,9 @@ package com.msksbr.bookmgr
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
/*
* Spring Boot 启动类
*/
@SpringBootApplication
class BookMgrApplication
@@ -3,7 +3,9 @@ package com.msksbr.bookmgr.config
import jakarta.servlet.http.HttpServletRequest
import org.springframework.stereotype.Component
//获取真实IP的bean
/*
* 从请求头中提取真实客户端 IP(处理反向代理)
*/
@Component
class IpExtractor {
fun getRealIp(request: HttpServletRequest): String {
@@ -13,7 +13,8 @@ import java.util.*
import javax.crypto.SecretKey
/*
* 注册JWT的失效时间和密钥
* JWT 令牌工具:签发、解析
* 密钥来源:环境变量 JWT_SECRET,未配置时自动生成(重启失效)
*/
@Component
class JwtUtils(
@@ -22,10 +23,9 @@ class JwtUtils(
) {
private val secretKey: SecretKey
// 没有配置key时自动生成
// 初始化密钥:有配置时用 SHA-256 哈希,无配置时随机生成
init {
val keyBytes = if (configuredSecret.isNotBlank()) {
// 把用户提供的secret,用SHA-256哈希成256字节固定长度
val md = MessageDigest.getInstance("SHA-256")
md.digest(configuredSecret.toByteArray())
} else {
@@ -39,7 +39,7 @@ class JwtUtils(
secretKey = Keys.hmacShaKeyFor(keyBytes)
}
// 生成token
// 签发 tokenpayload 包含 role
fun generateToken(username: String, role: String): String {
val claims = Jwts.claims().add("role", role).build()
val token = Jwts.builder().claims(claims).subject(username)
@@ -52,15 +52,15 @@ class JwtUtils(
return token
}
// 解析token
// 解析 token,校验失败时记日志并返回 null
fun parseToken(token: String): Claims? {
try {
val claims = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token)
return claims.payload
} catch (e: JwtException) {
log.error("Token parsing failed", e)
log.error("[JWT] parse failed: {}", e.message)
} catch (e: IllegalArgumentException) {
log.error("Token parsing failed with illegalArguments: $token", e)
log.error("[JWT] parse failed: illegal argument")
}
return null
}
@@ -7,6 +7,11 @@ import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.springframework.stereotype.Component
/*
* AOP 日志切面:自动记录方法入口/出口/耗时
* - controller/service/runner/jwtDEBUG 级别,超 500ms 时 WARN
* - mapperTRACE 级别,日常不输出
*/
@Aspect
@Component
class LoggingAspect {
@@ -5,6 +5,9 @@ import org.springframework.context.annotation.Configuration
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
/*
* Argon2 密码编码器配置
*/
// 将Argon2的加盐哈希方法注册成Bean
@Configuration
class PasswordConfig {
@@ -2,6 +2,9 @@ package com.msksbr.bookmgr.controller
import org.springframework.web.bind.annotation.RestController
/*
* 管理端图书接口
*/
@RestController
class AdminBookController {
}
@@ -2,6 +2,9 @@ package com.msksbr.bookmgr.controller
import org.springframework.web.bind.annotation.RestController
/*
* 管理端借阅接口
*/
@RestController
class AdminBorrowController {
}
@@ -14,8 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/*
* 登录接口
* 接收DTO,返回json
* 认证接口:登录 / 登出
*/
@RestController
@RequestMapping("/api/auth")
@@ -31,14 +30,13 @@ class AuthController(
loginDTO: UserLoginDTO,
request: HttpServletRequest
): Result<out Any?> {
log.info("Login from ${ipExtractor.getRealIp(request)} username: ${loginDTO.username}")
log.info("UA: ${request.getHeader("User-Agent")}")
// 调用service验证
log.info("[Auth] login attempt: user={}, ip={}", loginDTO.username, ipExtractor.getRealIp(request))
log.debug("[Auth] user agent: {}", request.getHeader("User-Agent"))
val user = authService.login(loginDTO)
return if (user != null) {
// 登录成功,返回JWT
// 登录成功,签发 JWT
val token = jwtUtils.generateToken(user.username, user.role)
log.info("Login success")
log.info("[Auth] login success: user={}", user.username)
Result.success(
mapOf(
"token" to token,
@@ -47,8 +45,8 @@ class AuthController(
)
)
} else {
log.info("Login failed")
Result.error("username or password not match")
log.info("[Auth] login failed: user={}", loginDTO.username)
Result.error("Incorrect username or password")
}
}
@@ -56,10 +54,10 @@ class AuthController(
fun logout(
request: HttpServletRequest
): Result<String> {
log.info("Logout from ${ipExtractor.getRealIp(request)}")
log.info("UA: ${request.getHeader("User-Agent")}")
// JWT无状态,只需返回成功让客户端自行删除token即可
log.info("Logout success")
log.info("[Auth] logout: ip={}", ipExtractor.getRealIp(request))
log.debug("[Auth] user agent: {}", request.getHeader("User-Agent"))
// JWT 无状态,登出只需客户端删除 token
log.info("[Auth] logout success")
return Result.success("logout successfully")
}
}
@@ -2,6 +2,9 @@ package com.msksbr.bookmgr.controller
import org.springframework.web.bind.annotation.RestController
/*
* 图书接口
*/
@RestController
class BookController {
}
@@ -2,6 +2,9 @@ package com.msksbr.bookmgr.controller
import org.springframework.web.bind.annotation.RestController
/*
* 借阅接口
*/
@RestController
class BorrowController {
}
@@ -2,6 +2,9 @@ package com.msksbr.bookmgr.controller
import org.springframework.web.bind.annotation.RestController
/*
* 仪表盘接口
*/
@RestController
class DashBoardController {
}
@@ -2,7 +2,9 @@ package com.msksbr.bookmgr.dto
import jakarta.validation.constraints.NotBlank
/*
* 登录请求体
*/
data class UserLoginDTO(
@NotBlank(message = "username is required")
val username: String?,
@@ -4,6 +4,9 @@ import com.baomidou.mybatisplus.annotation.IdType
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
/*
* 图书实体,映射 book 表
*/
@TableName("book")
data class Book(
@TableId(type = IdType.AUTO)
@@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.util.Date
/*
* 借阅记录实体,映射 book_record 表
*/
@TableName("book_record")
data class BorrowRecord(
@TableId(type = IdType.AUTO)
@@ -4,6 +4,9 @@ import com.baomidou.mybatisplus.annotation.IdType
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
/*
* 用户实体,映射 user 表
*/
@TableName("user")
data class User(
@TableId(type = IdType.AUTO) // 设置自增主键
@@ -1,12 +1,13 @@
package com.msksbr.bookmgr.interceptor
import com.fasterxml.jackson.databind.ObjectMapper
import com.msksbr.bookmgr.config.JwtUtils
import com.msksbr.bookmgr.template.Result
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.stereotype.Component
import org.springframework.web.servlet.HandlerInterceptor
import tools.jackson.databind.ObjectMapper
@Component
class JwtAuthInterceptor(
@@ -4,5 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.msksbr.bookmgr.entity.Book
import org.apache.ibatis.annotations.Mapper
/*
* 图书 Mapper,继承 MyBatis-Plus BaseMapper 获得通用 CRUD
*/
@Mapper
interface BookMapper: BaseMapper<Book>
@@ -4,5 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.msksbr.bookmgr.entity.BorrowRecord
import org.apache.ibatis.annotations.Mapper
/*
* 借阅记录 Mapper,继承 MyBatis-Plus BaseMapper 获得通用 CRUD
*/
@Mapper
interface BorrowRecordMapper: BaseMapper<BorrowRecord>
@@ -4,5 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.msksbr.bookmgr.entity.User
import org.apache.ibatis.annotations.Mapper
/*
* 用户 Mapper,继承 MyBatis-Plus BaseMapper 获得通用 CRUD
*/
@Mapper
interface UserMapper: BaseMapper<User>
@@ -10,21 +10,24 @@ import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
/*
* 应用启动时初始化默认用户(admin / user01 / user02
* 已存在的用户会跳过,因此可安全重复执行
*/
@Component
class InitUserRunner(
val passwordEncoder: PasswordEncoder,
val userMapper: UserMapper,
) : ApplicationRunner {
// 添加注解,失败时可回滚
@Transactional
override fun run(args: ApplicationArguments) {
log.info("Starting default user initialization")
log.info("[InitUser] starting")
val existsAdmin = userMapper.selectOne(
QueryWrapper<User>()
.eq("username", "admin")
)
if (existsAdmin == null) {
log.info("Admin user not found, creating...")
log.info("[InitUser] admin not found, creating")
insertAdmin()
}
val existsUser01 = userMapper.selectOne(
@@ -32,7 +35,7 @@ class InitUserRunner(
.eq("username", "user01")
)
if (existsUser01 == null) {
log.info("Common user01 not found, creating...")
log.info("[InitUser] user01 not found, creating")
insertUser01()
}
val existsUser02 = userMapper.selectOne(
@@ -40,10 +43,10 @@ class InitUserRunner(
.eq("username", "user02")
)
if (existsUser02 == null) {
log.info("Common user02 not found, creating...")
log.info("[InitUser] user02 not found, creating")
insertUser02()
}
log.info("Default user initialization completed")
log.info("[InitUser] completed")
}
fun insertAdmin() {
@@ -4,7 +4,7 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
/*
* 手动日志属性
* SLF4J 日志扩展属性,通过 T.log 在任意类获取 Logger
*/
val <T: Any> T.log: Logger
@@ -3,6 +3,9 @@ package com.msksbr.bookmgr.service
import com.msksbr.bookmgr.dto.UserLoginDTO
import com.msksbr.bookmgr.entity.User
/*
* 认证服务
*/
interface AuthService {
fun login(loginDTO: UserLoginDTO): User?
}
@@ -8,6 +8,9 @@ import com.msksbr.bookmgr.service.AuthService
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
/*
* 认证服务实现,含时序攻击防护
*/
@Service
class AuthServiceImpl(private val userMapper: UserMapper, private val passwordEncoder: PasswordEncoder) : AuthService {
override fun login(loginDTO: UserLoginDTO): User? {
@@ -1,5 +1,8 @@
package com.msksbr.bookmgr.template
/*
* 统一 API 响应格式
*/
data class Result<T>(
var code: Int,
var message: String,