# 实验室预约与资源管理平台 面向学校实验室的场景化预约系统:统一管理**实验室**、**设备**与**实验课程**,支持 **单次预约 / 多次预约 / 课程绑定预约**三种业务模式,内置可测试的**时间冲突检测算法**、 **资源状态机**与**基于角色的访问控制(RBAC)**,并提供 Vue3 前端与日历视图。 ## 技术栈 | 层次 | 技术 | | --- | --- | | 后端 | Spring Boot 4.1.1、MyBatis-Plus 3.5.17(XML 自定义 SQL + QueryWrapper)、JWT(jjwt)、BCrypt | | 数据库 | H2(默认,免安装演示)/ MariaDB(`mariadb` Profile,schema 脚本已提供) | | 测试 | JUnit 5、Mockito、MockMvc、AssertJ、JaCoCo(覆盖率门禁) | | 前端 | Vue 3 + TypeScript + Vite + Element Plus + Pinia + Vue Router + Axios,包管理 pnpm | ## 快速开始 ### 1. 启动后端(默认 H2 文件库,无需安装数据库) ```bash ./mvnw spring-boot:run # 或者:mvn spring-boot:run ``` - 服务地址:http://localhost:8080 - H2 控制台:http://localhost:8080/h2-console (JDBC URL:`jdbc:h2:file:./data/labdb`,用户 `sa`,密码留空) - 首次启动会自动建表并写入演示数据(`src/main/resources/db/schema.sql`、`data.sql`) 使用 MariaDB/MySQL 时: ```bash # 先创建数据库:CREATE DATABASE lab DEFAULT CHARSET utf8mb4; DB_HOST=localhost DB_USER=root DB_PASSWORD=yourpass \ mvn spring-boot:run -Dspring-boot.run.profiles=mariadb ``` ### 2. 启动前端 ```bash cd frontend pnpm install pnpm dev # http://localhost:5173,已配置 /api 代理到 8080 pnpm build # 生产构建(vue-tsc 类型检查 + vite build) ``` ### 3. Docker 一键部署(推荐用于演示/验收) ```bash cp .env.example .env # 可选:按需修改端口/密码/JWT 密钥 docker compose up -d --build # 构建并启动 mariadb + backend + frontend # 前端:http://localhost (Nginx 托管静态资源并反代 /api) # 后端:http://localhost:8080 docker compose ps # 查看健康状态 docker compose down # 停止容器,保留数据库数据卷 docker compose down -v # 停止并清空数据库(下次启动重建演示数据) ``` - 编排:`mariadb:11.4`(数据持久化到命名卷)+ 后端多阶段构建镜像(JRE 21,非 root 运行)+ Nginx 前端镜像 - 三个服务均配置健康检查,后端与前端会等待上游就绪后再启动 - 可用环境变量(见 `.env.example`):`WEB_PORT`(默认 80)、`BACKEND_PORT`(默认 8080)、 `DB_NAME` / `DB_USER` / `DB_PASSWORD` / `DB_ROOT_PASSWORD`、`LAB_JWT_SECRET`、`JAVA_OPTS` ### 4. 演示账号 | 账号 | 密码 | 角色 | 典型能力 | | --- | --- | --- | --- | | `admin` | `admin123` | 管理员 | 全部资源与用户管理、审批、状态流转 | | `teacher1` | `123456` | 教师 | 课程管理、发起课程预约、审批 | | `student1` | `123456` | 学生 | 发起预约、查看/取消自己的预约 | ## 功能地图 - **认证与权限**:JWT 登录、`@RequireRole` 角色注解、拦截器统一鉴权,前端路由/菜单按角色过滤 - **实验室管理**:增删改查、状态机流转(空闲 / 已预约 / 使用中 / 维护中)、可用实验室推荐 - **设备管理**:增删改查、归属实验室校验、状态机流转 - **实验课程**:教师排课(起止日期 + 星期 + 时间段)、一键生成整期课程绑定预约 - **预约中心**: - 单次预约(datetimerange) - 多次预约(按周重复,自动拆分为系列场次) - 课程绑定预约(按课程排课规则批量生成) - 冲突预检、审批(通过/驳回)、取消(可整系列)、开始/完成使用 - **日历视图**:按实验室 + 月份展示预约分布,点击查看详情 - **自动任务**:每 10 分钟自动完结已过期的预约并同步资源状态 ## 核心设计 ### 冲突检测 分层设计:数据库层用索引粗筛(XML 自定义 SQL,可选 `FOR UPDATE` 行锁), 应用层 `ConflictDetector` 做精确判断。 - 区间语义:左闭右开 `[start, end)`,**首尾相接不算冲突** - 规则校验:时间区间合法性、过去时间、提前预约上限、单次时长、开放时间/跨天、 资源维护状态、容量、课程绑定关系、请求内部重叠 - 冲突分类:`LAB_CONFLICT` / `DEVICE_CONFLICT` / `USER_CONFLICT` 等,返回多冲突明细 - 并发控制:同事务内 `SELECT ... FOR UPDATE` + 冲突复检,避免并发双写 详细说明见 [docs/design.md](docs/design.md)。 ### 状态机 - 实验室/设备:`IDLE → RESERVED → IN_USE → IDLE`,`IDLE ↔ MAINTENANCE`,非法迁移抛 `3003` - 预约:`PENDING → APPROVED → IN_USE → COMPLETED`,`PENDING → REJECTED`, `PENDING/APPROVED → CANCELLED`,终态不可再流转 ## 测试与覆盖率 ```bash mvn verify # 运行全部测试并执行 JaCoCo 覆盖率门禁 ``` - 测试规模:**219** 个用例(19 个测试类)(冲突算法边界、状态机、服务层 Mockito、Mapper/接口集成) - 覆盖率(核心包,排除实体/DTO/VO/配置):**行 98.1%,分支 92.8%** - 门禁:行 ≥ 90%,分支 ≥ 85%,不达标 `mvn verify` 直接失败 - 报告:`target/site/jacoco/index.html` ## 目录结构 ``` ├── src/main/java/com/example/lab │ ├── common/ # 统一响应、异常、注解、上下文、JWT │ ├── config/ # MyBatis-Plus、Web、安全、业务参数 │ ├── controller/ # REST 接口 │ ├── domain/ │ │ ├── conflict/ # 时间复杂度可控的冲突检测与重复规则展开 │ │ ├── entity/ # 实体 │ │ ├── enums/ # 角色/状态/类型枚举 │ │ └── statemachine/# 通用枚举状态机 │ ├── dto/ vo/ # 请求与视图对象 │ ├── mapper/ # MyBatis-Plus Mapper │ ├── security/ # 认证授权拦截器 │ └── service/ # 业务服务 ├── src/main/resources │ ├── db/ # H2 / MariaDB 建表与演示数据 │ └── mapper/ # XML 自定义 SQL ├── src/test/java/... # 单元测试 + 集成测试 ├── frontend/ # Vue3 + Element Plus 前端 │ ├── Dockerfile # pnpm 构建 + Nginx 托管(含 /api 反向代理) │ └── nginx.conf ├── Dockerfile # 后端多阶段构建镜像 ├── docker-compose.yml # mariadb + backend + frontend 一键编排 ├── .env.example # 部署环境变量示例(复制为 .env 使用) └── docs/design.md # 需求分析与详细设计 ``` ## 主要接口 | 方法 | 路径 | 说明 | 权限 | | --- | --- | --- | --- | | POST | `/api/auth/login` | 登录 | 公开 | | GET | `/api/auth/me` | 当前用户 | 登录 | | GET | `/api/labs` / `/api/labs/available` | 实验室列表 / 可用推荐 | 登录 | | POST/PUT/DELETE | `/api/labs/**` | 实验室管理 | 管理员 | | GET | `/api/devices` / `/api/devices/by-lab/{id}` | 设备列表 | 登录 | | POST/PUT/DELETE | `/api/devices/**` | 设备管理 | 管理员 | | GET | `/api/courses` | 课程列表 | 登录 | | POST/PUT/DELETE | `/api/courses/**` | 课程管理 | 管理员/教师 | | POST | `/api/reservations` | 单次预约 | 登录 | | POST | `/api/reservations/recurring` | 多次预约 | 登录 | | POST | `/api/reservations/course` | 课程绑定预约 | 管理员/授课教师 | | POST | `/api/reservations/check` | 冲突预检 | 登录 | | GET | `/api/reservations` / `/{id}` / `/calendar` | 列表 / 详情 / 日历 | 登录(学生仅本人) | | POST | `/api/reservations/{id}/approve\|reject` | 审批 | 管理员/教师 | | POST | `/api/reservations/{id}/cancel\|start\|complete` | 取消/开始/完成 | 本人或管理员 | | GET/POST/PUT/DELETE | `/api/users/**` | 用户管理 | 管理员 | 统一响应格式: ```json { "code": 0, "message": "success", "data": {} } ``` 业务错误码:`0` 成功、`1xxx` 用户与认证、`2xxx` 资源管理、`3xxx` 预约与冲突、`4xx/5xx` 通用错误。