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