当前位置: 首页 > article >正文

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

核心功能

  1. 用户管理

    • 学生/教师登录注册
    • 角色权限控制
    • 个人信息管理
  2. 教室管理

    • 教室信息维护
    • 教室状态实时更新
    • 教室使用统计
  3. 预约管理

    • 在线预约
    • 预约审核
    • 预约记录查询
  4. 系统管理

    • 用户管理
    • 权限配置
    • 系统日志

数据库设计

主要表结构

-- 用户表
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>

项目亮点

  1. 采用前后端分离架构,提高开发效率和系统可维护性
  2. 使用Redis缓存热点数据,提升查询性能
  3. 实现基于JWT的无状态认证
  4. 引入消息队列处理预约请求,提高系统并发能力
  5. 支持教室使用情况的可视化展示

项目部署

  1. 环境要求

    • JDK 1.8+
    • Node.js 16+
    • MySQL 8.0
    • Redis 6.0+
  2. 部署步骤

    • 导入数据库脚本
    • 配置后端application.yml
    • 前端打包构建
    • 使用Nginx部署前端资源
    • 启动后端服务

总结与展望

本项目实现了校园闲置教室的智能化管理,提高了教室资源利用率。未来计划添加以下功能:

  1. 引入人脸识别实现无感进出
  2. 集成教务系统数据
  3. 开发移动端应用
  4. 添加教室环境监控功能

校园闲置教室查询系统功能详解

一、用户管理模块

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>

以上代码展示了用户管理和教室管理模块的核心实现。主要特点包括:

  1. 用户管理:

    • 完善的用户信息存储
    • 安全的密码加密存储
    • 基于JWT的身份认证
    • 细粒度的权限控制
  2. 教室管理:

    • 详细的教室信息记录
    • 使用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>

主要特点包括:

  1. 预约管理:

    • 使用消息队列处理预约请求
    • 实时通知功能
    • 完善的时间冲突检查
    • 多维度的预约统计
  2. 系统管理:

    • AOP实现系统日志
    • 基于Redis的权限缓存
    • 完整的用户管理功能
    • 可视化的日志查询界面

这些功能的实现考虑了:

  • 系统性能:使用缓存和消息队列
  • 安全性:完善的权限控制
  • 可维护性:模块化设计
  • 用户体验:直观的管理界面

http://www.kler.cn/a/535451.html

相关文章:

  • 【Prometheus】如何通过golang生成prometheus格式数据
  • GPU-Z重磅更新,Blackwell架构全面支持
  • React 第二十二节 useSyncExternalStore Hook 常见问题及用法详解
  • C++ 入门速通-第5章【黑马】
  • 快速傅里叶离散变换FFT (更新中)
  • C#面试常考随笔13: 泛型的主要约束和次要约束是什么?
  • 【漫画机器学习】082.岭回归(或脊回归)中的α值(alpha in ridge regression)
  • 机器学习模型--线性回归、逻辑回归、分类
  • 【vue3 入门到实战】7. 标签中的 ref
  • 代码随想录day06
  • VScode如何使用deepseek详细教程
  • 某音小程序反编译签名加密静态分析
  • Mac下使用brew安装go 以及遇到的问题
  • 面试笔记-多线程篇
  • uv 安装包
  • hadoop生态 apache-Flume-1.8.0 的安装和 使用
  • 【Linux】如何创建一个可定时删除的文件
  • Kali Linux 渗透测试环境配置(Metasploit + Burp Suite)
  • 源路由 | 源路由网桥 / 生成树网桥
  • 面试笔记-基础篇
  • 网络安全辅助系统 框架图 网络安全模块
  • Vue中的的通信方式有几种?
  • vue2+vue3 HMCXY基础入门
  • ONE NET MQTT+HTTP多端控制
  • 实际时钟(RTC)的介绍
  • Qt最新热点