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:
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user