Java全栈项目-校园闲置教室查询系统
项目简介
本项目是一个基于Spring Boot + Vue.js的校园闲置教室查询系统,旨在帮助师生快速查找和使用空闲教室资源。系统支持教室使用情况的实时查询、预约管理以及数据统计分析等功能。
技术栈
后端
- Spring Boot 2.7.x
- Spring Security
- MyBatis-Plus
- MySQL 8.0
- Redis
- JWT
前端
- Vue 3
- Element Plus
- Axios
- Vite
- Pinia
核心功能
-
用户管理
- 学生/教师登录注册
- 角色权限控制
- 个人信息管理
-
教室管理
- 教室信息维护
- 教室状态实时更新
- 教室使用统计
-
预约管理
- 在线预约
- 预约审核
- 预约记录查询
-
系统管理
- 用户管理
- 权限配置
- 系统日志
数据库设计
主要表结构
-- 用户表
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`role` varchar(20) NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- 教室表
CREATE TABLE `classroom` (
`id` bigint NOT NULL AUTO_INCREMENT,
`room_number` varchar(20) NOT NULL,
`building` varchar(50) NOT NULL,
`capacity` int NOT NULL,
`status` tinyint DEFAULT '0',
PRIMARY KEY (`id`)
);
-- 预约记录表
CREATE TABLE `reservation` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`classroom_id` bigint NOT NULL,
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
`status` tinyint DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
核心代码示例
后端接口
@RestController
@RequestMapping("/api/classroom")
public class ClassroomController {
@Autowired
private ClassroomService classroomService;
@GetMapping("/available")
public Result getAvailableClassrooms(
@RequestParam LocalDateTime startTime,
@RequestParam LocalDateTime endTime) {
List<ClassroomVO> classrooms = classroomService.findAvailable(startTime, endTime);
return Result.success(classrooms);
}
}
前端组件
<template>
<div class="classroom-list">
<el-table :data="classrooms" border>
<el-table-column prop="roomNumber" label="教室编号" />
<el-table-column prop="building" label="教学楼" />
<el-table-column prop="capacity" label="容量" />
<el-table-column label="操作">
<template #default="scope">
<el-button @click="handleReserve(scope.row)">预约</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
项目亮点
- 采用前后端分离架构,提高开发效率和系统可维护性
- 使用Redis缓存热点数据,提升查询性能
- 实现基于JWT的无状态认证
- 引入消息队列处理预约请求,提高系统并发能力
- 支持教室使用情况的可视化展示
项目部署
-
环境要求
- JDK 1.8+
- Node.js 16+
- MySQL 8.0
- Redis 6.0+
-
部署步骤
- 导入数据库脚本
- 配置后端application.yml
- 前端打包构建
- 使用Nginx部署前端资源
- 启动后端服务
总结与展望
本项目实现了校园闲置教室的智能化管理,提高了教室资源利用率。未来计划添加以下功能:
- 引入人脸识别实现无感进出
- 集成教务系统数据
- 开发移动端应用
- 添加教室环境监控功能
校园闲置教室查询系统功能详解
一、用户管理模块
1. 用户表设计
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`real_name` varchar(20) NOT NULL COMMENT '真实姓名',
`role` varchar(20) NOT NULL COMMENT '角色:STUDENT/TEACHER/ADMIN',
`student_id` varchar(20) DEFAULT NULL COMMENT '学号',
`department` varchar(50) DEFAULT NULL COMMENT '院系',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用 1-启用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
);
2. 登录注册功能
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserVO register(RegisterDTO registerDTO) {
// 验证用户名是否存在
if (userMapper.selectByUsername(registerDTO.getUsername()) != null) {
throw new BusinessException("用户名已存在");
}
// 密码加密
User user = new User();
BeanUtils.copyProperties(registerDTO, user);
user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
// 保存用户信息
userMapper.insert(user);
return convertToVO(user);
}
@Override
public String login(LoginDTO loginDTO) {
// 验证用户名密码
User user = userMapper.selectByUsername(loginDTO.getUsername());
if (user == null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
throw new BusinessException("用户名或密码错误");
}
// 生成JWT令牌
return JwtUtil.generateToken(user);
}
}
3. 权限控制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/teacher/**").hasAnyRole("ADMIN", "TEACHER")
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
4. 个人信息管理
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/info")
public Result getUserInfo() {
return Result.success(userService.getCurrentUserInfo());
}
@PutMapping("/info")
public Result updateUserInfo(@RequestBody UserUpdateDTO updateDTO) {
userService.updateUserInfo(updateDTO);
return Result.success();
}
@PutMapping("/password")
public Result updatePassword(@RequestBody PasswordUpdateDTO updateDTO) {
userService.updatePassword(updateDTO);
return Result.success();
}
}
二、教室管理模块
1. 教室表设计
CREATE TABLE `classroom` (
`id` bigint NOT NULL AUTO_INCREMENT,
`room_number` varchar(20) NOT NULL COMMENT '教室编号',
`building` varchar(50) NOT NULL COMMENT '教学楼',
`floor` int NOT NULL COMMENT '楼层',
`capacity` int NOT NULL COMMENT '容量',
`type` varchar(20) NOT NULL COMMENT '教室类型:普通/多媒体/实验室',
`facilities` varchar(255) DEFAULT NULL COMMENT '设施配置',
`status` tinyint DEFAULT '0' COMMENT '状态:0-空闲 1-占用 2-维护中',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_room_number` (`room_number`)
);
2. 教室信息维护
@RestController
@RequestMapping("/api/admin/classroom")
@PreAuthorize("hasRole('ADMIN')")
public class ClassroomManageController {
@Autowired
private ClassroomService classroomService;
@PostMapping
public Result addClassroom(@RequestBody ClassroomDTO classroomDTO) {
classroomService.addClassroom(classroomDTO);
return Result.success();
}
@PutMapping("/{id}")
public Result updateClassroom(@PathVariable Long id, @RequestBody ClassroomDTO classroomDTO) {
classroomService.updateClassroom(id, classroomDTO);
return Result.success();
}
@DeleteMapping("/{id}")
public Result deleteClassroom(@PathVariable Long id) {
classroomService.deleteClassroom(id);
return Result.success();
}
}
3. 教室状态实时更新
@Service
public class ClassroomStatusService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ClassroomMapper classroomMapper;
// 使用Redis缓存教室状态
public void updateStatus(Long classroomId, Integer status) {
String key = "classroom:status:" + classroomId;
redisTemplate.opsForValue().set(key, status);
// 异步更新数据库
CompletableFuture.runAsync(() -> {
classroomMapper.updateStatus(classroomId, status);
});
}
// 定时同步Redis和数据库的状态
@Scheduled(fixedRate = 300000) // 每5分钟执行一次
public void syncStatus() {
Set<String> keys = redisTemplate.keys("classroom:status:*");
for (String key : keys) {
Integer status = (Integer) redisTemplate.opsForValue().get(key);
Long classroomId = Long.valueOf(key.split(":")[2]);
classroomMapper.updateStatus(classroomId, status);
}
}
}
4. 教室使用统计
@Service
public class ClassroomStatisticsService {
@Autowired
private ReservationMapper reservationMapper;
// 获取教室使用率统计
public List<ClassroomUsageVO> getUsageStatistics(LocalDate startDate, LocalDate endDate) {
return reservationMapper.selectUsageStatistics(startDate, endDate);
}
// 获取热门教室排行
public List<ClassroomRankVO> getPopularClassrooms(Integer limit) {
return reservationMapper.selectPopularClassrooms(limit);
}
// 获取各时段教室使用情况
public Map<String, Integer> getTimeSlotStatistics(LocalDate date) {
return reservationMapper.selectTimeSlotStatistics(date);
}
}
5. 前端展示组件
<template>
<div class="classroom-dashboard">
<!-- 教室使用率图表 -->
<div class="usage-chart">
<el-card>
<template #header>教室使用率统计</template>
<v-chart :option="usageOption" />
</el-card>
</div>
<!-- 实时状态展示 -->
<div class="status-board">
<el-card>
<template #header>教室实时状态</template>
<el-table :data="classrooms" border>
<el-table-column prop="roomNumber" label="教室编号" />
<el-table-column prop="building" label="教学楼" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useClassroomStore } from '@/stores/classroom'
const classroomStore = useClassroomStore()
const classrooms = ref([])
// 状态监听(使用WebSocket)
const ws = new WebSocket('ws://localhost:8080/ws/classroom/status')
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
updateClassroomStatus(data)
}
// 初始化数据
onMounted(async () => {
await classroomStore.fetchClassrooms()
classrooms.value = classroomStore.classrooms
})
</script>
以上代码展示了用户管理和教室管理模块的核心实现。主要特点包括:
-
用户管理:
- 完善的用户信息存储
- 安全的密码加密存储
- 基于JWT的身份认证
- 细粒度的权限控制
-
教室管理:
- 详细的教室信息记录
- 使用Redis实现状态实时更新
- 异步处理提高性能
- 多维度的统计分析
- 实时状态展示和监控
这些功能的实现充分考虑了系统的安全性、实时性和可扩展性。
校园闲置教室查询系统功能详解(续)
三、预约管理模块
1. 预约表设计
CREATE TABLE `reservation` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '预约用户ID',
`classroom_id` bigint NOT NULL COMMENT '教室ID',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`purpose` varchar(200) NOT NULL COMMENT '使用目的',
`attendees` int NOT NULL COMMENT '使用人数',
`status` tinyint DEFAULT '0' COMMENT '状态:0-待审核 1-已通过 2-已拒绝 3-已取消',
`approve_user_id` bigint DEFAULT NULL COMMENT '审核人ID',
`approve_time` datetime DEFAULT NULL COMMENT '审核时间',
`approve_remark` varchar(200) DEFAULT NULL COMMENT '审核备注',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_classroom_id` (`classroom_id`)
);
2. 在线预约功能
@Service
public class ReservationServiceImpl implements ReservationService {
@Autowired
private ReservationMapper reservationMapper;
@Autowired
private ClassroomService classroomService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
@Transactional
public void createReservation(ReservationDTO dto) {
// 检查时间冲突
if (checkTimeConflict(dto.getClassroomId(), dto.getStartTime(), dto.getEndTime())) {
throw new BusinessException("该时间段已被预约");
}
// 创建预约记录
Reservation reservation = new Reservation();
BeanUtils.copyProperties(dto, reservation);
reservation.setUserId(SecurityUtils.getCurrentUserId());
// 保存预约记录
reservationMapper.insert(reservation);
// 发送预约消息到消息队列
rabbitTemplate.convertAndSend("reservation.exchange", "reservation.new", reservation);
}
private boolean checkTimeConflict(Long classroomId, LocalDateTime startTime, LocalDateTime endTime) {
return reservationMapper.countTimeConflict(classroomId, startTime, endTime) > 0;
}
}
3. 预约审核功能
@RestController
@RequestMapping("/api/reservation/approve")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public class ReservationApproveController {
@Autowired
private ReservationService reservationService;
@PutMapping("/{id}/approve")
public Result approve(@PathVariable Long id, @RequestBody ApproveDTO approveDTO) {
reservationService.approveReservation(id, approveDTO);
return Result.success();
}
@PutMapping("/{id}/reject")
public Result reject(@PathVariable Long id, @RequestBody ApproveDTO approveDTO) {
reservationService.rejectReservation(id, approveDTO);
return Result.success();
}
}
// 消息监听器
@Component
@RabbitListener(queues = "reservation.queue")
public class ReservationListener {
@Autowired
private NotificationService notificationService;
@RabbitHandler
public void handleReservation(Reservation reservation) {
// 发送预约通知给管理员
notificationService.sendToAdmin(
NotificationType.NEW_RESERVATION,
String.format("新预约申请:%s 教室,时间:%s",
reservation.getClassroomId(),
reservation.getStartTime())
);
}
}
4. 预约记录查询
@RestController
@RequestMapping("/api/reservation")
public class ReservationController {
@Autowired
private ReservationService reservationService;
@GetMapping("/my")
public Result getMyReservations(ReservationQueryDTO queryDTO) {
PageResult<ReservationVO> result = reservationService.getMyReservations(queryDTO);
return Result.success(result);
}
@GetMapping("/statistics")
@PreAuthorize("hasRole('ADMIN')")
public Result getStatistics(StatisticsQueryDTO queryDTO) {
ReservationStatisticsVO statistics = reservationService.getStatistics(queryDTO);
return Result.success(statistics);
}
}
四、系统管理模块
1. 系统日志表设计
CREATE TABLE `sys_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT '操作用户ID',
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`operation` varchar(50) NOT NULL COMMENT '操作类型',
`method` varchar(200) NOT NULL COMMENT '请求方法',
`params` text COMMENT '请求参数',
`time` bigint NOT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_create_time` (`create_time`)
);
2. 系统日志记录
@Aspect
@Component
@Slf4j
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.example.annotation.SysLog)")
public void logPointCut() {}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
// 执行方法
Object result = point.proceed();
// 记录日志
long time = System.currentTimeMillis() - beginTime;
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
// 设置日志信息
sysLogService.save(sysLog);
}
}
3. 权限管理
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
@Override
@Cacheable(value = "permissions", key = "#userId")
public Set<String> getUserPermissions(Long userId) {
return permissionMapper.selectPermissionsByUserId(userId);
}
@Override
@CacheEvict(value = "permissions", allEntries = true)
public void updateRolePermissions(Long roleId, List<Long> permissionIds) {
// 更新角色权限关系
roleMapper.deleteRolePermissions(roleId);
if (!CollectionUtils.isEmpty(permissionIds)) {
roleMapper.insertRolePermissions(roleId, permissionIds);
}
}
}
4. 前端管理界面
<template>
<div class="system-manage">
<!-- 用户管理 -->
<el-card class="manage-card">
<template #header>
<div class="card-header">
<span>用户管理</span>
<el-button type="primary" @click="handleAddUser">新增用户</el-button>
</div>
</template>
<el-table :data="users" border>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="realName" label="真实姓名" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-switch
v-model="scope.row.status"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="handleEditUser(scope.row)">编辑</el-button>
<el-button type="text" @click="handleDeleteUser(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 系统日志 -->
<el-card class="manage-card">
<template #header>
<div class="card-header">
<span>系统日志</span>
<el-date-picker
v-model="dateRange"
type="daterange"
@change="handleDateChange"
/>
</div>
</template>
<el-table :data="logs" border>
<el-table-column prop="username" label="操作用户" />
<el-table-column prop="operation" label="操作类型" />
<el-table-column prop="createTime" label="操作时间" />
<el-table-column prop="ip" label="IP地址" />
<el-table-column prop="time" label="执行时长(ms)" />
</el-table>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useSystemStore } from '@/stores/system'
const systemStore = useSystemStore()
const users = ref([])
const logs = ref([])
const dateRange = ref([])
// 初始化数据
onMounted(async () => {
await loadUsers()
await loadLogs()
})
// 用户管理方法
const loadUsers = async () => {
users.value = await systemStore.getUsers()
}
const handleAddUser = () => {
// 实现新增用户逻辑
}
// 日志查询方法
const loadLogs = async () => {
logs.value = await systemStore.getLogs({
startDate: dateRange.value[0],
endDate: dateRange.value[1]
})
}
</script>
主要特点包括:
-
预约管理:
- 使用消息队列处理预约请求
- 实时通知功能
- 完善的时间冲突检查
- 多维度的预约统计
-
系统管理:
- AOP实现系统日志
- 基于Redis的权限缓存
- 完整的用户管理功能
- 可视化的日志查询界面
这些功能的实现考虑了:
- 系统性能:使用缓存和消息队列
- 安全性:完善的权限控制
- 可维护性:模块化设计
- 用户体验:直观的管理界面