design.md 31 KB

校园二手书交易系统 — 详细设计文档

1. 引言

1.1 项目背景

校园内二手教材流转需求旺盛,但缺乏统一的线上交易平台。本项目旨在构建一个 C/S 架构的校园二手书交易系统,为学生提供便捷的书籍买卖渠道,为管理员提供数据监控与统计能力。

1.2 项目目标

  • 实现学生用户的注册、登录、浏览、搜索、购买书籍及订单管理
  • 支持卖家用户上架、编辑、下架书籍及查看销售记录
  • 提供管理员后台进行用户管理、商品管理、交易管理和数据统计
  • 预留 AI 分析扩展能力(固定流程 + LLM 调用)

1.3 适用范围

本系统面向高校在校学生与管理人员,部署于校园局域网环境。

1.4 术语与缩略语

术语 说明
Buyer 买家/普通学生用户
Seller 卖家(买家身份的权限扩展)
Admin 系统管理员
C/S Client/Server 架构
MyBatis-Plus MyBatis 增强工具
LLM Large Language Model,大语言模型

2. 总体设计

2.1 系统架构

本系统采用经典 C/S 两层架构:

flowchart TB
    subgraph Client [JavaFX 客户端 space.anyi.client]
        VIEW[FXML 视图层]
        CTRL[Controller 层]
        HSVC[HTTP Service 层]
    end
    subgraph Server [Spring Boot 服务端 space.anyi.server]
        CTL[Controller 层]
        SVC[Service 层]
        MPR[Mapper 层]
    end
    subgraph DB [MySQL 8.0]
        D[(campus_book_trade)]
    end

    Client <-->|HTTP/JSON RESTful API| Server
    MPR <--> D

客户端与服务端之间仅通过 HTTP RESTful API 通信,无直接依赖。共享模块 common 提供实体类、DTO 和工具类。

2.2 技术栈

层次 技术 版本
客户端 UI JavaFX + FXML + Scene Builder 23.0.2
客户端 HTTP java.net.http.HttpClient JDK 内置
JSON 序列化 Jackson (jackson-databind + jackson-datatype-jsr310) 2.17.3
服务端框架 Spring Boot Web 3.4.4
ORM MyBatis-Plus (mybatis-plus-spring-boot3-starter) 3.5.9
数据库 MySQL 8.0+ (Connector 8.4.0)
构建工具 Maven (3.9+)
Java Corretto-23 23

2.3 模块划分

CampusUsedBookTradingSystem/
├── common/         共享模块 — 实体、DTO、枚举
├── server/         Spring Boot 服务端
│   ├── config/        配置类(CORS、MyBatis-Plus、异常处理)
│   ├── controller/    REST 控制器
│   ├── service/       业务接口
│   ├── service/impl/  业务实现
│   └── mapper/        MyBatis-Plus Mapper + XML
├── client/         JavaFX 客户端
│   ├── controller/    JavaFX 控制器
│   ├── service/       HTTP 调用封装
│   └── util/          工具类
└── sql/            数据库建表脚本

依赖关系:server → commonclient → common

2.4 包结构总览

space.anyi
├── common
│   ├── entity/          User, Book, TransactionRecord
│   ├── dto/             LoginRequest, RegisterRequest, R<T>, PageResult<T>,
│   │                    StatisticsSummary, DeptSalesDTO, DeptDiscountDTO, HotBookDTO
│   └── enums/           UserRole
├── server
│   ├── ServerApplication.java
│   ├── config/          WebConfig, MyBatisPlusConfig, GlobalExceptionHandler
│   ├── controller/      UserController, BookController, TransactionController, StatisticsController
│   ├── service/         UserService, BookService, TransactionService, StatisticsService
│   ├── service/impl/    UserServiceImpl, BookServiceImpl, TransactionServiceImpl, StatisticsServiceImpl
│   └── mapper/          UserMapper, BookMapper, TransactionRecordMapper, StatisticsMapper
└── client
    ├── App.java, Client.java
    ├── controller/      LoginController, RegisterController, MainController
    ├── service/         UserService, BookService, TransactionService, StatisticsService
    └── util/            HttpClientUtil, LoginUserHolder

3. 数据库设计

3.1 ER 图

erDiagram
    USER {
        int userid PK
        varchar username
        varchar password
        varchar nickname
        varchar email
        varchar phone
        varchar dept
        int credit
        int role
        int is_seller
        datetime created_at
    }

    BOOK {
        int book_id PK
        int seller_id FK
        varchar title
        varchar author
        varchar category
        decimal original_price
        decimal selling_price
        varchar image_url
        datetime created_at
        int stock
    }

    TRANSACTION_RECORD {
        int trans_id PK
        int book_id FK
        int buyer_id FK
        int transaction_num
        decimal transaction_price
        datetime transaction_time
    }

    USER ||--o{ BOOK : "seller"
    USER ||--o{ TRANSACTION_RECORD : "buyer"
    BOOK ||--o{ TRANSACTION_RECORD : "contains"

3.2 表结构

3.2.1 user 表

字段 类型 约束 说明
userid int PK, AUTO_INCREMENT 用户ID
username varchar(255) 用户名
password varchar(255) 密码(明文存储,后续改进)
nickname varchar(255) 昵称
email varchar(255) 邮箱
phone varchar(255) 电话
dept varchar(255) 院系
credit int DEFAULT 100 信用分
role int DEFAULT 0 角色(0=学生, 1=管理员)
is_seller int DEFAULT 0 是否为卖家(0=否, 1=是)
created_at datetime 注册时间

3.2.2 book 表

字段 类型 约束 说明
book_id int PK, AUTO_INCREMENT 书籍ID
seller_id int 卖家ID(逻辑外键→user.userid)
title varchar(255) 书名
author varchar(255) 作者
category varchar(255) 分类
original_price decimal(10,2) 原价
selling_price decimal(10,2) 售价
image_url varchar(255) 图片URL(预留)
created_at datetime 上架时间
stock int 库存数量

3.2.3 transaction_record 表

字段 类型 约束 说明
trans_id int PK, AUTO_INCREMENT 交易ID
book_id int 书籍ID(逻辑外键→book.book_id)
buyer_id int 买家ID(逻辑外键→user.userid)
transaction_num int 购买数量
transaction_price decimal(10,2) 交易总价
transaction_time datetime 交易时间

3.3 表关系说明

  • book.seller_iduser.userid:一个卖家可发布多本书(一对多)
  • transaction_record.book_idbook.book_id:一本书可被多次交易(一对多)
  • transaction_record.buyer_iduser.userid:一个买家可有多笔交易(一对多)

当前 DDL 中未显式声明外键约束,由应用层(事务)保证数据一致性。

3.4 SQL 脚本

建库脚本位于 sql/campus_book_trade.sql,包含完整 CREATE TABLE 和示例初始数据。字符集采用 utf8mb4


4. 服务端详细设计

4.1 分层架构

flowchart LR
    subgraph Controller [Controller 层]
        direction LR
        UC[UserController]
        BC[BookController]
        TC[TransactionController]
        SC[StatisticsController]
    end
    subgraph Service [Service 层]
        direction LR
        US[UserService]
        BS[BookService]
        TS[TransactionService]
        SS[StatisticsService]
    end
    subgraph Mapper [Mapper 层]
        direction LR
        UM[UserMapper]
        BM[BookMapper]
        TM[TransactionRecordMapper]
        SM[StatisticsMapper]
    end
    DB[(MySQL)]

    Controller --> Service
    Service --> Mapper
    Mapper --> DB

各层职责:

4.2 RESTful API 接口定义

4.2.1 用户模块 — /api/user

层次 职责
Controller 请求映射、参数解析校验、调用 Service、封装 R 响应
Service 核心业务逻辑、事务控制、组合操作
Mapper 数据访问(继承 BaseMapper 提供 CRUD,自定义 SQL 提供统计)
Config CORS 跨域配置、MyBatis-Plus 分页插件、全局异常处理
方法 路径 请求体/参数 响应 说明
POST /api/user/login LoginRequest R<User> 登录认证
POST /api/user/register RegisterRequest R<User> 注册新用户
GET /api/user ?page=&size= R<IPage<User>> 分页获取用户列表(Admin)
GET /api/user/{id} R<User> 获取单个用户
PUT /api/user/{id} User JSON R<User> 更新用户信息
DELETE /api/user/{id} R<Void> 删除用户(Admin)

LoginRequest:

{"username": "string", "password": "string"}

RegisterRequest:

{"username": "string", "password": "string", "nickname": "string", "email": "string", "phone": "string", "dept": "string"}

4.2.2 书籍模块 — /api/book

方法 路径 参数 响应 说明
GET /api/book keyword, category, page, size R<PageResult<Book>> 搜索书籍(分页)
GET /api/book/all R<List<Book>> 获取所有书籍
GET /api/book/{id} R<Book> 获取书籍详情
GET /api/book/my sellerId R<List<Book>> 卖家查看自己的书籍
GET /api/book/{id}/sales R<List<SalesRecordDTO>> 查看书籍销售记录(卖家)
GET /api/book/pending R<List<Book>> 查看待审核书籍列表(Admin)
POST /api/book Book JSON R<Book> 创建/上架书籍(默认 status=0)
PUT /api/book/{id} Book JSON R<Book> 更新书籍
PUT /api/book/{id}/audit ?status= R<Book> 审核书籍(Admin: 0待审/1通过/2拒绝)
DELETE /api/book/{id} R<Void> 删除书籍

4.2.3 交易模块 — /api/transaction

方法 路径 请求体/参数 响应 说明
POST /api/transaction {bookId, buyerId, quantity} R<TransactionRecord> 购买
GET /api/transaction ?page=&size= R<IPage<TransactionRecord>> 分页查询所有交易
GET /api/transaction/{id} R<TransactionRecord> 交易详情
GET /api/transaction/user/{userId} R<List<TransactionRecord>> 用户交易记录
PUT /api/transaction/{id} TransactionRecord JSON R<TransactionRecord> 更新交易(预留)
DELETE /api/transaction/{id} R<Void> 删除交易

4.2.4 统计模块 — /api/statistics

方法 路径 响应 说明
GET /api/statistics/summary R<StatisticsSummary> 交易总金额和总笔数
GET /api/statistics/top-departments R<List<DeptSalesDTO>> 销售额前三的院系
GET /api/statistics/top-books R<List<HotBookDTO>> 销量前五的书籍
GET /api/statistics/discount-rates R<List<DeptDiscountDTO>> 各院系平均折扣率

4.2.5 AI 分析模块 — /api/ai

方法 路径 响应 说明
GET /api/ai/analysis R<String> 触发服务端固定分析流程,返回文本分析报告

4.3 统一响应格式 R<T>

所有 API 返回统一结构:

{
  "code": 200,
  "message": "success",
  "data": { ... }
}
code 含义
200 成功
400 业务错误(参数错误、库存不足等)
500 服务器内部错误

4.4 分页设计

sequenceDiagram
    participant Client as JavaFX 客户端
    participant Server as Spring Boot 服务端
    participant DB as MySQL

    Client->>Server: GET /api/book?keyword=&category=&page=1&size=20
    Server->>DB: MyBatis-Plus Page + LambdaQueryWrapper
    DB-->>Server: 分页数据 + 总数
    Server->>Server: 转换为 PageResult<Book>
    Server-->>Client: R<PageResult<Book>>
    Client->>Client: FXCollections.observableArrayList(records) → TableView

服务端使用 MyBatis-Plus IPage 分页,通过 PageResult<T> DTO 传输给客户端。MyBatis-Plus PaginationInnerInterceptor 自动拦截并改写分页 SQL。

4.5 关键配置

WebConfig.java — CORS

允许跨域访问(开发环境 JavaFX 直连服务端,无代理层)。

MyBatisPlusConfig.java

  • 启用分页插件 PaginationInnerInterceptor(DbType.MYSQL)
  • 开启 map-underscore-to-camel-case: true
  • 主键生成策略:id-type: auto(数据库自增)

GlobalExceptionHandler

异常 响应
DuplicateKeyException R.error("用户名已存在")
IllegalArgumentException R.error(异常消息)
Exception(兜底) R.error("服务器内部错误: " + e.getMessage())

4.6 核心业务逻辑

4.6.1 注册流程

sequenceDiagram
    participant Client as JavaFX 客户端
    participant Server as 服务端
    participant DB as MySQL

    Client->>Server: POST /api/user/register (RegisterRequest)
    Server->>Server: 创建 User 对象
    Note over Server: 设置默认值: <br/>credit=100, role=0, <br/>isSeller=0, createdAt=now
    Server->>DB: INSERT INTO user
    DB-->>Server: User (含自增ID)
    Server-->>Client: R.ok(user)

4.6.2 登录流程

sequenceDiagram
    participant Client as JavaFX 客户端
    participant Server as 服务端
    participant DB as MySQL

    Client->>Server: POST /api/user/login (LoginRequest)
    Server->>DB: SELECT * FROM user WHERE username=? AND password=?
    DB-->>Server: User / null
    alt user == null
        Server-->>Client: R.error("Invalid username or password")
    else
        Server-->>Client: R.ok(user)
        Client->>Client: LoginUserHolder.set(user)
        Client->>Client: App.showMainScene()
    end

4.6.3 购买流程(事务)

sequenceDiagram
    participant Client as JavaFX 客户端
    participant Server as 服务端
    participant DB as MySQL

    Client->>Server: POST /api/transaction {bookId, buyerId, quantity}
    Server->>Server: @Transactional 开启事务
    Server->>DB: SELECT * FROM book WHERE book_id=?
    DB-->>Server: {stock=N, sellingPrice=P}
    alt stock < quantity
        Server-->>Client: R.error("Insufficient stock")
        Server->>Server: 回滚事务
    else
        Server->>DB: UPDATE book SET stock = stock - quantity
        Server->>DB: INSERT INTO transaction_record (bookId, buyerId, transactionNum, transactionPrice, transactionTime)
        Server->>Server: 提交事务
        Server-->>Client: R.ok(transactionRecord)
    end

事务边界TransactionServiceImpl.purchase()):

  1. 查询书籍,校验库存
  2. 扣减库存 book.stock -= quantity
  3. 创建交易记录(价格 = sellingPrice × quantity
  4. 任一失败则 @Transactional 回滚

4.6.4 搜索流程

1. 客户端组装参数:keyword + category + page + size
2. GET /api/book?keyword=&category=&page=&size=
3. 服务端构建动态查询条件:
   - keyword → LIKE title OR LIKE author
   - category → EQ category
   - 按 created_at DESC 排序
4. MyBatis-Plus 分页查询 + 返回 PageResult<Book>

4.7 统计实现

统计模块全部使用自定义 SQL 在 StatisticsMapper 中通过 @Select 注解实现:

统计项 SQL 概要
summary SELECT COALESCE(SUM(transaction_price),0), COUNT(*) FROM transaction_record
top-departments JOIN transaction_record + book + user,按 user.dept 分组,销售额前3
top-books JOIN transaction_record + book,按 book 分组,销量前5
discount-rates AVG((original_price - unit_price) / original_price * 100),按 dept 分组

5. 客户端详细设计

5.1 界面导航

flowchart LR
    A[login.fxml] -->|登录成功| C[main.fxml]
    A -->|点击注册链接| B[register.fxml]
    B -->|注册成功| A
    B -->|返回登录| A
    C -->|退出登录| A

App.java 作为 JavaFX Application 入口,通过静态方法实现场景切换。Client.java 为启动入口(Application.launch(App.class, args))。

5.2 FXML 视图结构

login.fxml

VBox (400×350)
├── Label "校园书市"(标题,22px bold)
├── TextField #usernameField(用户名输入)
├── PasswordField #passwordField(密码输入)
├── Button "登录" → #handleLogin(btn-primary)
└── Hyperlink "没有账号?立即注册" → #handleRegister

register.fxml

VBox (400×500)
├── Label "创建账号"(标题,20px bold)
├── TextField #usernameField(用户名 *)
├── PasswordField #passwordField(密码 *)
├── TextField #nicknameField(昵称)
├── TextField #emailField(邮箱)
├── TextField #phoneField(电话)
├── TextField #deptField(院系)
├── Button "注册" → #handleRegister(btn-primary)
└── Hyperlink "已有账号?去登录" → #handleBack

main.fxml

BorderPane (1000×700)
├── Top: HBox 顶部标题栏
│   ├── Label #welcomeLabel "欢迎,xxx"
│   └── Button "退出登录" → #handleLogout(btn-danger)
│
└── Center: TabPane(6个Tab)
    │
    ├── Tab "浏览书籍"(买家/卖家共有的 browse tab)
    │   ├── HBox 搜索栏: TextField #searchField + ComboBox #categoryCombo + Button "搜索"
    │   └── SplitPane (0.6)
    │       ├── TableView #bookTable [编号,书名,作者,分类,价格,库存]
    │       └── VBox: 详情 Label 组 + TextField #quantityField + Button "购买"
    │
    ├── Tab "我的交易"(买家/卖家)
    │   ├── Button "刷新" → #handleLoadTransactions
    │   └── TableView #transTable [交易编号,书籍编号,数量,总价,时间]
    │
    ├── Tab "个人信息"(买家/卖家)
    │   └── VBox: #profileUsername(只读), #profileNickname, #profileEmail, #profilePhone, #profileDept, #profileCredit(只读) + Button "保存修改"
    │
    ├── Tab "卖家中心"(isSeller=1 启用)
    │   ├── Label "我的书籍"
    │   ├── Button "刷新" → #handleLoadMyBooks
    │   ├── TableView #myBookTable [编号,书名,价格,库存]
    │   ├── Button "删除选中" → #handleDeleteBook
    │   └── TitledPane "添加新书"
    │       └── GridPane: 书名/作者/分类/原价/售价/库存 输入 + Button "添加书籍"
    │
    ├── Tab "数据统计"(所有角色可见)
    │   └── ScrollPane > VBox
    │       ├── Button "刷新数据" → #handleLoadStatistics
    │       ├── TitledPane "总览": statTotalAmount + statTotalCount
    │       ├── TitledPane "热门院系(销量前三)": TableView #deptTable
    │       ├── TitledPane "热门书籍(前五)": TableView #hotBookTable
    │       └── TitledPane "各院系平均折扣率": TableView #discountTable
    │
    └── Tab "管理"(role=1 启用)
        ├── Label "用户管理"
        ├── Button "刷新用户" → #handleLoadAllUsers
        ├── TableView #userTable [编号,用户名,昵称,院系,信用分,角色,卖家]
        ├── Button "删除选中用户" → #handleDeleteUser
        ├── Label "全部交易"
        ├── Button "刷新交易" → #handleLoadAllTransactions
        ├── TableView #allTransTable [交易编号,书籍编号,买家编号,数量,价格,时间]
        └── Button "删除选中交易" → #handleDeleteTransaction

5.3 控制器设计

LoginController

字段/方法 类型 说明
usernameField TextField 用户名输入框
passwordField PasswordField 密码输入框
userService UserService HTTP 服务层
handleLogin() void 校验非空 → 调用登录 API → 存入会话 → 跳转主界面
handleRegister() void 跳转到注册界面

RegisterController

字段/方法 类型 说明
usernameField ~ deptField TextField / PasswordField 注册表单字段
userService UserService HTTP 服务层
handleRegister() void 校验必填 → 调用注册 API → 提示 → 跳回登录
handleBack() void 返回登录界面

MainController

Tab 核心方法 说明
浏览书籍 handleSearch() 搜索 + 刷新列表
handlePurchase() 校验 → 购买 → 刷新
initialize() 绑定表格列、设置角色权限、加载数据
我的交易 handleLoadTransactions() 加载当前用户交易记录
个人信息 handleUpdateProfile() 保存修改 + 更新会话
卖家中心 handleLoadMyBooks() 加载卖家自己的书籍
handleAddBook() 上架新书
handleDeleteBook() 删除选中书籍
数据统计 handleLoadStatistics() 加载4个统计指标
管理 handleLoadAllUsers() 加载所有用户
handleDeleteUser() 删除选中用户
handleLoadAllTransactions() 加载所有交易
handleDeleteTransaction() 删除选中交易

会话初始化initialize() 方法):

  1. LoginUserHolder 获取当前用户
  2. 设置欢迎语和个人信息回显
  3. 根据 isSellerrole 控制 sellerTab 和 adminTab 的启用状态

5.4 HTTP 服务层设计

HttpClientUtil

单例模式的 HttpClient + Jackson ObjectMapper,提供泛型方法:

方法 说明
get(path, Class<T>) GET 请求,简单类型反序列化
get(path, TypeReference) GET 请求,泛型类型反序列化
post(path, body, Class<T>) POST 请求
put(path, body, Class<T>) PUT 请求
delete(path, Class<T>) DELETE 请求

基 URL = http://localhost:8080,错误时返回 code=500。

客户端 Service 封装

Service 封装方法 对应 API
UserService login, register, getById, update, delete /api/user/*
BookService search, getById, getMyBooks, listAll, create, update, delete /api/book/*
TransactionService purchase, getById, getByUser, update, delete /api/transaction/*
StatisticsService getSummary, getTopDepartments, getTopBooks, getDiscountRates /api/statistics/*

5.5 会话管理

LoginUserHolder:基于内存的静态会话管理。

LoginUserHolder.set(user);      // 登录成功后存入
LoginUserHolder.get();          // 获取当前用户
LoginUserHolder.isSeller();     // 判断是否为卖家
LoginUserHolder.clear();        // 退出时清除

当前无 Token/JWT 机制,userId 通过请求参数/体明文传输。

5.6 BootstrapFX 样式整合

引入 org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0,在场景加载时添加 bootstrapfx.css。额外定制 css/style.css:全局字体、按钮手型光标、表格边框圆角。


6. 用户角色与权限设计

6.1 角色模型

用户角色由两个字段组合定义:

role isSeller 角色
0 0 普通学生(买家)
0 1 卖家(同时具备买家权限)
1 0/1 管理员

UserRole.java 枚举仅定义 STUDENT(0)ADMIN(1),卖家用 isSeller 标志区分。

6.2 权限矩阵

功能模块 功能点 买家 卖家 管理员
账号 注册
账号 登录
书籍 浏览/搜索
书籍 查看详情
书籍 购买
书籍 上架
书籍 编辑
书籍 下架/删除
书籍 查看自己的发布
书籍 审核(待实现)
订单 查看自己的订单
订单 查看全部订单
订单 删除订单
用户 CRUD
用户 重置密码
统计 查看统计数据
AI 分析报告(待实现)

6.3 客户端权限控制

MainController.initialize() 中:

if (user.getIsSeller() != 1) sellerTab.setDisable(true);  // 非卖家禁用卖家中心
if (user.getRole() != 1)     adminTab.setDisable(true);    // 非管理员禁用管理Tab

Tab 被禁用后用户无法点击,但 FXML 元素仍保留。

6.4 服务端校验(待完善)

当前服务端未实现权限拦截,所有 API 均可被任意客户端调用。生产环境建议引入 Spring Security 或自定义拦截器。


7. 数据流设计

7.1 登录注册流程

sequenceDiagram
    actor User as 用户
    participant Login as 登录界面
    participant Reg as 注册界面
    participant Main as 主界面
    participant Svc as 客户端Service
    participant API as 服务端API
    participant DB as 数据库

    Note over User,DB: === 登录流程 ===
    User->>Login: 输入用户名+密码
    Login->>Svc: userService.login(username, password)
    Svc->>API: POST /api/user/login
    API->>DB: SELECT * FROM user WHERE username=? AND password=?
    DB-->>API: User
    API-->>Svc: R.ok(user)
    Svc-->>Login: R<User>
    Login->>Login: LoginUserHolder.set(user)
    Login->>Main: App.showMainScene()

    Note over User,DB: === 注册流程 ===
    User->>Reg: 填写表单
    Reg->>Svc: userService.register(RegisterRequest)
    Svc->>API: POST /api/user/register
    API->>API: 设置默认值 (credit=100,role=0,isSeller=0)
    API->>DB: INSERT INTO user
    DB-->>API: User
    API-->>Svc: R.ok(user)
    Svc-->>Reg: 注册成功
    Reg->>Login: App.showLoginScene()

7.2 购买交易流程

sequenceDiagram
    actor Buyer as 买家
    participant Browse as 浏览Tab
    participant Svc as TransactionService
    participant API as 服务端
    participant DB as 数据库

    Buyer->>Browse: 选择书籍 → 输入数量 → 点击购买
    Browse->>Svc: purchase(bookId, buyerId, quantity)
    Svc->>API: POST /api/transaction
    API->>API: @Transactional 开启
    API->>DB: SELECT stock FROM book WHERE book_id=?
    DB-->>API: stock=N
    alt N < quantity
        API-->>Svc: R.error("Insufficient stock")
        Svc-->>Browse: Alert 警告
    else
        API->>DB: UPDATE book SET stock = stock - quantity
        API->>DB: INSERT INTO transaction_record
        API->>API: 提交事务
        API-->>Svc: R.ok(record)
        Svc-->>Browse: Alert 成功 + 刷新列表
    end

7.3 搜索书籍流程

sequenceDiagram
    actor User as 用户
    participant Browse as 浏览Tab
    participant Svc as BookService
    participant API as 服务端
    participant DB as 数据库

    User->>Browse: 输入关键字 / 选择分类
    User->>Browse: 点击"搜索"
    Browse->>Svc: search(keyword, category, page, size)
    Svc->>API: GET /api/book?keyword=&category=
    API->>API: LambdaQueryWrapper 动态条件
    API->>DB: SELECT ... WHERE title LIKE ? OR author LIKE ? AND category=? ORDER BY created_at DESC
    DB-->>API: 分页结果
    API-->>Svc: R.ok(PageResult<Book>)
    Svc-->>Browse: 数据列表
    Browse->>Browse: bookTable.setItems(...)

8. 已实现功能清单

以下列出所有基于 demand.md 需求已实现的功能及其对应代码位置:

8.1 卖家-编辑书籍 — ✅ 已实现

组件 说明
FXML 卖家中心 Tab 新增"编辑选中"按钮 (main.fxml:92)
Controller handleEditBook() — 选中书籍回填表单,切换为编辑模式 (MainController.java)
复用 handleAddBook() — 检测 editingBook 字段,区分新增/更新逻辑
重置 clearBookForm() — 编辑完成后重置表单状态

8.2 卖家-查看销售记录 — ✅ 已实现

组件 说明
API GET /api/book/{id}/sales — 返回 List<SalesRecordDTO> (BookController.java:56)
DTO SalesRecordDTO — bookId, title, buyerId, buyerName, quantity, totalPrice, transactionTime
Mapper BookMapper.selectSalesByBookId() — JOIN transaction_record + book + user
客户端 handleViewSales() — 弹出 Dialog 展示销售记录表格 (MainController.java)

8.3 管理员-用户管理完善 — ✅ 已实现

组件 说明
编辑用户 handleEditUser() — 选中用户回填表单,支持修改 (MainController.java)
添加用户 handleSaveUser() — 创建用户(调用 Register API)
重置密码 handleResetPassword() — 密码重置为 123456
FXML 用户表单 TitledPane (main.fxml)

8.4 管理员-商品管理 — ✅ 已实现

组件 说明
TableView adminBookTable — 显示所有书籍(编号、书名、作者、分类、售价、库存、卖家ID)
刷新 handleLoadAllBooksAdmin() — 调用 GET /api/book/all
删除 handleDeleteBookAdmin() — 删除选中书籍

8.5 管理员-审核书籍 — ✅ 已实现

组件 说明
Schema book 表增加 status 字段: 0=待审核, 1=已上架, 2=已下架
Entity Book.java 增加 @TableField("status") private Integer status;
API GET /api/book/pending — 获取待审核书籍
API PUT /api/book/{id}/audit?status= — 审核通过/拒绝
搜索过滤 searchBooks() 仅返回 status=1 的书籍
新书上架 create() 默认 status=0(待审核)
客户端 pendingBookTable + "通过"/"拒绝" 按钮

8.6 AI 分析 — ✅ 已实现

组件 说明
Service AiServiceImpl.generateAnalysis() — 汇总统计数据 → 格式化分析报告
Controller GET /api/ai/analysis — 返回 R<String> 文本报告
客户端 AiService — HTTP 调用封装
UI 管理 Tab 增加"AI 分析"区域: "生成分析报告"按钮 + TextArea 展示区
后续 LLM 接入方案(API Key、模型选择、固定提示词模板)待补充

8.7 信用分修改 — ✅ 已实现

组件 说明
UI 管理 Tab 用户管理区增加"修改信用分"按钮
交互 TextInputDialog 输入新分值 → 调用 PUT /api/user/{id} 更新

附录 A:配置文件

application.yml

server:
  port: 8080
  servlet:
    context-path: /api

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/campus_book_trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      id-type: auto

附录 B:构建命令

# 全量构建(需 JDK 23)
JAVA_HOME=/path/to/corretto-23 mvn clean install

# 运行服务端
mvn spring-boot:run -pl server

# 运行客户端
mvn javafx:run -pl client