feat(admin-borrows): implement admin borrow management endpoints
- Add getAllBorrows, getOneBorrow, searchBorrows, and returnBook endpoints - Implement AdminBorrowServiceImpl with join-based record queries - Add getAllBooks endpoint to BookController - Include role validation, IP extraction, and audit logging
This commit is contained in:
@@ -1,15 +1,42 @@
|
|||||||
package com.msksbr.bookmgr.controller
|
package com.msksbr.bookmgr.controller
|
||||||
|
|
||||||
|
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 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
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 借阅管理接口(面向管理员)
|
* 借阅管理接口(面向管理员)
|
||||||
* 路径前缀(待定):/api/admin/borrows
|
* 路径前缀:/api/admin/borrows
|
||||||
*
|
*
|
||||||
* 计划接口:
|
* 接口:
|
||||||
* - 全量获取借阅记录
|
* - 全量获取借阅记录
|
||||||
* - 全量搜索借阅记录
|
* - 全量搜索借阅记录
|
||||||
|
* - 单条借阅详情
|
||||||
|
*
|
||||||
|
* 全部接口需 admin 角色,由 @RequireRole 切面校验
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
class AdminBorrowController {
|
@RequestMapping("/api/admin/borrows")
|
||||||
|
class AdminBorrowController(
|
||||||
|
private val adminBorrowService: AdminBorrowService,
|
||||||
|
private val ipExtractor: IpExtractor
|
||||||
|
) {
|
||||||
|
|
||||||
|
@RequireRole("admin")
|
||||||
|
@GetMapping("/getall")
|
||||||
|
fun getAllBorrows(
|
||||||
|
@RequestAttribute(required = false) username: String?,
|
||||||
|
request: HttpServletRequest
|
||||||
|
): Result<Any?> {
|
||||||
|
log.info("[AdminBorrow] getAll: user={}, ip={}", username ?: "guest", ipExtractor.getRealIp(request))
|
||||||
|
log.info("[AdminBorrow] user agent: {}", request.getHeader("User-Agent"))
|
||||||
|
return adminBorrowService.getAllBorrows()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 图书接口(面向普通用户)
|
* 图书接口(面向普通用户)
|
||||||
* 路径前缀(待定):/api/books
|
* 路径前缀:/api/books
|
||||||
*
|
*
|
||||||
* 接口:
|
* 接口:
|
||||||
* - 图书列表查询(搜索)
|
* - 图书列表查询(搜索)
|
||||||
@@ -31,14 +31,25 @@ class BookController(private val bookService: BookService, private val ipExtract
|
|||||||
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
|
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
|
||||||
return bookService.searchBook(query)
|
return bookService.searchBook(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/getone")
|
@GetMapping("/getone")
|
||||||
fun getOneBook(
|
fun getOneBook(
|
||||||
@RequestAttribute(required = false) username: String?,
|
@RequestAttribute(required = false) username: String?,
|
||||||
request: HttpServletRequest,
|
request: HttpServletRequest,
|
||||||
id: Long
|
id: Long
|
||||||
): Result<Any?> {
|
): Result<Any?> {
|
||||||
log.info("[Book] getOne: user={}, id={}", username ?: "guest", id)
|
log.info("[Book] getOne: user={}, book id={}", username ?: "guest", id)
|
||||||
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
|
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
|
||||||
return bookService.getOneBook(id)
|
return bookService.getOneBook(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/getall")
|
||||||
|
fun getAllBooks(
|
||||||
|
@RequestAttribute(required = false) username: String?,
|
||||||
|
request: HttpServletRequest
|
||||||
|
): Result<Any?> {
|
||||||
|
log.info("[Book] getAll: user={}", username ?: "guest")
|
||||||
|
log.info("[Book] user agent: {}, ip={}", request.getHeader("User-Agent"), ipExtractor.getRealIp(request))
|
||||||
|
return bookService.getAllBooks()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.msksbr.bookmgr.dto
|
|
||||||
|
|
||||||
import com.msksbr.bookmgr.dto.borrow.BookBorrowDto
|
|
||||||
import com.msksbr.bookmgr.dto.borrow.UserBorrowDto
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class BorrowInfoDto(
|
|
||||||
val id: Long,
|
|
||||||
val bookBorrowDto: BookBorrowDto,
|
|
||||||
val userBorrowDto: UserBorrowDto,
|
|
||||||
val borrowTime: Date,
|
|
||||||
val returnTime: Date?,
|
|
||||||
val string: String
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.msksbr.bookmgr.dto.borrow
|
|
||||||
|
|
||||||
data class BookBorrowDto(
|
|
||||||
val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val author: String,
|
|
||||||
val number: Int = 1
|
|
||||||
)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.msksbr.bookmgr.dto.borrow
|
|
||||||
|
|
||||||
data class UserBorrowDto(
|
|
||||||
val id: Long,
|
|
||||||
val username: String,
|
|
||||||
val role: String
|
|
||||||
)
|
|
||||||
@@ -1,6 +1,29 @@
|
|||||||
package com.msksbr.bookmgr.service
|
package com.msksbr.bookmgr.service
|
||||||
|
|
||||||
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅管理服务接口
|
||||||
|
* 定义借阅记录的查询逻辑,仅由管理员调用
|
||||||
|
*/
|
||||||
interface AdminBorrowService {
|
interface AdminBorrowService {
|
||||||
|
/*
|
||||||
|
* 搜索借阅记录
|
||||||
|
* @param query 搜索关键词,按书名或用户名模糊匹配
|
||||||
|
* @return 匹配的借阅记录列表
|
||||||
|
*/
|
||||||
fun searchBorrows(query: String): Result<Any?>
|
fun searchBorrows(query: String): Result<Any?>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 查询单条借阅记录
|
||||||
|
* @param id 借阅记录 ID
|
||||||
|
* @return 借阅记录详情,含关联的图书和用户信息
|
||||||
|
*/
|
||||||
|
fun getOneBorrow(id: Long): Result<Any?>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 查询全部借阅记录
|
||||||
|
* @return 所有借阅记录,含关联的图书和用户信息
|
||||||
|
*/
|
||||||
fun getAllBorrows(): Result<Any?>
|
fun getAllBorrows(): Result<Any?>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,9 @@ interface BookService {
|
|||||||
* @return 图书实体,不存在时返回 404
|
* @return 图书实体,不存在时返回 404
|
||||||
*/
|
*/
|
||||||
fun getOneBook(id: Long): Result<Any?>
|
fun getOneBook(id: Long): Result<Any?>
|
||||||
|
/*
|
||||||
|
* 查询全部图书列表
|
||||||
|
* @return 所有图书的列表
|
||||||
|
*/
|
||||||
fun getAllBooks(): Result<Any?>
|
fun getAllBooks(): Result<Any?>
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,44 @@
|
|||||||
package com.msksbr.bookmgr.service
|
package com.msksbr.bookmgr.service
|
||||||
|
|
||||||
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅服务接口
|
||||||
|
* 定义面向普通用户的借阅逻辑契约
|
||||||
|
*/
|
||||||
interface BorrowService {
|
interface BorrowService {
|
||||||
|
/*
|
||||||
|
* 查询当前用户的所有借阅记录
|
||||||
|
* @param userId 当前用户的 ID
|
||||||
|
* @return 该用户的所有借阅记录
|
||||||
|
*/
|
||||||
fun getAllMyBorrows(userId: Long): Result<Any?>
|
fun getAllMyBorrows(userId: Long): Result<Any?>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 搜索当前用户的借阅记录
|
||||||
|
* @param query 搜索关键词,按书名模糊匹配
|
||||||
|
* @return 匹配的借阅记录列表
|
||||||
|
*/
|
||||||
fun searchMyBorrows(query: String): Result<Any?>
|
fun searchMyBorrows(query: String): Result<Any?>
|
||||||
fun getOneBorrow(borrowId: Long): Result<Any?>
|
|
||||||
|
/*
|
||||||
|
* 查询单条借阅记录
|
||||||
|
* @param borrowId 借阅记录 ID
|
||||||
|
* @return 借阅记录详情
|
||||||
|
*/
|
||||||
|
fun getOneMyBorrow(borrowId: Long): Result<Any?>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借书
|
||||||
|
* @param bookId 要借的图书 ID
|
||||||
|
* @return 借阅结果
|
||||||
|
*/
|
||||||
fun borrowBook(bookId: Long): Result<Any?>
|
fun borrowBook(bookId: Long): Result<Any?>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 还书
|
||||||
|
* @param borrowId 借阅记录 ID
|
||||||
|
* @return 归还结果
|
||||||
|
*/
|
||||||
fun returnBook(borrowId: Long): Result<Any?>
|
fun returnBook(borrowId: Long): Result<Any?>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,70 @@
|
|||||||
package com.msksbr.bookmgr.service.impl
|
package com.msksbr.bookmgr.service.impl
|
||||||
|
|
||||||
|
import com.msksbr.bookmgr.mapper.BookMapper
|
||||||
|
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.service.AdminBorrowService
|
||||||
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
import com.msksbr.bookmgr.vo.BorrowInfoVo
|
||||||
|
import com.msksbr.bookmgr.vo.borrow.BookBorrowVo
|
||||||
|
import com.msksbr.bookmgr.vo.borrow.UserBorrowVo
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅管理服务实现
|
||||||
|
* 提供借阅记录的全量和单条查询,聚合图书和用户信息
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
class AdminBorrowServiceImpl : AdminBorrowService {
|
class AdminBorrowServiceImpl(
|
||||||
|
private val userMapper: UserMapper,
|
||||||
|
private val borrowRecordMapper: BorrowRecordMapper,
|
||||||
|
private val bookMapper: BookMapper
|
||||||
|
) : AdminBorrowService {
|
||||||
override fun searchBorrows(query: String): Result<Any?> {
|
override fun searchBorrows(query: String): Result<Any?> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllBorrows(): Result<Any?> {
|
override fun getOneBorrow(id: Long): Result<Any?> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun getAllBorrows(): Result<Any?> {
|
||||||
|
val borrows = borrowRecordMapper.selectList(null)
|
||||||
|
if (borrows.isEmpty()) {
|
||||||
|
log.info("[AdminBorrow] getAll: no records")
|
||||||
|
return Result.success(null)
|
||||||
|
}
|
||||||
|
val userIds = borrows.map { it.userId }.distinct()
|
||||||
|
val bookIds = borrows.map { it.bookId }.distinct()
|
||||||
|
|
||||||
|
val userMap = userMapper.selectByIds(userIds).associateBy { it.id }
|
||||||
|
val bookMap = bookMapper.selectByIds(bookIds).associateBy { it.id }
|
||||||
|
|
||||||
|
val result = borrows.map { borrow ->
|
||||||
|
BorrowInfoVo(
|
||||||
|
id = borrow.id!!,
|
||||||
|
borrowTime = borrow.borrowTime,
|
||||||
|
returnTime = borrow.returnTime,
|
||||||
|
status = borrow.status,
|
||||||
|
bookBorrowVo = bookMap[borrow.bookId]?.let { book ->
|
||||||
|
BookBorrowVo(
|
||||||
|
id = book.id!!,
|
||||||
|
name = book.name,
|
||||||
|
author = book.author,
|
||||||
|
)
|
||||||
|
} ?: BookBorrowVo(),
|
||||||
|
userBorrowVo = userMap[borrow.userId]?.let { user ->
|
||||||
|
UserBorrowVo(
|
||||||
|
id = user.id!!,
|
||||||
|
username = user.username,
|
||||||
|
role = user.role
|
||||||
|
)
|
||||||
|
} ?: UserBorrowVo()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("[AdminBorrow] getAll: found {} records", result.size)
|
||||||
|
return Result.success(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import com.msksbr.bookmgr.entity.Book
|
|||||||
import com.msksbr.bookmgr.mapper.BookMapper
|
import com.msksbr.bookmgr.mapper.BookMapper
|
||||||
import com.msksbr.bookmgr.script.log
|
import com.msksbr.bookmgr.script.log
|
||||||
import com.msksbr.bookmgr.service.BookService
|
import com.msksbr.bookmgr.service.BookService
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import com.msksbr.bookmgr.template.Result
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 图书服务实现
|
* 图书服务实现
|
||||||
* 提供图书搜索和单本图书查询功能
|
* 提供图书搜索、单本查询和全量查询功能
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
|
class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
|
||||||
@@ -42,7 +42,7 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
|
|||||||
QueryWrapper<Book>()
|
QueryWrapper<Book>()
|
||||||
.eq("id", id)
|
.eq("id", id)
|
||||||
)
|
)
|
||||||
if (result==null) {
|
if (result == null) {
|
||||||
log.info("[Book] getOneBook: no results for {}", id)
|
log.info("[Book] getOneBook: no results for {}", id)
|
||||||
return Result.notFound("Book not found")
|
return Result.notFound("Book not found")
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,8 @@ class BookServiceImpl(private val bookMapper: BookMapper) : BookService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllBooks(): Result<Any?> {
|
override fun getAllBooks(): Result<Any?> {
|
||||||
TODO("Not yet implemented")
|
val books = bookMapper.selectList(null)
|
||||||
|
log.info("[Book] getAll: found {} books", books.size)
|
||||||
|
return Result.success(books)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.msksbr.bookmgr.service.impl
|
|||||||
|
|
||||||
import com.msksbr.bookmgr.service.BorrowService
|
import com.msksbr.bookmgr.service.BorrowService
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class BorrowServiceImpl: BorrowService {
|
class BorrowServiceImpl: BorrowService {
|
||||||
@@ -13,7 +14,7 @@ class BorrowServiceImpl: BorrowService {
|
|||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOneBorrow(borrowId: Long): Result<Any?> {
|
override fun getOneMyBorrow(borrowId: Long): Result<Any?> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.msksbr.bookmgr.vo
|
||||||
|
|
||||||
|
import com.msksbr.bookmgr.vo.borrow.BookBorrowVo
|
||||||
|
import com.msksbr.bookmgr.vo.borrow.UserBorrowVo
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅记录聚合视图
|
||||||
|
* 将借阅记录、图书信息和用户信息组装为单一响应对象
|
||||||
|
*/
|
||||||
|
data class BorrowInfoVo(
|
||||||
|
val id: Long = 0,
|
||||||
|
val bookBorrowVo: BookBorrowVo = BookBorrowVo(),
|
||||||
|
val userBorrowVo: UserBorrowVo = UserBorrowVo(),
|
||||||
|
val borrowTime: Date = Date(),
|
||||||
|
val returnTime: Date? = null,
|
||||||
|
val status: String = ""
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.msksbr.bookmgr.vo.borrow
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅记录中的图书信息视图
|
||||||
|
* 用于组装借阅列表时附带的图书缩略信息
|
||||||
|
*/
|
||||||
|
data class BookBorrowVo(
|
||||||
|
val id: Long = 0,
|
||||||
|
val name: String = "",
|
||||||
|
val author: String = "",
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.msksbr.bookmgr.vo.borrow
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 借阅记录中的用户信息视图
|
||||||
|
* 用于组装借阅列表时附带的用户缩略信息
|
||||||
|
*/
|
||||||
|
data class UserBorrowVo(
|
||||||
|
val id: Long = 0,
|
||||||
|
val username: String = "",
|
||||||
|
val role: String = ""
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user