From bfaa5a0dd928fd20049e0a613b8215bbf5f312ed Mon Sep 17 00:00:00 2001 From: msksbr515 Date: Fri, 22 May 2026 17:44:31 +0800 Subject: [PATCH] feat(books): add book search service and improve error responses - Add BookService interface and MyBatis-based implementation with fuzzy search by title or author - Add forbidden (403) response helper to Result template - Upgrade auth failure log from info to warn level - Reorganize BookController imports and restructure class --- .../bookmgr/controller/AuthController.kt | 2 +- .../bookmgr/controller/BookController.kt | 23 ++++++++++-- .../com/msksbr/bookmgr/service/BookService.kt | 16 +++++++++ .../bookmgr/service/impl/BookServiceImpl.kt | 35 +++++++++++++++++++ .../com/msksbr/bookmgr/template/Result.kt | 18 +++++++--- 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt create mode 100644 src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt index 964dae7..533f9f5 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt @@ -61,7 +61,7 @@ class AuthController( ) ) } else { - log.info("[Auth] login failed: user={}", loginDTO.username) + log.warn("[Auth] login failed: user={}", loginDTO.username) Result.error("Incorrect username or password") } } diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt index 357fd27..0107aeb 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/BookController.kt @@ -1,5 +1,13 @@ package com.msksbr.bookmgr.controller +import com.msksbr.bookmgr.config.IpExtractor +import com.msksbr.bookmgr.script.log +import com.msksbr.bookmgr.service.BookService +import com.msksbr.bookmgr.template.Result +import jakarta.servlet.http.HttpServletRequest +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestAttribute +import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController /* @@ -7,9 +15,20 @@ import org.springframework.web.bind.annotation.RestController * 路径前缀(待定):/api/books * * 计划接口: -* - 图书列表查询(支持分页、搜索) +* - 图书列表查询(搜索) * - 单本图书详情 */ @RestController -class BookController { +@RequestMapping("/api/books") +class BookController(private val bookService: BookService, private val ipExtractor: IpExtractor) { + @GetMapping("/search") + fun searchBook( + @RequestAttribute(required = false) username: String?, + query: String, + request: HttpServletRequest + ): Result { + 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) + } } diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt b/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt new file mode 100644 index 0000000..266294c --- /dev/null +++ b/src/main/kotlin/com/msksbr/bookmgr/service/BookService.kt @@ -0,0 +1,16 @@ +package com.msksbr.bookmgr.service + +import com.msksbr.bookmgr.template.Result + +/* +* 图书服务接口 +* 定义图书搜索逻辑的契约 +*/ +interface BookService { + /* + * 按书名或作者搜索图书 + * @param query 搜索关键词,不可为空 + * @return 搜索结果列表,无匹配时返回 404 + */ + fun searchBook(query: String): Result +} \ No newline at end of file diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt new file mode 100644 index 0000000..bc9a1a1 --- /dev/null +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/BookServiceImpl.kt @@ -0,0 +1,35 @@ +package com.msksbr.bookmgr.service.impl + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper +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 org.springframework.stereotype.Service +import com.msksbr.bookmgr.template.Result + +/* +* 图书服务实现 +* 提供图书搜索功能,按书名或作者进行模糊匹配 +*/ +@Service +class BookServiceImpl(private val bookMapper: BookMapper) : BookService { + override fun searchBook(query: String): Result { + if (query.isBlank()) { + log.warn("[Book] search: query is blank") + return Result.error("Search query cannot be empty") + } + val result = bookMapper.selectList( + QueryWrapper() + .like("name", query) + .or() + .like("author", query) + ) + if (result.isEmpty()) { + log.info("[Book] search: no results for {}", query) + return Result.notFound("No matching books found") + } + log.info("[Book] search: found {} results for {}", result.size, query) + return Result.success(result) + } +} diff --git a/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt b/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt index 5442e64..d757864 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/template/Result.kt @@ -53,10 +53,6 @@ data class Result( * * JSON 输出示例:{ "code": 401, "message": "xxx" } */ - /* - * 未登录响应 — 返回 401 - * JSON 输出示例:{"code":401,"message":"Missing Authorization header"} - */ fun unauthorized(message: String): Result { return Result( code = 401, @@ -76,5 +72,19 @@ data class Result( data = null ) } + + /* + * 未找到响应 — 返回 404 + 自定义提示 + * @param message 提示信息,如 "No matching books found" + * + * JSON 输出示例:{ "code": 404, "message": "xxx" } + */ + fun notFound(message: String): Result { + return Result( + code = 404, + message = message, + data = null + ) + } } }