refactor(admin-book): rename Result to ApiResult and tighten return types

- Replace `Result` with `ApiResult` across admin book/borrow modules
- Change return type from `Result<Any?>` to `ApiResult<String>`
- Reformat multi-arg log statements for readability
This commit is contained in:
2026-05-23 21:43:40 +08:00
parent b79fd24ed5
commit bc4f7ac8cc
18 changed files with 167 additions and 145 deletions
+1
View File
@@ -27,6 +27,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.aspectj:aspectjweaver") implementation("org.aspectj:aspectjweaver")
implementation("org.springframework.security:spring-security-crypto") implementation("org.springframework.security:spring-security-crypto")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.3")
implementation("org.bouncycastle:bcprov-jdk18on:1.84") implementation("org.bouncycastle:bcprov-jdk18on:1.84")
implementation("com.mysql:mysql-connector-j") implementation("com.mysql:mysql-connector-j")
implementation("com.baomidou:mybatis-plus-spring-boot4-starter:3.5.15") implementation("com.baomidou:mybatis-plus-spring-boot4-starter:3.5.15")
@@ -3,10 +3,10 @@ package com.msksbr.bookmgr.config
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice import org.springframework.web.bind.annotation.RestControllerAdvice
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
/* /*
* 全局异常处理,将未捕获异常统一转为 Result 格式,避免泄露堆栈信息 * 全局异常处理,将未捕获异常统一转为 ApiResult 格式,避免泄露堆栈信息
* *
* 返回格式: * 返回格式:
* {"code":500,"message":"Internal server error"} * {"code":500,"message":"Internal server error"}
@@ -14,8 +14,8 @@ import com.msksbr.bookmgr.template.Result
@RestControllerAdvice @RestControllerAdvice
class GlobalExceptionHandler { class GlobalExceptionHandler {
@ExceptionHandler(Exception::class) @ExceptionHandler(Exception::class)
fun handle(ex: Exception): Result<Any?>{ fun handle(ex: Exception): ApiResult<Any?>{
log.error("[Global] unhandled exception: {}", ex.message) log.error("[Global] unhandled exception: {}", ex.message)
return Result.error("Internal server error") return ApiResult.error("Internal server error")
} }
} }
@@ -2,7 +2,7 @@ package com.msksbr.bookmgr.config
import com.msksbr.bookmgr.annotation.RequireRole import com.msksbr.bookmgr.annotation.RequireRole
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
@@ -44,7 +44,7 @@ class RequireRoleAspect(
if (username == null) { if (username == null) {
log.warn("[AUDIT] unauthenticated | ip={} | path={} | required={}", ip, path, requireRole.role) log.warn("[AUDIT] unauthenticated | ip={} | path={} | required={}", ip, path, requireRole.role)
return Result.unauthorized("Missing or invalid token") return ApiResult.unauthorized<Any?>("Missing or invalid token")
} }
val allowedRoles = rolePermissions[role] ?: emptySet() val allowedRoles = rolePermissions[role] ?: emptySet()
@@ -53,7 +53,7 @@ class RequireRoleAspect(
"[AUDIT] access denied | user={} | ip={} | path={} | required={} | actual={}", "[AUDIT] access denied | user={} | ip={} | path={} | required={} | actual={}",
username, ip, path, requireRole.role, role username, ip, path, requireRole.role, role
) )
return Result.forbidden("Access denied: insufficient permissions") return ApiResult.forbidden<Any?>("Access denied: insufficient permissions")
} }
log.info("[AUDIT] access allowed | user={} | ip={} | path={} | role={}", log.info("[AUDIT] access allowed | user={} | ip={} | path={} | role={}",
username, ip, path, role) username, ip, path, role)
@@ -6,7 +6,7 @@ import com.msksbr.bookmgr.dto.BookAddDto
import com.msksbr.bookmgr.dto.BookUpdateDto import com.msksbr.bookmgr.dto.BookUpdateDto
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AdminBookService import com.msksbr.bookmgr.service.AdminBookService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -35,8 +35,13 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
bookAddDto: BookAddDto, bookAddDto: BookAddDto,
request: HttpServletRequest, request: HttpServletRequest,
@RequestAttribute(required = false) username: String? @RequestAttribute(required = false) username: String?
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBook] add: user={}, name={}, author={}", username ?: "guest", bookAddDto.name, bookAddDto.author) log.info(
"[AdminBook] add: user={}, name={}, author={}",
username ?: "guest",
bookAddDto.name,
bookAddDto.author
)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.addBook(bookAddDto) return adminBookService.addBook(bookAddDto)
} }
@@ -50,7 +55,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
bookUpdateDto: BookUpdateDto, bookUpdateDto: BookUpdateDto,
request: HttpServletRequest, request: HttpServletRequest,
@RequestAttribute(required = false) username: String? @RequestAttribute(required = false) username: String?
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBook] update: user={}, id={}", username ?: "guest", id) log.info("[AdminBook] update: user={}, id={}", username ?: "guest", id)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.updateBook(id, bookUpdateDto) return adminBookService.updateBook(id, bookUpdateDto)
@@ -62,7 +67,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
id: Long, id: Long,
request: HttpServletRequest, request: HttpServletRequest,
@RequestAttribute(required = false) username: String? @RequestAttribute(required = false) username: String?
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBook] delete: user={}, id={}", username ?: "guest", id) log.info("[AdminBook] delete: user={}, id={}", username ?: "guest", id)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.deleteBook(id) return adminBookService.deleteBook(id)
@@ -75,7 +80,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
stock: Int, stock: Int,
request: HttpServletRequest, request: HttpServletRequest,
@RequestAttribute(required = false) username: String? @RequestAttribute(required = false) username: String?
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBook] updateStock: user={}, id={}, stock={}", username ?: "guest", id, stock) log.info("[AdminBook] updateStock: user={}, id={}, stock={}", username ?: "guest", id, stock)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.updateStock(id, stock) return adminBookService.updateStock(id, stock)
@@ -4,7 +4,8 @@ import com.msksbr.bookmgr.annotation.RequireRole
import com.msksbr.bookmgr.config.IpExtractor import com.msksbr.bookmgr.config.IpExtractor
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AdminBorrowService import com.msksbr.bookmgr.service.AdminBorrowService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.BorrowInfoVo
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -37,7 +38,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
query: String, query: String,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<BorrowInfoVo>> {
log.info("[AdminBorrow] search: user={}, query={}", username ?: "guest", query) log.info("[AdminBorrow] search: user={}, query={}", username ?: "guest", query)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.searchBorrows(query) return adminBorrowService.searchBorrows(query)
@@ -53,7 +54,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
id: Long, id: Long,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<BorrowInfoVo> {
log.info("[AdminBorrow] getOne: user={}, borrow record id={}", username ?: "guest", id) log.info("[AdminBorrow] getOne: user={}, borrow record id={}", username ?: "guest", id)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.getOneBorrow(id) return adminBorrowService.getOneBorrow(id)
@@ -68,7 +69,7 @@ class AdminBorrowController(
fun getAllBorrows( fun getAllBorrows(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<BorrowInfoVo>> {
log.info("[AdminBorrow] getAll: user={}, ip={}", username ?: "guest", ipExtractor.getRealIp(request)) log.info("[AdminBorrow] getAll: user={}, ip={}", username ?: "guest", ipExtractor.getRealIp(request))
log.info("[AdminBorrow] user agent: {}", request.getHeader("User-Agent")) log.info("[AdminBorrow] user agent: {}", request.getHeader("User-Agent"))
return adminBorrowService.getAllBorrows() return adminBorrowService.getAllBorrows()
@@ -85,7 +86,7 @@ class AdminBorrowController(
bookId: Long, bookId: Long,
userId: Long, userId: Long,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBorrow] borrowBook: user={}, bookId={}, userId={}", username ?: "guest", bookId, userId) log.info("[AdminBorrow] borrowBook: user={}, bookId={}, userId={}", username ?: "guest", bookId, userId)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.borrowBook(bookId, userId) return adminBorrowService.borrowBook(bookId, userId)
@@ -101,7 +102,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
recordId: Long, recordId: Long,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<String> {
log.info("[AdminBorrow] returnBook: user={}, recordId={}", username ?: "guest", recordId) log.info("[AdminBorrow] returnBook: user={}, recordId={}", username ?: "guest", recordId)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.returnBook(recordId) return adminBorrowService.returnBook(recordId)
@@ -5,7 +5,8 @@ import com.msksbr.bookmgr.config.JwtUtils
import com.msksbr.bookmgr.dto.UserLoginDto import com.msksbr.bookmgr.dto.UserLoginDto
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AuthService import com.msksbr.bookmgr.service.AuthService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.LoginVo
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -42,7 +43,7 @@ class AuthController(
@RequestBody @RequestBody
loginDTO: UserLoginDto, loginDTO: UserLoginDto,
request: HttpServletRequest request: HttpServletRequest
): Result<out Any?> { ): ApiResult<LoginVo> {
log.info("[Auth] login attempt: user={}, ip={}", loginDTO.username, ipExtractor.getRealIp(request)) log.info("[Auth] login attempt: user={}, ip={}", loginDTO.username, ipExtractor.getRealIp(request))
log.debug("[Auth] user agent: {}", request.getHeader("User-Agent")) log.debug("[Auth] user agent: {}", request.getHeader("User-Agent"))
val user = authService.login(loginDTO) val user = authService.login(loginDTO)
@@ -50,16 +51,15 @@ class AuthController(
// 登录成功,签发 JWT // 登录成功,签发 JWT
val token = jwtUtils.generateToken(user.username, user.role, user.id!!) val token = jwtUtils.generateToken(user.username, user.role, user.id!!)
log.info("[Auth] login success: user={}", user.username) log.info("[Auth] login success: user={}", user.username)
Result.success( val loginVo = LoginVo(
mapOf( token = token,
"token" to token, username = user.username,
"username" to user.username, role = user.role
"role" to user.role
)
) )
ApiResult.success(loginVo)
} else { } else {
log.warn("[Auth] login failed: user={}", loginDTO.username) log.warn("[Auth] login failed: user={}", loginDTO.username)
Result.error("Incorrect username or password") ApiResult.error("Incorrect username or password")
} }
} }
@@ -74,7 +74,7 @@ class AuthController(
fun logout( fun logout(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
request: HttpServletRequest request: HttpServletRequest
): Result<String> { ): ApiResult<String> {
if (username != null) { if (username != null) {
log.info("[Auth] logout username: {}", username) log.info("[Auth] logout username: {}", username)
} }
@@ -82,6 +82,6 @@ class AuthController(
log.debug("[Auth] user agent: {}", request.getHeader("User-Agent")) log.debug("[Auth] user agent: {}", request.getHeader("User-Agent"))
// JWT 无状态,登出只需客户端删除 token // JWT 无状态,登出只需客户端删除 token
log.info("[Auth] logout success") log.info("[Auth] logout success")
return Result.success("logout successfully") return ApiResult.success("logout successfully")
} }
} }
@@ -1,9 +1,10 @@
package com.msksbr.bookmgr.controller package com.msksbr.bookmgr.controller
import com.msksbr.bookmgr.config.IpExtractor import com.msksbr.bookmgr.config.IpExtractor
import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BookService import com.msksbr.bookmgr.service.BookService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestAttribute import org.springframework.web.bind.annotation.RequestAttribute
@@ -26,7 +27,7 @@ class BookController(private val bookService: BookService, private val ipExtract
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
query: String, query: String,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<Book>> {
log.info("[Book] search: user={}, query={}", username ?: "guest", query) log.info("[Book] search: user={}, query={}", username ?: "guest", query)
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.searchBook(query) return bookService.searchBook(query)
@@ -37,7 +38,7 @@ class BookController(private val bookService: BookService, private val ipExtract
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
request: HttpServletRequest, request: HttpServletRequest,
id: Long id: Long
): Result<Any?> { ): ApiResult<Book> {
log.info("[Book] getOne: user={}, book id={}", username ?: "guest", id) log.info("[Book] getOne: user={}, book id={}", username ?: "guest", id)
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.getOneBook(id) return bookService.getOneBook(id)
@@ -47,7 +48,7 @@ class BookController(private val bookService: BookService, private val ipExtract
fun getAllBooks( fun getAllBooks(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<Book>> {
log.info("[Book] getAll: user={}", username ?: "guest") log.info("[Book] getAll: user={}", username ?: "guest")
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.getAllBooks() return bookService.getAllBooks()
@@ -4,7 +4,8 @@ import com.msksbr.bookmgr.annotation.RequireRole
import com.msksbr.bookmgr.config.IpExtractor import com.msksbr.bookmgr.config.IpExtractor
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BorrowService import com.msksbr.bookmgr.service.BorrowService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.MyBorrowVo
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -37,7 +38,7 @@ class BorrowController(
@RequestAttribute(required = false) username: String?, @RequestAttribute(required = false) username: String?,
@RequestAttribute(required = false) userId: Long?, @RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<MyBorrowVo>> {
log.info("[Borrow] getAll: user={}, userId={}", username ?: "guest", userId) log.info("[Borrow] getAll: user={}, userId={}", username ?: "guest", userId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.getAllMyBorrows(userId!!) return borrowService.getAllMyBorrows(userId!!)
@@ -54,7 +55,7 @@ class BorrowController(
query: String, query: String,
@RequestAttribute(required = false) userId: Long?, @RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<List<MyBorrowVo>> {
log.info("[Borrow] search: user={}, userId={}, query={}", username ?: "guest", userId, query) log.info("[Borrow] search: user={}, userId={}, query={}", username ?: "guest", userId, query)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.searchMyBorrows(query, userId!!) return borrowService.searchMyBorrows(query, userId!!)
@@ -71,7 +72,7 @@ class BorrowController(
borrowId: Long, borrowId: Long,
@RequestAttribute(required = false) userId: Long?, @RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<MyBorrowVo> {
log.info("[Borrow] getOne: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId) log.info("[Borrow] getOne: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.getOneMyBorrow(borrowId, userId!!) return borrowService.getOneMyBorrow(borrowId, userId!!)
@@ -88,7 +89,7 @@ class BorrowController(
bookId: Long, bookId: Long,
@RequestAttribute(required = false) userId: Long?, @RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<String> {
log.info("[Borrow] borrow: user={}, userId={}, bookId={}", username ?: "guest", userId, bookId) log.info("[Borrow] borrow: user={}, userId={}, bookId={}", username ?: "guest", userId, bookId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.borrowBookForMe(bookId, userId!!) return borrowService.borrowBookForMe(bookId, userId!!)
@@ -105,7 +106,7 @@ class BorrowController(
borrowId: Long, borrowId: Long,
@RequestAttribute(required = false) userId: Long?, @RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest request: HttpServletRequest
): Result<Any?> { ): ApiResult<String> {
log.info("[Borrow] return: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId) log.info("[Borrow] return: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.returnBookForMe(borrowId, userId!!) return borrowService.returnBookForMe(borrowId, userId!!)
@@ -2,7 +2,7 @@ package com.msksbr.bookmgr.service
import com.msksbr.bookmgr.dto.BookAddDto import com.msksbr.bookmgr.dto.BookAddDto
import com.msksbr.bookmgr.dto.BookUpdateDto import com.msksbr.bookmgr.dto.BookUpdateDto
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
/* /*
* 图书管理服务接口 * 图书管理服务接口
@@ -14,25 +14,28 @@ interface AdminBookService {
* @param bookAddDto 包含 name、author、stock * @param bookAddDto 包含 name、author、stock
* @return 成功返回 200,重复返回 409 * @return 成功返回 200,重复返回 409
*/ */
fun addBook(bookAddDto: BookAddDto): Result<Any?> fun addBook(bookAddDto: BookAddDto): ApiResult<String>
/* /*
* 修改图书信息(名称或作者) * 修改图书信息(名称或作者)
* @param id 图书 ID * @param id 图书 ID
* @param bookUpdateDto 包含可选的 name 和 author * @param bookUpdateDto 包含可选的 name 和 author
* @return 成功返回 200,不存在返回 500,重复返回 409 * @return 成功返回 200,不存在返回 500,重复返回 409
*/ */
fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): Result<Any?> fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): ApiResult<String>
/* /*
* 删除图书 * 删除图书
* @param id 图书 ID * @param id 图书 ID
* @return 成功返回 200,不存在返回 500 * @return 成功返回 200,不存在返回 500
*/ */
fun deleteBook(id: Long): Result<Any?> fun deleteBook(id: Long): ApiResult<String>
/* /*
* 调整库存 * 调整库存
* @param id 图书 ID * @param id 图书 ID
* @param stock 新的库存数量,必须大于 0 * @param stock 新的库存数量,必须大于 0
* @return 成功返回 200,不存在或库存不合法返回 500 * @return 成功返回 200,不存在或库存不合法返回 500
*/ */
fun updateStock(id: Long, stock: Int): Result<Any?> fun updateStock(id: Long, stock: Int): ApiResult<String>
} }
@@ -1,6 +1,7 @@
package com.msksbr.bookmgr.service package com.msksbr.bookmgr.service
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.BorrowInfoVo
/* /*
* 借阅管理服务接口 * 借阅管理服务接口
@@ -12,20 +13,20 @@ interface AdminBorrowService {
* @param query 搜索关键词,按书名或用户名模糊匹配 * @param query 搜索关键词,按书名或用户名模糊匹配
* @return 匹配的借阅记录列表 * @return 匹配的借阅记录列表
*/ */
fun searchBorrows(query: String): Result<Any?> fun searchBorrows(query: String): ApiResult<List<BorrowInfoVo>>
/* /*
* 查询单条借阅记录 * 查询单条借阅记录
* @param id 借阅记录 ID * @param id 借阅记录 ID
* @return 借阅记录详情,含关联的图书和用户信息 * @return 借阅记录详情,含关联的图书和用户信息
*/ */
fun getOneBorrow(id: Long): Result<Any?> fun getOneBorrow(id: Long): ApiResult<BorrowInfoVo>
/* /*
* 查询全部借阅记录 * 查询全部借阅记录
* @return 所有借阅记录,含关联的图书和用户信息 * @return 所有借阅记录,含关联的图书和用户信息
*/ */
fun getAllBorrows(): Result<Any?> fun getAllBorrows(): ApiResult<List<BorrowInfoVo>>
/* /*
* 管理员手动借书 * 管理员手动借书
@@ -33,12 +34,12 @@ interface AdminBorrowService {
* @param userId 借书的用户 ID * @param userId 借书的用户 ID
* @return 借阅结果 * @return 借阅结果
*/ */
fun borrowBook(bookId: Long, userId: Long): Result<Any?> fun borrowBook(bookId: Long, userId: Long): ApiResult<String>
/* /*
* 管理员手动还书 * 管理员手动还书
* @param recordId 借阅记录 ID * @param recordId 借阅记录 ID
* @return 归还结果 * @return 归还结果
*/ */
fun returnBook(recordId: Long): Result<Any?> fun returnBook(recordId: Long): ApiResult<String>
} }
@@ -1,6 +1,7 @@
package com.msksbr.bookmgr.service package com.msksbr.bookmgr.service
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.template.ApiResult
/* /*
* 图书服务接口 * 图书服务接口
@@ -12,17 +13,17 @@ interface BookService {
* @param query 搜索关键词,不可为空 * @param query 搜索关键词,不可为空
* @return 搜索结果列表,无匹配时返回 404 * @return 搜索结果列表,无匹配时返回 404
*/ */
fun searchBook(query: String): Result<Any?> fun searchBook(query: String): ApiResult<List<Book>>
/* /*
* 根据 ID 查询单本图书 * 根据 ID 查询单本图书
* @param id 图书 ID,必须为正整数 * @param id 图书 ID,必须为正整数
* @return 图书实体,不存在时返回 404 * @return 图书实体,不存在时返回 404
*/ */
fun getOneBook(id: Long): Result<Any?> fun getOneBook(id: Long): ApiResult<Book>
/* /*
* 查询全部图书列表 * 查询全部图书列表
* @return 所有图书的列表 * @return 所有图书的列表
*/ */
fun getAllBooks(): Result<Any?> fun getAllBooks(): ApiResult<List<Book>>
} }
@@ -1,6 +1,7 @@
package com.msksbr.bookmgr.service package com.msksbr.bookmgr.service
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.MyBorrowVo
/* /*
* 借阅服务接口 * 借阅服务接口
@@ -12,7 +13,7 @@ interface BorrowService {
* @param userId 当前用户的 ID * @param userId 当前用户的 ID
* @return 该用户的所有借阅记录 * @return 该用户的所有借阅记录
*/ */
fun getAllMyBorrows(userId: Long): Result<Any?> fun getAllMyBorrows(userId: Long): ApiResult<List<MyBorrowVo>>
/* /*
* 搜索当前用户的借阅记录 * 搜索当前用户的借阅记录
@@ -20,7 +21,7 @@ interface BorrowService {
* @param userId 当前用户的 ID * @param userId 当前用户的 ID
* @return 匹配的借阅记录列表 * @return 匹配的借阅记录列表
*/ */
fun searchMyBorrows(query: String, userId: Long): Result<Any?> fun searchMyBorrows(query: String, userId: Long): ApiResult<List<MyBorrowVo>>
/* /*
* 查询单条借阅记录 * 查询单条借阅记录
@@ -28,7 +29,7 @@ interface BorrowService {
* @param userId 当前用户的 ID * @param userId 当前用户的 ID
* @return 借阅记录详情 * @return 借阅记录详情
*/ */
fun getOneMyBorrow(borrowId: Long, userId: Long): Result<Any?> fun getOneMyBorrow(borrowId: Long, userId: Long): ApiResult<MyBorrowVo>
/* /*
* 借书 * 借书
@@ -36,7 +37,7 @@ interface BorrowService {
* @param userId 当前用户的 ID * @param userId 当前用户的 ID
* @return 借阅结果 * @return 借阅结果
*/ */
fun borrowBookForMe(bookId: Long, userId: Long): Result<Any?> fun borrowBookForMe(bookId: Long, userId: Long): ApiResult<String>
/* /*
* 还书 * 还书
@@ -44,5 +45,5 @@ interface BorrowService {
* @param userId 当前用户的 ID * @param userId 当前用户的 ID
* @return 归还结果 * @return 归还结果
*/ */
fun returnBookForMe(borrowId: Long, userId: Long): Result<Any?> fun returnBookForMe(borrowId: Long, userId: Long): ApiResult<String>
} }
@@ -7,7 +7,7 @@ import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.mapper.BookMapper import com.msksbr.bookmgr.mapper.BookMapper
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AdminBookService import com.msksbr.bookmgr.service.AdminBookService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
/* /*
@@ -16,7 +16,7 @@ import org.springframework.stereotype.Service
*/ */
@Service @Service
class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookService { class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookService {
override fun addBook(bookAddDto: BookAddDto): Result<Any?> { override fun addBook(bookAddDto: BookAddDto): ApiResult<String> {
val book = Book( val book = Book(
id = null, id = null,
name = bookAddDto.name, name = bookAddDto.name,
@@ -25,25 +25,25 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
) )
if (isBookDuplicate(book)) { if (isBookDuplicate(book)) {
log.warn("[AdminBook] add: duplicate book, name={}, author={}", book.name, book.author) log.warn("[AdminBook] add: duplicate book, name={}, author={}", book.name, book.author)
return Result.conflict("Book already exists, please update the stock instead") return ApiResult.conflict("Book already exists, please update the stock instead")
} }
bookMapper.insert(book) bookMapper.insert(book)
log.info("[AdminBook] add: success, id={}, name={}", book.id, book.name) log.info("[AdminBook] add: success, id={}, name={}", book.id, book.name)
return Result.success("Book added successfully") return ApiResult.success("Book added successfully")
} }
override fun updateBook( override fun updateBook(
id: Long, id: Long,
bookUpdateDto: BookUpdateDto bookUpdateDto: BookUpdateDto
): Result<Any?> { ): ApiResult<String> {
if (bookUpdateDto.name.isBlank() && bookUpdateDto.author.isBlank()) { if (bookUpdateDto.name.isBlank() && bookUpdateDto.author.isBlank()) {
log.warn("[AdminBook] update: both name and author are blank, id={}", id) log.warn("[AdminBook] update: both name and author are blank, id={}", id)
return Result.error("At least one of name or author must be provided") return ApiResult.error("At least one of name or author must be provided")
} }
val existing = bookMapper.selectById(id) val existing = bookMapper.selectById(id)
?: run { ?: run {
log.warn("[AdminBook] update: book not found, id={}", id) log.warn("[AdminBook] update: book not found, id={}", id)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
val book = Book( val book = Book(
id = id, id = id,
@@ -53,33 +53,33 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
) )
if (isBookDuplicate(book, book.id)) { if (isBookDuplicate(book, book.id)) {
log.warn("[AdminBook] update: duplicate book, name={}, author={}", book.name, book.author) log.warn("[AdminBook] update: duplicate book, name={}, author={}", book.name, book.author)
return Result.conflict("Book already exists, please update the stock instead") return ApiResult.conflict("Book already exists, please update the stock instead")
} }
bookMapper.updateById(book) bookMapper.updateById(book)
log.info("[AdminBook] update: success, id={}", book.id) log.info("[AdminBook] update: success, id={}", book.id)
return Result.success("Book updated successfully") return ApiResult.success("Book updated successfully")
} }
override fun deleteBook(id: Long): Result<Any?> { override fun deleteBook(id: Long): ApiResult<String> {
val existing = bookMapper.selectById(id) val existing = bookMapper.selectById(id)
?: run { ?: run {
log.warn("[AdminBook] delete: book not found, id={}", id) log.warn("[AdminBook] delete: book not found, id={}", id)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
bookMapper.deleteById(id) bookMapper.deleteById(id)
log.info("[AdminBook] delete: success, id={}, name={}", id, existing.name) log.info("[AdminBook] delete: success, id={}, name={}", id, existing.name)
return Result.success("Book deleted successfully") return ApiResult.success("Book deleted successfully")
} }
override fun updateStock(id: Long, stock: Int): Result<Any?> { override fun updateStock(id: Long, stock: Int): ApiResult<String> {
if (stock <= 0) { if (stock <= 0) {
log.warn("[AdminBook] updateStock: invalid stock={}, id={}", stock, id) log.warn("[AdminBook] updateStock: invalid stock={}, id={}", stock, id)
return Result.error("Stock must be greater than zero") return ApiResult.error("Stock must be greater than zero")
} }
val existing = bookMapper.selectById(id) val existing = bookMapper.selectById(id)
?: run { ?: run {
log.warn("[AdminBook] updateStock: book not found, id={}", id) log.warn("[AdminBook] updateStock: book not found, id={}", id)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
val book = Book( val book = Book(
id = id, id = id,
@@ -89,7 +89,7 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
) )
bookMapper.updateById(book) bookMapper.updateById(book)
log.debug("[AdminBook] updateStock: success, id={}, stock={}", id, stock) log.debug("[AdminBook] updateStock: success, id={}, stock={}", id, stock)
return Result.success("Book updated successfully") return ApiResult.success("Book updated successfully")
} }
// 查询是否有书名和作者完全相同的书 // 查询是否有书名和作者完全相同的书
@@ -9,7 +9,7 @@ import com.msksbr.bookmgr.mapper.BorrowRecordMapper
import com.msksbr.bookmgr.mapper.UserMapper import com.msksbr.bookmgr.mapper.UserMapper
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AdminBorrowService import com.msksbr.bookmgr.service.AdminBorrowService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.BorrowInfoVo import com.msksbr.bookmgr.vo.BorrowInfoVo
import com.msksbr.bookmgr.vo.borrow.BookBorrowVo import com.msksbr.bookmgr.vo.borrow.BookBorrowVo
import com.msksbr.bookmgr.vo.borrow.UserBorrowVo import com.msksbr.bookmgr.vo.borrow.UserBorrowVo
@@ -34,10 +34,10 @@ class AdminBorrowServiceImpl(
* 搜索借阅记录:并发查询 user 表和 book 表,按用户名、角色、书名或作者匹配 * 搜索借阅记录:并发查询 user 表和 book 表,按用户名、角色、书名或作者匹配
* 将匹配到的 userId / bookId 取并集后查询借阅记录,降序排列 * 将匹配到的 userId / bookId 取并集后查询借阅记录,降序排列
*/ */
override fun searchBorrows(query: String): Result<Any?> { override fun searchBorrows(query: String): ApiResult<List<BorrowInfoVo>> {
if (query.isBlank()) { if (query.isBlank()) {
log.warn("[AdminBorrow] search: query is blank") log.warn("[AdminBorrow] search: query is blank")
return Result.error("Search query cannot be empty") return ApiResult.error("Search query cannot be empty")
} }
val (matchedUserIds, matchedBookIds) = runBlocking { val (matchedUserIds, matchedBookIds) = runBlocking {
val userDeferred = async(Dispatchers.IO) { val userDeferred = async(Dispatchers.IO) {
@@ -60,7 +60,7 @@ class AdminBorrowServiceImpl(
} }
if (matchedUserIds.isEmpty() && matchedBookIds.isEmpty()) { if (matchedUserIds.isEmpty() && matchedBookIds.isEmpty()) {
log.info("[AdminBorrow] search: no results for {}", query) log.info("[AdminBorrow] search: no results for {}", query)
return Result.notFound("No matching borrow records found") return ApiResult.notFound("No matching borrow records found")
} }
val borrows = borrowRecordMapper.selectList( val borrows = borrowRecordMapper.selectList(
QueryWrapper<BorrowRecord>() QueryWrapper<BorrowRecord>()
@@ -75,17 +75,17 @@ class AdminBorrowServiceImpl(
buildBorrowVos(borrows, userIds, bookIds) buildBorrowVos(borrows, userIds, bookIds)
} }
log.info("[AdminBorrow] search: found {} records for {}", result.size, query) log.info("[AdminBorrow] search: found {} records for {}", result.size, query)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
* 查询单条借阅记录,含关联的图书和用户信息 * 查询单条借阅记录,含关联的图书和用户信息
*/ */
override fun getOneBorrow(id: Long): Result<Any?> { override fun getOneBorrow(id: Long): ApiResult<BorrowInfoVo> {
val borrow = borrowRecordMapper.selectById(id) val borrow = borrowRecordMapper.selectById(id)
if (borrow == null) { if (borrow == null) {
log.info("[AdminBorrow] getOne: no record for id={}", id) log.info("[AdminBorrow] getOne: no record for id={}", id)
return Result.notFound("Borrow record not found") return ApiResult.notFound("Borrow record not found")
} }
val user = userMapper.selectById(borrow.userId) val user = userMapper.selectById(borrow.userId)
val book = bookMapper.selectById(borrow.bookId) val book = bookMapper.selectById(borrow.bookId)
@@ -106,17 +106,17 @@ class AdminBorrowServiceImpl(
) )
) )
log.info("[AdminBorrow] getOne: found record id={}, user={}, book={}", id, user.username, book.name) log.info("[AdminBorrow] getOne: found record id={}, user={}, book={}", id, user.username, book.name)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
* 查询全部借阅记录,并发加载关联的用户和图书信息 * 查询全部借阅记录,并发加载关联的用户和图书信息
*/ */
override fun getAllBorrows(): Result<Any?> { override fun getAllBorrows(): ApiResult<List<BorrowInfoVo>> {
val borrows = borrowRecordMapper.selectList(null) val borrows = borrowRecordMapper.selectList(null)
if (borrows.isEmpty()) { if (borrows.isEmpty()) {
log.info("[AdminBorrow] getAll: no records") log.info("[AdminBorrow] getAll: no records")
return Result.notFound("No borrow records found") return ApiResult.notFound("No borrow records found")
} }
val userIds = borrows.map { it.userId }.distinct() val userIds = borrows.map { it.userId }.distinct()
val bookIds = borrows.map { it.bookId }.distinct() val bookIds = borrows.map { it.bookId }.distinct()
@@ -125,13 +125,13 @@ class AdminBorrowServiceImpl(
val result = runBlocking { buildBorrowVos(borrows, userIds, bookIds) } val result = runBlocking { buildBorrowVos(borrows, userIds, bookIds) }
log.info("[AdminBorrow] getAll: found {} records", result.size) log.info("[AdminBorrow] getAll: found {} records", result.size)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
* 管理员手动借书 * 管理员手动借书
*/ */
override fun borrowBook(bookId: Long, userId: Long): Result<Any?> { override fun borrowBook(bookId: Long, userId: Long): ApiResult<String> {
val (matchedUser, matchedBook) = runBlocking { val (matchedUser, matchedBook) = runBlocking {
val userDeferred = async(Dispatchers.IO) { val userDeferred = async(Dispatchers.IO) {
userMapper.selectById(userId) userMapper.selectById(userId)
@@ -143,15 +143,15 @@ class AdminBorrowServiceImpl(
} }
if (matchedUser == null) { if (matchedUser == null) {
log.warn("[AdminBorrow] borrowBook: user not found, userId={}", userId) log.warn("[AdminBorrow] borrowBook: user not found, userId={}", userId)
return Result.error("User does not exist") return ApiResult.error("User does not exist")
} }
if (matchedBook == null) { if (matchedBook == null) {
log.warn("[AdminBorrow] borrowBook: book not found, bookId={}", bookId) log.warn("[AdminBorrow] borrowBook: book not found, bookId={}", bookId)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
if (matchedBook.stock < 1) { if (matchedBook.stock < 1) {
log.warn("[AdminBorrow] borrowBook: book out of stock, bookId={}, stock={}", bookId, matchedBook.stock) log.warn("[AdminBorrow] borrowBook: book out of stock, bookId={}, stock={}", bookId, matchedBook.stock)
return Result.conflict("Book is out of stock") return ApiResult.conflict("Book is out of stock")
} }
val book = Book( val book = Book(
id = matchedBook.id, id = matchedBook.id,
@@ -170,21 +170,21 @@ class AdminBorrowServiceImpl(
bookMapper.updateById(book) bookMapper.updateById(book)
borrowRecordMapper.insert(borrow) borrowRecordMapper.insert(borrow)
log.info("[AdminBorrow] borrowBook: success, userId={}, bookId={}, book={}", userId, bookId, matchedBook.name) log.info("[AdminBorrow] borrowBook: success, userId={}, bookId={}, book={}", userId, bookId, matchedBook.name)
return Result.success("Book borrowed successfully") return ApiResult.success("Book borrowed successfully")
} }
/* /*
* 管理员手动还书 * 管理员手动还书
*/ */
override fun returnBook(recordId: Long): Result<Any?> { override fun returnBook(recordId: Long): ApiResult<String> {
val matchedBorrow = borrowRecordMapper.selectById(recordId) val matchedBorrow = borrowRecordMapper.selectById(recordId)
if (matchedBorrow == null) { if (matchedBorrow == null) {
log.warn("[AdminBorrow] returnBook: record not found, recordId={}", recordId) log.warn("[AdminBorrow] returnBook: record not found, recordId={}", recordId)
return Result.error("Borrow record does not exist") return ApiResult.error("Borrow record does not exist")
} }
if (matchedBorrow.status == "RETURNED") { if (matchedBorrow.status == "RETURNED") {
log.warn("[AdminBorrow] returnBook: already returned, recordId={}", recordId) log.warn("[AdminBorrow] returnBook: already returned, recordId={}", recordId)
return Result.conflict("Book has already been returned") return ApiResult.conflict("Book has already been returned")
} }
val borrow = BorrowRecord( val borrow = BorrowRecord(
id = matchedBorrow.id, id = matchedBorrow.id,
@@ -197,7 +197,7 @@ class AdminBorrowServiceImpl(
val matchedBook = bookMapper.selectById(matchedBorrow.bookId) val matchedBook = bookMapper.selectById(matchedBorrow.bookId)
if (matchedBook == null) { if (matchedBook == null) {
log.warn("[AdminBorrow] returnBook: book not found, bookId={}", matchedBorrow.bookId) log.warn("[AdminBorrow] returnBook: book not found, bookId={}", matchedBorrow.bookId)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
val book = Book( val book = Book(
id = matchedBook.id, id = matchedBook.id,
@@ -208,7 +208,7 @@ class AdminBorrowServiceImpl(
bookMapper.updateById(book) bookMapper.updateById(book)
borrowRecordMapper.updateById(borrow) borrowRecordMapper.updateById(borrow)
log.info("[AdminBorrow] returnBook: success, recordId={}, userId={}, book={}", recordId, borrow.userId, matchedBook.name) log.info("[AdminBorrow] returnBook: success, recordId={}, userId={}, book={}", recordId, borrow.userId, matchedBook.name)
return Result.success("Book returned successfully") return ApiResult.success("Book returned successfully")
} }
// 并发查询用户和图书信息,组装为 BorrowInfoVo 列表 // 并发查询用户和图书信息,组装为 BorrowInfoVo 列表
@@ -5,7 +5,7 @@ import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.mapper.BookMapper import com.msksbr.bookmgr.mapper.BookMapper
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BookService import com.msksbr.bookmgr.service.BookService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
/* /*
@@ -14,10 +14,10 @@ import org.springframework.stereotype.Service
*/ */
@Service @Service
class BookServiceImpl(private val bookMapper: BookMapper) : BookService { class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
override fun searchBook(query: String): Result<Any?> { override fun searchBook(query: String): ApiResult<List<Book>> {
if (query.isBlank()) { if (query.isBlank()) {
log.warn("[Book] search: query is blank") log.warn("[Book] search: query is blank")
return Result.error("Search query cannot be empty") return ApiResult.error("Search query cannot be empty")
} }
val result = bookMapper.selectList( val result = bookMapper.selectList(
QueryWrapper<Book>() QueryWrapper<Book>()
@@ -27,16 +27,16 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
) )
if (result.isEmpty()) { if (result.isEmpty()) {
log.info("[Book] search: no results for {}", query) log.info("[Book] search: no results for {}", query)
return Result.notFound("No matching books found") return ApiResult.notFound("No matching books found")
} }
log.info("[Book] search: found {} results for {}", result.size, query) log.info("[Book] search: found {} results for {}", result.size, query)
return Result.success(result) return ApiResult.success(result)
} }
override fun getOneBook(id: Long): Result<Any?> { override fun getOneBook(id: Long): ApiResult<Book> {
if (id < 1) { if (id < 1) {
log.warn("[Book] getOne: invalid id={}", id) log.warn("[Book] getOne: invalid id={}", id)
return Result.error("Invalid book ID") return ApiResult.error("Invalid book ID")
} }
val result = bookMapper.selectOne( val result = bookMapper.selectOne(
QueryWrapper<Book>() QueryWrapper<Book>()
@@ -44,15 +44,15 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
) )
if (result == null) { if (result == null) {
log.info("[Book] getOneBook: no results for {}", id) log.info("[Book] getOneBook: no results for {}", id)
return Result.notFound("Book not found") return ApiResult.notFound("Book not found")
} }
log.info("[Book] getOne: found {}, author={}", result.name, result.author) log.info("[Book] getOne: found {}, author={}", result.name, result.author)
return Result.success(result) return ApiResult.success(result)
} }
override fun getAllBooks(): Result<Any?> { override fun getAllBooks(): ApiResult<List<Book>> {
val books = bookMapper.selectList(null) val books = bookMapper.selectList(null)
log.info("[Book] getAll: found {} books", books.size) log.info("[Book] getAll: found {} books", books.size)
return Result.success(books) return ApiResult.success(books)
} }
} }
@@ -7,7 +7,7 @@ import com.msksbr.bookmgr.mapper.BookMapper
import com.msksbr.bookmgr.mapper.BorrowRecordMapper import com.msksbr.bookmgr.mapper.BorrowRecordMapper
import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BorrowService import com.msksbr.bookmgr.service.BorrowService
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.ApiResult
import com.msksbr.bookmgr.vo.MyBorrowVo import com.msksbr.bookmgr.vo.MyBorrowVo
import com.msksbr.bookmgr.vo.borrow.BookBorrowVo import com.msksbr.bookmgr.vo.borrow.BookBorrowVo
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -28,18 +28,18 @@ class BorrowServiceImpl(
/* /*
* 查询当前用户的所有借阅记录 * 查询当前用户的所有借阅记录
*/ */
override fun getAllMyBorrows(userId: Long): Result<Any?> { override fun getAllMyBorrows(userId: Long): ApiResult<List<MyBorrowVo>> {
val borrows = borrowRecordMapper.selectList( val borrows = borrowRecordMapper.selectList(
QueryWrapper<BorrowRecord>().eq("user_id", userId).orderByDesc("borrow_time") QueryWrapper<BorrowRecord>().eq("user_id", userId).orderByDesc("borrow_time")
) )
if (borrows.isEmpty()) { if (borrows.isEmpty()) {
log.info("[Borrow] getAll: no records for userId={}", userId) log.info("[Borrow] getAll: no records for userId={}", userId)
return Result.notFound("No borrow records found") return ApiResult.notFound("No borrow records found")
} }
val bookIds = borrows.map { it.bookId }.distinct() val bookIds = borrows.map { it.bookId }.distinct()
val result = runBlocking { buildMyBorrowVos(borrows, bookIds) } val result = runBlocking { buildMyBorrowVos(borrows, bookIds) }
log.info("[Borrow] getAll: found {} records for userId={}", result.size, userId) log.info("[Borrow] getAll: found {} records for userId={}", result.size, userId)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
@@ -48,10 +48,10 @@ class BorrowServiceImpl(
override fun searchMyBorrows( override fun searchMyBorrows(
query: String, query: String,
userId: Long userId: Long
): Result<Any?> { ): ApiResult<List<MyBorrowVo>> {
if (query.isBlank()) { if (query.isBlank()) {
log.warn("[Borrow] search: query is blank") log.warn("[Borrow] search: query is blank")
return Result.error("Search query cannot be empty") return ApiResult.error("Search query cannot be empty")
} }
val matchedBookIds = bookMapper.selectList( val matchedBookIds = bookMapper.selectList(
QueryWrapper<Book>() QueryWrapper<Book>()
@@ -61,7 +61,7 @@ class BorrowServiceImpl(
).map { it.id } ).map { it.id }
if (matchedBookIds.isEmpty()) { if (matchedBookIds.isEmpty()) {
log.info("[Borrow] search: no results for {}", query) log.info("[Borrow] search: no results for {}", query)
return Result.notFound("No matching borrow records found") return ApiResult.notFound("No matching borrow records found")
} }
val borrows = borrowRecordMapper.selectList( val borrows = borrowRecordMapper.selectList(
QueryWrapper<BorrowRecord>() QueryWrapper<BorrowRecord>()
@@ -71,12 +71,12 @@ class BorrowServiceImpl(
) )
if (borrows.isEmpty()) { if (borrows.isEmpty()) {
log.info("[Borrow] search: no borrows match for userId={}, query={}", userId, query) log.info("[Borrow] search: no borrows match for userId={}, query={}", userId, query)
return Result.notFound("No matching borrow records found") return ApiResult.notFound("No matching borrow records found")
} }
val bookIds = borrows.map { it.bookId }.distinct() val bookIds = borrows.map { it.bookId }.distinct()
val result = runBlocking { buildMyBorrowVos(borrows, bookIds) } val result = runBlocking { buildMyBorrowVos(borrows, bookIds) }
log.info("[Borrow] search: found {} records for userId={}, query={}", result.size, userId, query) log.info("[Borrow] search: found {} records for userId={}, query={}", result.size, userId, query)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
@@ -85,18 +85,18 @@ class BorrowServiceImpl(
override fun getOneMyBorrow( override fun getOneMyBorrow(
borrowId: Long, borrowId: Long,
userId: Long userId: Long
): Result<Any?> { ): ApiResult<MyBorrowVo> {
val borrow = borrowRecordMapper.selectById(borrowId) val borrow = borrowRecordMapper.selectById(borrowId)
if (borrow == null) { if (borrow == null) {
log.info("[Borrow] getOne: record not found, borrowId={}", borrowId) log.info("[Borrow] getOne: record not found, borrowId={}", borrowId)
return Result.notFound("Borrow record not found") return ApiResult.notFound("Borrow record not found")
} }
if (borrow.userId != userId) { if (borrow.userId != userId) {
log.warn( log.warn(
"[Borrow] getOne: OWNERSHIP MISMATCH — userId={} attempted to access borrowId={} belonging to userId={}, bookId={}, status={}", "[Borrow] getOne: OWNERSHIP MISMATCH — userId={} attempted to access borrowId={} belonging to userId={}, bookId={}, status={}",
userId, borrowId, borrow.userId, borrow.bookId, borrow.status userId, borrowId, borrow.userId, borrow.bookId, borrow.status
) )
return Result.notFound("Borrow record not found") return ApiResult.notFound("Borrow record not found")
} }
val book = bookMapper.selectById(borrow.bookId) val book = bookMapper.selectById(borrow.bookId)
val result = MyBorrowVo( val result = MyBorrowVo(
@@ -109,7 +109,7 @@ class BorrowServiceImpl(
} ?: BookBorrowVo() } ?: BookBorrowVo()
) )
log.info("[Borrow] getOne: found record borrowId={}, userId={}, book={}", borrowId, userId, book?.name) log.info("[Borrow] getOne: found record borrowId={}, userId={}, book={}", borrowId, userId, book?.name)
return Result.success(result) return ApiResult.success(result)
} }
/* /*
@@ -118,15 +118,15 @@ class BorrowServiceImpl(
override fun borrowBookForMe( override fun borrowBookForMe(
bookId: Long, bookId: Long,
userId: Long userId: Long
): Result<Any?> { ): ApiResult<String> {
val matchedBook = bookMapper.selectById(bookId) val matchedBook = bookMapper.selectById(bookId)
if (matchedBook == null) { if (matchedBook == null) {
log.warn("[Borrow] borrow: book not found, bookId={}", bookId) log.warn("[Borrow] borrow: book not found, bookId={}", bookId)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
if (matchedBook.stock < 1) { if (matchedBook.stock < 1) {
log.warn("[Borrow] borrow: book out of stock, bookId={}, stock={}", bookId, matchedBook.stock) log.warn("[Borrow] borrow: book out of stock, bookId={}, stock={}", bookId, matchedBook.stock)
return Result.conflict("Book is out of stock") return ApiResult.conflict("Book is out of stock")
} }
val book = Book( val book = Book(
id = matchedBook.id, id = matchedBook.id,
@@ -145,7 +145,7 @@ class BorrowServiceImpl(
bookMapper.updateById(book) bookMapper.updateById(book)
borrowRecordMapper.insert(borrow) borrowRecordMapper.insert(borrow)
log.info("[Borrow] borrow: success, userId={}, bookId={}, book={}", userId, bookId, matchedBook.name) log.info("[Borrow] borrow: success, userId={}, bookId={}, book={}", userId, bookId, matchedBook.name)
return Result.success("Book borrowed successfully") return ApiResult.success("Book borrowed successfully")
} }
/* /*
@@ -154,22 +154,22 @@ class BorrowServiceImpl(
override fun returnBookForMe( override fun returnBookForMe(
borrowId: Long, borrowId: Long,
userId: Long userId: Long
): Result<Any?> { ): ApiResult<String> {
val matchedBorrow = borrowRecordMapper.selectById(borrowId) val matchedBorrow = borrowRecordMapper.selectById(borrowId)
if (matchedBorrow == null) { if (matchedBorrow == null) {
log.warn("[Borrow] return: record not found, borrowId={}", borrowId) log.warn("[Borrow] return: record not found, borrowId={}", borrowId)
return Result.error("Borrow record does not exist") return ApiResult.error("Borrow record does not exist")
} }
if (matchedBorrow.userId != userId) { if (matchedBorrow.userId != userId) {
log.warn( log.warn(
"[Borrow] return: OWNERSHIP MISMATCH — userId={} attempted to return borrowId={} belonging to userId={}, bookId={}, status={}", "[Borrow] return: OWNERSHIP MISMATCH — userId={} attempted to return borrowId={} belonging to userId={}, bookId={}, status={}",
userId, borrowId, matchedBorrow.userId, matchedBorrow.bookId, matchedBorrow.status userId, borrowId, matchedBorrow.userId, matchedBorrow.bookId, matchedBorrow.status
) )
return Result.error("Borrow record does not exist") return ApiResult.error("Borrow record does not exist")
} }
if (matchedBorrow.status == "RETURNED") { if (matchedBorrow.status == "RETURNED") {
log.warn("[Borrow] return: already returned, borrowId={}", borrowId) log.warn("[Borrow] return: already returned, borrowId={}", borrowId)
return Result.conflict("Book has already been returned") return ApiResult.conflict("Book has already been returned")
} }
val borrow = BorrowRecord( val borrow = BorrowRecord(
id = matchedBorrow.id, id = matchedBorrow.id,
@@ -182,7 +182,7 @@ class BorrowServiceImpl(
val matchedBook = bookMapper.selectById(matchedBorrow.bookId) val matchedBook = bookMapper.selectById(matchedBorrow.bookId)
if (matchedBook == null) { if (matchedBook == null) {
log.warn("[Borrow] return: book not found, bookId={}", matchedBorrow.bookId) log.warn("[Borrow] return: book not found, bookId={}", matchedBorrow.bookId)
return Result.error("Book does not exist") return ApiResult.error("Book does not exist")
} }
val book = Book( val book = Book(
id = matchedBook.id, id = matchedBook.id,
@@ -193,7 +193,7 @@ class BorrowServiceImpl(
bookMapper.updateById(book) bookMapper.updateById(book)
borrowRecordMapper.updateById(borrow) borrowRecordMapper.updateById(borrow)
log.info("[Borrow] return: success, borrowId={}, userId={}, book={}", borrowId, userId, matchedBook.name) log.info("[Borrow] return: success, borrowId={}, userId={}, book={}", borrowId, userId, matchedBook.name)
return Result.success("Book returned successfully") return ApiResult.success("Book returned successfully")
} }
// 并发查询图书信息,组装为 MyBorrowVo 列表 // 并发查询图书信息,组装为 MyBorrowVo 列表
@@ -13,7 +13,7 @@ package com.msksbr.bookmgr.template
* message - 人类可读的提示信息前端可直接展示 * message - 人类可读的提示信息前端可直接展示
* data - 响应数据体可为 null * data - 响应数据体可为 null
*/ */
data class Result<T>( data class ApiResult<T>(
var code: Int, var code: Int,
var message: String, var message: String,
var data: T? var data: T?
@@ -25,8 +25,8 @@ data class Result<T>(
* *
* JSON 输出示例{ "code": 200, "message": "success", "data": {...} } * JSON 输出示例{ "code": 200, "message": "success", "data": {...} }
*/ */
fun <T> success(data: T): Result<T> { fun <T> success(data: T): ApiResult<T> {
return Result( return ApiResult(
code = 200, code = 200,
message = "success", message = "success",
data = data data = data
@@ -39,8 +39,8 @@ data class Result<T>(
* *
* JSON 输出示例{ "code": 500, "message": "xxx", "data": null } * JSON 输出示例{ "code": 500, "message": "xxx", "data": null }
*/ */
fun error(message: String): Result<Any?> { fun <T> error(message: String): ApiResult<T> {
return Result( return ApiResult(
code = 500, code = 500,
message = message, message = message,
data = null data = null
@@ -53,8 +53,8 @@ data class Result<T>(
* *
* JSON 输出示例{ "code": 401, "message": "xxx" } * JSON 输出示例{ "code": 401, "message": "xxx" }
*/ */
fun unauthorized(message: String): Result<Any?> { fun <T> unauthorized(message: String): ApiResult<T> {
return Result( return ApiResult(
code = 401, code = 401,
message = message, message = message,
data = null data = null
@@ -65,8 +65,8 @@ data class Result<T>(
* 权限不足响应 返回 403 * 权限不足响应 返回 403
* JSON 输出示例{"code":403,"message":"Access denied: insufficient permissions"} * JSON 输出示例{"code":403,"message":"Access denied: insufficient permissions"}
*/ */
fun forbidden(message: String): Result<Any?> { fun <T> forbidden(message: String): ApiResult<T> {
return Result( return ApiResult(
code = 403, code = 403,
message = message, message = message,
data = null data = null
@@ -79,8 +79,8 @@ data class Result<T>(
* *
* JSON 输出示例{ "code": 404, "message": "xxx" } * JSON 输出示例{ "code": 404, "message": "xxx" }
*/ */
fun notFound(message: String): Result<Any?> { fun <T> notFound(message: String): ApiResult<T> {
return Result( return ApiResult(
code = 404, code = 404,
message = message, message = message,
data = null data = null
@@ -93,8 +93,8 @@ data class Result<T>(
* *
* JSON 输出示例{ "code": 409, "message": "xxx" } * JSON 输出示例{ "code": 409, "message": "xxx" }
*/ */
fun conflict(message: String): Result<Any?> { fun <T> conflict(message: String): ApiResult<T> {
return Result( return ApiResult(
code = 409, code = 409,
message = message, message = message,
data = null data = null
@@ -0,0 +1,7 @@
package com.msksbr.bookmgr.vo
data class LoginVo(
val token: String,
val username: String,
val role: String
)