diff --git a/build.gradle.kts b/build.gradle.kts index c0bdd33..dd0afef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") diff --git a/src/main/kotlin/com/msksbr/bookmgr/config/GlobalExceptionHandler.kt b/src/main/kotlin/com/msksbr/bookmgr/config/GlobalExceptionHandler.kt index 1270a51..21460fb 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/config/GlobalExceptionHandler.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/config/GlobalExceptionHandler.kt @@ -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{ + fun handle(ex: Exception): ApiResult{ log.error("[Global] unhandled exception: {}", ex.message) - return Result.error("Internal server error") + return ApiResult.error("Internal server error") } } \ No newline at end of file diff --git a/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt b/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt index afad2de..ef08953 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt @@ -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("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("Access denied: insufficient permissions") } log.info("[AUDIT] access allowed | user={} | ip={} | path={} | role={}", username, ip, path, role) diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBookController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBookController.kt index 2c84841..1985ddc 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBookController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBookController.kt @@ -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 { - log.info("[AdminBook] add: user={}, name={}, author={}", username ?: "guest", bookAddDto.name, bookAddDto.author) + ): ApiResult { + 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 { + ): ApiResult { 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 { + ): ApiResult { 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 { + ): ApiResult { 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) diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBorrowController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBorrowController.kt index 8c731f3..6e2d09e 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBorrowController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/AdminBorrowController.kt @@ -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 { + ): ApiResult> { 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 { + ): ApiResult { 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 { + ): ApiResult> { 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 { + ): ApiResult { 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 { + ): ApiResult { 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) diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt index ce226c4..1c0fc5e 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt @@ -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 { + ): ApiResult { 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 { + ): ApiResult { 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") } } diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt index 49fc199..a3f2c24 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt @@ -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 { + ): ApiResult> { 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 { + ): ApiResult { 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 { + ): ApiResult> { log.info("[Book] getAll: user={}", username ?: "guest") log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request)) return bookService.getAllBooks() diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/BorrowController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/BorrowController.kt index 9278089..fd79432 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/BorrowController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/BorrowController.kt @@ -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 { + ): ApiResult> { 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 { + ): ApiResult> { 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 { + ): ApiResult { 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 { + ): ApiResult { 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 { + ): ApiResult { 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!!) diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/AdminBookService.kt b/src/main/kotlin/com/msksbr/bookmgr/service/AdminBookService.kt index 92e6a22..baaf3e7 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/AdminBookService.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/AdminBookService.kt @@ -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 + fun addBook(bookAddDto: BookAddDto): ApiResult + /* * 修改图书信息(名称或作者) * @param id 图书 ID * @param bookUpdateDto 包含可选的 name 和 author * @return 成功返回 200,不存在返回 500,重复返回 409 */ - fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): Result + fun updateBook(id: Long, bookUpdateDto: BookUpdateDto): ApiResult + /* * 删除图书 * @param id 图书 ID * @return 成功返回 200,不存在返回 500 */ - fun deleteBook(id: Long): Result + fun deleteBook(id: Long): ApiResult + /* * 调整库存 * @param id 图书 ID * @param stock 新的库存数量,必须大于 0 * @return 成功返回 200,不存在或库存不合法返回 500 */ - fun updateStock(id: Long, stock: Int): Result + fun updateStock(id: Long, stock: Int): ApiResult } diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/AdminBorrowService.kt b/src/main/kotlin/com/msksbr/bookmgr/service/AdminBorrowService.kt index 6adf637..f181202 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/AdminBorrowService.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/AdminBorrowService.kt @@ -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 + fun searchBorrows(query: String): ApiResult> /* * 查询单条借阅记录 * @param id 借阅记录 ID * @return 借阅记录详情,含关联的图书和用户信息 */ - fun getOneBorrow(id: Long): Result + fun getOneBorrow(id: Long): ApiResult /* * 查询全部借阅记录 * @return 所有借阅记录,含关联的图书和用户信息 */ - fun getAllBorrows(): Result + fun getAllBorrows(): ApiResult> /* * 管理员手动借书 @@ -33,12 +34,12 @@ interface AdminBorrowService { * @param userId 借书的用户 ID * @return 借阅结果 */ - fun borrowBook(bookId: Long, userId: Long): Result + fun borrowBook(bookId: Long, userId: Long): ApiResult /* * 管理员手动还书 * @param recordId 借阅记录 ID * @return 归还结果 */ - fun returnBook(recordId: Long): Result + fun returnBook(recordId: Long): ApiResult } diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt b/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt index 2c7b1de..c0e66e9 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt @@ -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 + fun searchBook(query: String): ApiResult> /* * 根据 ID 查询单本图书 * @param id 图书 ID,必须为正整数 * @return 图书实体,不存在时返回 404 */ - fun getOneBook(id: Long): Result + fun getOneBook(id: Long): ApiResult /* * 查询全部图书列表 * @return 所有图书的列表 */ - fun getAllBooks(): Result + fun getAllBooks(): ApiResult> } \ No newline at end of file diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/BorrowService.kt b/src/main/kotlin/com/msksbr/bookmgr/service/BorrowService.kt index f43f4ab..7801de1 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/BorrowService.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/BorrowService.kt @@ -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 + fun getAllMyBorrows(userId: Long): ApiResult> /* * 搜索当前用户的借阅记录 @@ -20,7 +21,7 @@ interface BorrowService { * @param userId 当前用户的 ID * @return 匹配的借阅记录列表 */ - fun searchMyBorrows(query: String, userId: Long): Result + fun searchMyBorrows(query: String, userId: Long): ApiResult> /* * 查询单条借阅记录 @@ -28,7 +29,7 @@ interface BorrowService { * @param userId 当前用户的 ID * @return 借阅记录详情 */ - fun getOneMyBorrow(borrowId: Long, userId: Long): Result + fun getOneMyBorrow(borrowId: Long, userId: Long): ApiResult /* * 借书 @@ -36,7 +37,7 @@ interface BorrowService { * @param userId 当前用户的 ID * @return 借阅结果 */ - fun borrowBookForMe(bookId: Long, userId: Long): Result + fun borrowBookForMe(bookId: Long, userId: Long): ApiResult /* * 还书 @@ -44,5 +45,5 @@ interface BorrowService { * @param userId 当前用户的 ID * @return 归还结果 */ - fun returnBookForMe(borrowId: Long, userId: Long): Result + fun returnBookForMe(borrowId: Long, userId: Long): ApiResult } diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBookServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBookServiceImpl.kt index 6980602..02e5ec9 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBookServiceImpl.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBookServiceImpl.kt @@ -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 { + override fun addBook(bookAddDto: BookAddDto): ApiResult { 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 { + ): ApiResult { 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 { + override fun deleteBook(id: Long): ApiResult { 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 { + override fun updateStock(id: Long, stock: Int): ApiResult { 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") } // 查询是否有书名和作者完全相同的书 diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBorrowServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBorrowServiceImpl.kt index 575e00d..6639670 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBorrowServiceImpl.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AdminBorrowServiceImpl.kt @@ -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 { + override fun searchBorrows(query: String): ApiResult> { 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() @@ -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 { + override fun getOneBorrow(id: Long): ApiResult { 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 { + override fun getAllBorrows(): ApiResult> { 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 { + override fun borrowBook(bookId: Long, userId: Long): ApiResult { 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 { + override fun returnBook(recordId: Long): ApiResult { 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 列表 diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt index fc2a8d3..5dfeb1e 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt @@ -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 { + override fun searchBook(query: String): ApiResult> { 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() @@ -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 { + override fun getOneBook(id: Long): ApiResult { 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() @@ -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 { + override fun getAllBooks(): ApiResult> { val books = bookMapper.selectList(null) log.info("[Book] getAll: found {} books", books.size) - return Result.success(books) + return ApiResult.success(books) } } diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/BorrowServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BorrowServiceImpl.kt index ce10a7d..7327858 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/impl/BorrowServiceImpl.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BorrowServiceImpl.kt @@ -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 { + override fun getAllMyBorrows(userId: Long): ApiResult> { val borrows = borrowRecordMapper.selectList( QueryWrapper().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 { + ): ApiResult> { 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() @@ -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() @@ -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 { + ): ApiResult { 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 { + ): ApiResult { 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 { + ): ApiResult { 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 列表 diff --git a/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt b/src/main/kotlin/com/msksbr/bookmgr/template/ApiResult.kt similarity index 83% rename from src/main/kotlin/com/msksbr/bookmgr/template/Result.kt rename to src/main/kotlin/com/msksbr/bookmgr/template/ApiResult.kt index c41fffd..8e5eb4d 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/template/ApiResult.kt @@ -13,7 +13,7 @@ package com.msksbr.bookmgr.template * message - 人类可读的提示信息,前端可直接展示 * data - 响应数据体,可为 null */ -data class Result( +data class ApiResult( var code: Int, var message: String, var data: T? @@ -25,8 +25,8 @@ data class Result( * * JSON 输出示例:{ "code": 200, "message": "success", "data": {...} } */ - fun success(data: T): Result { - return Result( + fun success(data: T): ApiResult { + return ApiResult( code = 200, message = "success", data = data @@ -39,8 +39,8 @@ data class Result( * * JSON 输出示例:{ "code": 500, "message": "xxx", "data": null } */ - fun error(message: String): Result { - return Result( + fun error(message: String): ApiResult { + return ApiResult( code = 500, message = message, data = null @@ -53,8 +53,8 @@ data class Result( * * JSON 输出示例:{ "code": 401, "message": "xxx" } */ - fun unauthorized(message: String): Result { - return Result( + fun unauthorized(message: String): ApiResult { + return ApiResult( code = 401, message = message, data = null @@ -65,8 +65,8 @@ data class Result( * 权限不足响应 — 返回 403 * JSON 输出示例:{"code":403,"message":"Access denied: insufficient permissions"} */ - fun forbidden(message: String): Result { - return Result( + fun forbidden(message: String): ApiResult { + return ApiResult( code = 403, message = message, data = null @@ -79,8 +79,8 @@ data class Result( * * JSON 输出示例:{ "code": 404, "message": "xxx" } */ - fun notFound(message: String): Result { - return Result( + fun notFound(message: String): ApiResult { + return ApiResult( code = 404, message = message, data = null @@ -93,8 +93,8 @@ data class Result( * * JSON 输出示例:{ "code": 409, "message": "xxx" } */ - fun conflict(message: String): Result { - return Result( + fun conflict(message: String): ApiResult { + return ApiResult( code = 409, message = message, data = null diff --git a/src/main/kotlin/com/msksbr/bookmgr/vo/LoginVo.kt b/src/main/kotlin/com/msksbr/bookmgr/vo/LoginVo.kt new file mode 100644 index 0000000..51705b8 --- /dev/null +++ b/src/main/kotlin/com/msksbr/bookmgr/vo/LoginVo.kt @@ -0,0 +1,7 @@ +package com.msksbr.bookmgr.vo + +data class LoginVo( + val token: String, + val username: String, + val role: String +) \ No newline at end of file