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.aspectj:aspectjweaver")
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("com.mysql:mysql-connector-j")
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 org.springframework.web.bind.annotation.ExceptionHandler
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"}
@@ -14,8 +14,8 @@ import com.msksbr.bookmgr.template.Result
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(Exception::class)
fun handle(ex: Exception): Result<Any?>{
fun handle(ex: Exception): ApiResult<Any?>{
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.script.log
import com.msksbr.bookmgr.template.Result
import com.msksbr.bookmgr.template.ApiResult
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@@ -44,7 +44,7 @@ class RequireRoleAspect(
if (username == null) {
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()
@@ -53,7 +53,7 @@ class RequireRoleAspect(
"[AUDIT] access denied | user={} | ip={} | path={} | required={} | actual={}",
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={}",
username, ip, path, role)
@@ -6,7 +6,7 @@ import com.msksbr.bookmgr.dto.BookAddDto
import com.msksbr.bookmgr.dto.BookUpdateDto
import com.msksbr.bookmgr.script.log
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.validation.Valid
import org.springframework.web.bind.annotation.*
@@ -35,8 +35,13 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
bookAddDto: BookAddDto,
request: HttpServletRequest,
@RequestAttribute(required = false) username: String?
): Result<Any?> {
log.info("[AdminBook] add: user={}, name={}, author={}", username ?: "guest", bookAddDto.name, bookAddDto.author)
): ApiResult<String> {
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))
return adminBookService.addBook(bookAddDto)
}
@@ -50,7 +55,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
bookUpdateDto: BookUpdateDto,
request: HttpServletRequest,
@RequestAttribute(required = false) username: String?
): Result<Any?> {
): ApiResult<String> {
log.info("[AdminBook] update: user={}, id={}", username ?: "guest", id)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.updateBook(id, bookUpdateDto)
@@ -62,7 +67,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
id: Long,
request: HttpServletRequest,
@RequestAttribute(required = false) username: String?
): Result<Any?> {
): ApiResult<String> {
log.info("[AdminBook] delete: user={}, id={}", username ?: "guest", id)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBookService.deleteBook(id)
@@ -75,7 +80,7 @@ class AdminBookController(private val adminBookService: AdminBookService, privat
stock: Int,
request: HttpServletRequest,
@RequestAttribute(required = false) username: String?
): Result<Any?> {
): ApiResult<String> {
log.info("[AdminBook] updateStock: user={}, id={}, stock={}", username ?: "guest", id, stock)
log.info("[AdminBook] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
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.script.log
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 org.springframework.web.bind.annotation.*
@@ -37,7 +38,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?,
query: String,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<BorrowInfoVo>> {
log.info("[AdminBorrow] search: user={}, query={}", username ?: "guest", query)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.searchBorrows(query)
@@ -53,7 +54,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?,
id: Long,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<BorrowInfoVo> {
log.info("[AdminBorrow] getOne: user={}, borrow record id={}", username ?: "guest", id)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.getOneBorrow(id)
@@ -68,7 +69,7 @@ class AdminBorrowController(
fun getAllBorrows(
@RequestAttribute(required = false) username: String?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<BorrowInfoVo>> {
log.info("[AdminBorrow] getAll: user={}, ip={}", username ?: "guest", ipExtractor.getRealIp(request))
log.info("[AdminBorrow] user agent: {}", request.getHeader("User-Agent"))
return adminBorrowService.getAllBorrows()
@@ -85,7 +86,7 @@ class AdminBorrowController(
bookId: Long,
userId: Long,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<String> {
log.info("[AdminBorrow] borrowBook: user={}, bookId={}, userId={}", username ?: "guest", bookId, userId)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.borrowBook(bookId, userId)
@@ -101,7 +102,7 @@ class AdminBorrowController(
@RequestAttribute(required = false) username: String?,
recordId: Long,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<String> {
log.info("[AdminBorrow] returnBook: user={}, recordId={}", username ?: "guest", recordId)
log.info("[AdminBorrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return adminBorrowService.returnBook(recordId)
@@ -5,7 +5,8 @@ import com.msksbr.bookmgr.config.JwtUtils
import com.msksbr.bookmgr.dto.UserLoginDto
import com.msksbr.bookmgr.script.log
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.validation.Valid
import org.springframework.web.bind.annotation.*
@@ -42,7 +43,7 @@ class AuthController(
@RequestBody
loginDTO: UserLoginDto,
request: HttpServletRequest
): Result<out Any?> {
): ApiResult<LoginVo> {
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)
@@ -50,16 +51,15 @@ class AuthController(
// 登录成功,签发 JWT
val token = jwtUtils.generateToken(user.username, user.role, user.id!!)
log.info("[Auth] login success: user={}", user.username)
Result.success(
mapOf(
"token" to token,
"username" to user.username,
"role" to user.role
)
val loginVo = LoginVo(
token = token,
username = user.username,
role = user.role
)
ApiResult.success(loginVo)
} else {
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(
@RequestAttribute(required = false) username: String?,
request: HttpServletRequest
): Result<String> {
): ApiResult<String> {
if (username != null) {
log.info("[Auth] logout username: {}", username)
}
@@ -82,6 +82,6 @@ class AuthController(
log.debug("[Auth] user agent: {}", request.getHeader("User-Agent"))
// JWT 无状态,登出只需客户端删除 token
log.info("[Auth] logout success")
return Result.success("logout successfully")
return ApiResult.success("logout successfully")
}
}
@@ -1,9 +1,10 @@
package com.msksbr.bookmgr.controller
import com.msksbr.bookmgr.config.IpExtractor
import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BookService
import com.msksbr.bookmgr.template.Result
import com.msksbr.bookmgr.template.ApiResult
import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.GetMapping
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?,
query: String,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<Book>> {
log.info("[Book] search: user={}, query={}", username ?: "guest", query)
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.searchBook(query)
@@ -37,7 +38,7 @@ class BookController(private val bookService: BookService, private val ipExtract
@RequestAttribute(required = false) username: String?,
request: HttpServletRequest,
id: Long
): Result<Any?> {
): ApiResult<Book> {
log.info("[Book] getOne: user={}, book id={}", username ?: "guest", id)
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.getOneBook(id)
@@ -47,7 +48,7 @@ class BookController(private val bookService: BookService, private val ipExtract
fun getAllBooks(
@RequestAttribute(required = false) username: String?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<Book>> {
log.info("[Book] getAll: user={}", username ?: "guest")
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return bookService.getAllBooks()
@@ -4,7 +4,8 @@ import com.msksbr.bookmgr.annotation.RequireRole
import com.msksbr.bookmgr.config.IpExtractor
import com.msksbr.bookmgr.script.log
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 org.springframework.web.bind.annotation.*
@@ -37,7 +38,7 @@ class BorrowController(
@RequestAttribute(required = false) username: String?,
@RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<MyBorrowVo>> {
log.info("[Borrow] getAll: user={}, userId={}", username ?: "guest", userId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.getAllMyBorrows(userId!!)
@@ -54,7 +55,7 @@ class BorrowController(
query: String,
@RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<List<MyBorrowVo>> {
log.info("[Borrow] search: user={}, userId={}, query={}", username ?: "guest", userId, query)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.searchMyBorrows(query, userId!!)
@@ -71,7 +72,7 @@ class BorrowController(
borrowId: Long,
@RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<MyBorrowVo> {
log.info("[Borrow] getOne: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.getOneMyBorrow(borrowId, userId!!)
@@ -88,7 +89,7 @@ class BorrowController(
bookId: Long,
@RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<String> {
log.info("[Borrow] borrow: user={}, userId={}, bookId={}", username ?: "guest", userId, bookId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
return borrowService.borrowBookForMe(bookId, userId!!)
@@ -105,7 +106,7 @@ class BorrowController(
borrowId: Long,
@RequestAttribute(required = false) userId: Long?,
request: HttpServletRequest
): Result<Any?> {
): ApiResult<String> {
log.info("[Borrow] return: user={}, userId={}, borrowId={}", username ?: "guest", userId, borrowId)
log.info("[Borrow] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
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.BookUpdateDto
import com.msksbr.bookmgr.template.Result
import com.msksbr.bookmgr.template.ApiResult
/*
* 图书管理服务接口
@@ -14,25 +14,28 @@ interface AdminBookService {
* @param bookAddDto 包含 name、author、stock
* @return 成功返回 200,重复返回 409
*/
fun addBook(bookAddDto: BookAddDto): Result<Any?>
fun addBook(bookAddDto: BookAddDto): ApiResult<String>
/*
* 修改图书信息(名称或作者)
* @param id 图书 ID
* @param bookUpdateDto 包含可选的 name 和 author
* @return 成功返回 200,不存在返回 500,重复返回 409
*/
fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): Result<Any?>
fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): ApiResult<String>
/*
* 删除图书
* @param id 图书 ID
* @return 成功返回 200,不存在返回 500
*/
fun deleteBook(id: Long): Result<Any?>
fun deleteBook(id: Long): ApiResult<String>
/*
* 调整库存
* @param id 图书 ID
* @param stock 新的库存数量,必须大于 0
* @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
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 搜索关键词,按书名或用户名模糊匹配
* @return 匹配的借阅记录列表
*/
fun searchBorrows(query: String): Result<Any?>
fun searchBorrows(query: String): ApiResult<List<BorrowInfoVo>>
/*
* 查询单条借阅记录
* @param id 借阅记录 ID
* @return 借阅记录详情,含关联的图书和用户信息
*/
fun getOneBorrow(id: Long): Result<Any?>
fun getOneBorrow(id: Long): ApiResult<BorrowInfoVo>
/*
* 查询全部借阅记录
* @return 所有借阅记录,含关联的图书和用户信息
*/
fun getAllBorrows(): Result<Any?>
fun getAllBorrows(): ApiResult<List<BorrowInfoVo>>
/*
* 管理员手动借书
@@ -33,12 +34,12 @@ interface AdminBorrowService {
* @param userId 借书的用户 ID
* @return 借阅结果
*/
fun borrowBook(bookId: Long, userId: Long): Result<Any?>
fun borrowBook(bookId: Long, userId: Long): ApiResult<String>
/*
* 管理员手动还书
* @param recordId 借阅记录 ID
* @return 归还结果
*/
fun returnBook(recordId: Long): Result<Any?>
fun returnBook(recordId: Long): ApiResult<String>
}
@@ -1,6 +1,7 @@
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 搜索关键词,不可为空
* @return 搜索结果列表,无匹配时返回 404
*/
fun searchBook(query: String): Result<Any?>
fun searchBook(query: String): ApiResult<List<Book>>
/*
* 根据 ID 查询单本图书
* @param id 图书 ID,必须为正整数
* @return 图书实体,不存在时返回 404
*/
fun getOneBook(id: Long): Result<Any?>
fun getOneBook(id: Long): ApiResult<Book>
/*
* 查询全部图书列表
* @return 所有图书的列表
*/
fun getAllBooks(): Result<Any?>
fun getAllBooks(): ApiResult<List<Book>>
}
@@ -1,6 +1,7 @@
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
* @return 该用户的所有借阅记录
*/
fun getAllMyBorrows(userId: Long): Result<Any?>
fun getAllMyBorrows(userId: Long): ApiResult<List<MyBorrowVo>>
/*
* 搜索当前用户的借阅记录
@@ -20,7 +21,7 @@ interface BorrowService {
* @param userId 当前用户的 ID
* @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
* @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
* @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
* @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.script.log
import com.msksbr.bookmgr.service.AdminBookService
import com.msksbr.bookmgr.template.Result
import com.msksbr.bookmgr.template.ApiResult
import org.springframework.stereotype.Service
/*
@@ -16,7 +16,7 @@ import org.springframework.stereotype.Service
*/
@Service
class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookService {
override fun addBook(bookAddDto: BookAddDto): Result<Any?> {
override fun addBook(bookAddDto: BookAddDto): ApiResult<String> {
val book = Book(
id = null,
name = bookAddDto.name,
@@ -25,25 +25,25 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
)
if (isBookDuplicate(book)) {
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)
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(
id: Long,
bookUpdateDto: BookUpdateDto
): Result<Any?> {
): ApiResult<String> {
if (bookUpdateDto.name.isBlank() && bookUpdateDto.author.isBlank()) {
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)
?: run {
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(
id = id,
@@ -53,33 +53,33 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
)
if (isBookDuplicate(book, book.id)) {
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)
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)
?: run {
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)
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) {
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)
?: run {
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(
id = id,
@@ -89,7 +89,7 @@ class AdminBookServiceImpl(private val bookMapper: BookMapper) : AdminBookServic
)
bookMapper.updateById(book)
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.script.log
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.borrow.BookBorrowVo
import com.msksbr.bookmgr.vo.borrow.UserBorrowVo
@@ -34,10 +34,10 @@ class AdminBorrowServiceImpl(
* 搜索借阅记录:并发查询 user 表和 book 表,按用户名、角色、书名或作者匹配
* 将匹配到的 userId / bookId 取并集后查询借阅记录,降序排列
*/
override fun searchBorrows(query: String): Result<Any?> {
override fun searchBorrows(query: String): ApiResult<List<BorrowInfoVo>> {
if (query.isBlank()) {
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 userDeferred = async(Dispatchers.IO) {
@@ -60,7 +60,7 @@ class AdminBorrowServiceImpl(
}
if (matchedUserIds.isEmpty() && matchedBookIds.isEmpty()) {
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(
QueryWrapper<BorrowRecord>()
@@ -75,17 +75,17 @@ class AdminBorrowServiceImpl(
buildBorrowVos(borrows, userIds, bookIds)
}
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)
if (borrow == null) {
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 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)
return Result.success(result)
return ApiResult.success(result)
}
/*
* 查询全部借阅记录,并发加载关联的用户和图书信息
*/
override fun getAllBorrows(): Result<Any?> {
override fun getAllBorrows(): ApiResult<List<BorrowInfoVo>> {
val borrows = borrowRecordMapper.selectList(null)
if (borrows.isEmpty()) {
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 bookIds = borrows.map { it.bookId }.distinct()
@@ -125,13 +125,13 @@ class AdminBorrowServiceImpl(
val result = runBlocking { buildBorrowVos(borrows, userIds, bookIds) }
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 userDeferred = async(Dispatchers.IO) {
userMapper.selectById(userId)
@@ -143,15 +143,15 @@ class AdminBorrowServiceImpl(
}
if (matchedUser == null) {
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) {
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) {
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(
id = matchedBook.id,
@@ -170,21 +170,21 @@ class AdminBorrowServiceImpl(
bookMapper.updateById(book)
borrowRecordMapper.insert(borrow)
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)
if (matchedBorrow == null) {
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") {
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(
id = matchedBorrow.id,
@@ -197,7 +197,7 @@ class AdminBorrowServiceImpl(
val matchedBook = bookMapper.selectById(matchedBorrow.bookId)
if (matchedBook == null) {
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(
id = matchedBook.id,
@@ -208,7 +208,7 @@ class AdminBorrowServiceImpl(
bookMapper.updateById(book)
borrowRecordMapper.updateById(borrow)
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 列表
@@ -5,7 +5,7 @@ import com.msksbr.bookmgr.entity.Book
import com.msksbr.bookmgr.mapper.BookMapper
import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.BookService
import com.msksbr.bookmgr.template.Result
import com.msksbr.bookmgr.template.ApiResult
import org.springframework.stereotype.Service
/*
@@ -14,10 +14,10 @@ import org.springframework.stereotype.Service
*/
@Service
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()) {
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(
QueryWrapper<Book>()
@@ -27,16 +27,16 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
)
if (result.isEmpty()) {
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)
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) {
log.warn("[Book] getOne: invalid id={}", id)
return Result.error("Invalid book ID")
return ApiResult.error("Invalid book ID")
}
val result = bookMapper.selectOne(
QueryWrapper<Book>()
@@ -44,15 +44,15 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
)
if (result == null) {
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)
return Result.success(result)
return ApiResult.success(result)
}
override fun getAllBooks(): Result<Any?> {
override fun getAllBooks(): ApiResult<List<Book>> {
val books = bookMapper.selectList(null)
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.script.log
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.borrow.BookBorrowVo
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(
QueryWrapper<BorrowRecord>().eq("user_id", userId).orderByDesc("borrow_time")
)
if (borrows.isEmpty()) {
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 result = runBlocking { buildMyBorrowVos(borrows, bookIds) }
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(
query: String,
userId: Long
): Result<Any?> {
): ApiResult<List<MyBorrowVo>> {
if (query.isBlank()) {
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(
QueryWrapper<Book>()
@@ -61,7 +61,7 @@ class BorrowServiceImpl(
).map { it.id }
if (matchedBookIds.isEmpty()) {
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(
QueryWrapper<BorrowRecord>()
@@ -71,12 +71,12 @@ class BorrowServiceImpl(
)
if (borrows.isEmpty()) {
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 result = runBlocking { buildMyBorrowVos(borrows, bookIds) }
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(
borrowId: Long,
userId: Long
): Result<Any?> {
): ApiResult<MyBorrowVo> {
val borrow = borrowRecordMapper.selectById(borrowId)
if (borrow == null) {
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) {
log.warn(
"[Borrow] getOne: OWNERSHIP MISMATCH — userId={} attempted to access borrowId={} belonging to userId={}, bookId={}, 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 result = MyBorrowVo(
@@ -109,7 +109,7 @@ class BorrowServiceImpl(
} ?: BookBorrowVo()
)
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(
bookId: Long,
userId: Long
): Result<Any?> {
): ApiResult<String> {
val matchedBook = bookMapper.selectById(bookId)
if (matchedBook == null) {
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) {
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(
id = matchedBook.id,
@@ -145,7 +145,7 @@ class BorrowServiceImpl(
bookMapper.updateById(book)
borrowRecordMapper.insert(borrow)
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(
borrowId: Long,
userId: Long
): Result<Any?> {
): ApiResult<String> {
val matchedBorrow = borrowRecordMapper.selectById(borrowId)
if (matchedBorrow == null) {
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) {
log.warn(
"[Borrow] return: OWNERSHIP MISMATCH — userId={} attempted to return borrowId={} belonging to userId={}, bookId={}, 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") {
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(
id = matchedBorrow.id,
@@ -182,7 +182,7 @@ class BorrowServiceImpl(
val matchedBook = bookMapper.selectById(matchedBorrow.bookId)
if (matchedBook == null) {
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(
id = matchedBook.id,
@@ -193,7 +193,7 @@ class BorrowServiceImpl(
bookMapper.updateById(book)
borrowRecordMapper.updateById(borrow)
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 列表
@@ -13,7 +13,7 @@ package com.msksbr.bookmgr.template
* message - 人类可读的提示信息前端可直接展示
* data - 响应数据体可为 null
*/
data class Result<T>(
data class ApiResult<T>(
var code: Int,
var message: String,
var data: T?
@@ -25,8 +25,8 @@ data class Result<T>(
*
* JSON 输出示例{ "code": 200, "message": "success", "data": {...} }
*/
fun <T> success(data: T): Result<T> {
return Result(
fun <T> success(data: T): ApiResult<T> {
return ApiResult(
code = 200,
message = "success",
data = data
@@ -39,8 +39,8 @@ data class Result<T>(
*
* JSON 输出示例{ "code": 500, "message": "xxx", "data": null }
*/
fun error(message: String): Result<Any?> {
return Result(
fun <T> error(message: String): ApiResult<T> {
return ApiResult(
code = 500,
message = message,
data = null
@@ -53,8 +53,8 @@ data class Result<T>(
*
* JSON 输出示例{ "code": 401, "message": "xxx" }
*/
fun unauthorized(message: String): Result<Any?> {
return Result(
fun <T> unauthorized(message: String): ApiResult<T> {
return ApiResult(
code = 401,
message = message,
data = null
@@ -65,8 +65,8 @@ data class Result<T>(
* 权限不足响应 返回 403
* JSON 输出示例{"code":403,"message":"Access denied: insufficient permissions"}
*/
fun forbidden(message: String): Result<Any?> {
return Result(
fun <T> forbidden(message: String): ApiResult<T> {
return ApiResult(
code = 403,
message = message,
data = null
@@ -79,8 +79,8 @@ data class Result<T>(
*
* JSON 输出示例{ "code": 404, "message": "xxx" }
*/
fun notFound(message: String): Result<Any?> {
return Result(
fun <T> notFound(message: String): ApiResult<T> {
return ApiResult(
code = 404,
message = message,
data = null
@@ -93,8 +93,8 @@ data class Result<T>(
*
* JSON 输出示例{ "code": 409, "message": "xxx" }
*/
fun conflict(message: String): Result<Any?> {
return Result(
fun <T> conflict(message: String): ApiResult<T> {
return ApiResult(
code = 409,
message = message,
data = null
@@ -0,0 +1,7 @@
package com.msksbr.bookmgr.vo
data class LoginVo(
val token: String,
val username: String,
val role: String
)