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
This commit is contained in:
2026-05-22 17:44:31 +08:00
parent 3e7145c091
commit bfaa5a0dd9
5 changed files with 87 additions and 7 deletions
@@ -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")
}
}
@@ -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<Any?> {
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)
}
}
@@ -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<Any?>
}
@@ -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<Any?> {
if (query.isBlank()) {
log.warn("[Book] search: query is blank")
return Result.error("Search query cannot be empty")
}
val result = bookMapper.selectList(
QueryWrapper<Book>()
.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)
}
}
@@ -53,10 +53,6 @@ data class Result<T>(
*
* JSON 输出示例:{ "code": 401, "message": "xxx" }
*/
/*
* 未登录响应 — 返回 401
* JSON 输出示例:{"code":401,"message":"Missing Authorization header"}
*/
fun unauthorized(message: String): Result<Any?> {
return Result(
code = 401,
@@ -76,5 +72,19 @@ data class Result<T>(
data = null
)
}
/*
* 未找到响应 — 返回 404 + 自定义提示
* @param message 提示信息,如 "No matching books found"
*
* JSON 输出示例:{ "code": 404, "message": "xxx" }
*/
fun notFound(message: String): Result<Any?> {
return Result(
code = 404,
message = message,
data = null
)
}
}
}