1. 项目概述:宿舍报修管理系统的现实需求
大学宿舍作为学生日常生活的重要场所,各类设施设备的报修需求频繁发生。传统纸质报修单或电话报修方式存在响应慢、进度不透明、统计困难等问题。这个基于SpringBoot的宿舍报修管理系统正是为解决这些痛点而设计。
我在实际开发中发现,一个完善的宿舍报修系统需要满足以下核心需求:学生能够便捷提交报修申请、维修人员可以高效处理工单、管理员需要全面掌握维修数据。系统采用B/S架构,前端使用Thymeleaf模板引擎,后端基于SpringBoot框架,数据库选用MySQL,实现了从报修到完工的全流程数字化管理。
2. 技术选型与架构设计
2.1 SpringBoot框架优势分析
选择SpringBoot作为基础框架主要基于以下几个考虑:
- 快速启动:内嵌Tomcat服务器,无需复杂配置即可运行
- 约定优于配置:减少了大量XML配置工作
- 丰富的Starter依赖:轻松集成各种常用组件
- 完善的生态系统:社区支持强大,问题解决资源丰富
在项目中,我们特别使用了SpringBoot的这些特性:
- spring-boot-starter-web:处理HTTP请求和响应
- spring-boot-starter-data-jpa:简化数据库操作
- spring-boot-starter-thymeleaf:实现服务端页面渲染
- spring-boot-devtools:提升开发效率
2.2 数据库设计要点
报修系统的核心数据模型包括:
- 用户表(user):存储学生、维修工和管理员信息
- 宿舍表(dormitory):记录宿舍楼和房间信息
- 报修单表(repair_order):存储报修详情和状态
- 维修记录表(repair_record):跟踪维修过程和结果
sql复制CREATE TABLE repair_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT NOT NULL,
dormitory_id BIGINT NOT NULL,
title VARCHAR(100) NOT NULL,
description TEXT,
status TINYINT DEFAULT 0 COMMENT '0-待处理,1-已分配,2-维修中,3-已完成',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
finish_time DATETIME,
FOREIGN KEY (student_id) REFERENCES user(id),
FOREIGN KEY (dormitory_id) REFERENCES dormitory(id)
);
2.3 前后端交互设计
系统采用传统的服务端渲染模式,主要考虑以下因素:
- 开发效率高:适合小型管理系统快速开发
- SEO友好:所有内容由服务端生成
- 技术门槛低:团队成员熟悉Thymeleaf模板
关键页面路由设计:
- /login:用户登录
- /student/dashboard:学生主页
- /student/repair:报修申请
- /worker/tasks:维修工任务列表
- /admin/statistics:管理员数据统计
3. 核心功能实现细节
3.1 报修流程实现
报修是系统的核心功能,实现时需要注意以下几点:
- 表单验证:前端使用HTML5验证结合后端Spring Validation
java复制@PostMapping("/repair")
public String submitRepair(@Valid RepairForm form, BindingResult result,
@AuthenticationPrincipal User user) {
if (result.hasErrors()) {
return "student/repair";
}
// 处理报修逻辑
}
- 状态机设计:使用枚举定义报修单状态
java复制public enum RepairStatus {
PENDING(0, "待处理"),
ASSIGNED(1, "已分配"),
PROCESSING(2, "维修中"),
COMPLETED(3, "已完成");
private final int code;
private final String desc;
// 构造方法和getter
}
- 通知机制:使用Spring的事件机制实现状态变更通知
java复制@Service
@RequiredArgsConstructor
public class RepairService {
private final ApplicationEventPublisher eventPublisher;
public void updateStatus(Long orderId, RepairStatus status) {
// 更新状态
eventPublisher.publishEvent(new RepairStatusEvent(orderId, status));
}
}
3.2 权限控制实现
系统包含三类角色:学生、维修工和管理员,使用Spring Security实现权限控制:
- 安全配置类
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/student/**").hasRole("STUDENT")
.antMatchers("/worker/**").hasRole("WORKER")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.and()
.logout()
.logoutSuccessUrl("/login");
}
}
- 用户详情服务
java复制@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.roles(user.getRole().name())
.build();
}
}
3.3 数据统计功能
管理员需要查看各类统计报表,我们使用JPA的聚合查询和Chart.js实现:
- 统计服务层
java复制public interface RepairOrderRepository extends JpaRepository<RepairOrder, Long> {
@Query("SELECT new map(r.status as status, COUNT(r) as count) " +
"FROM RepairOrder r GROUP BY r.status")
List<Map<String, Object>> countByStatus();
@Query("SELECT new map(d.name as dormitory, COUNT(r) as count) " +
"FROM RepairOrder r JOIN r.dormitory d GROUP BY d.name")
List<Map<String, Object>> countByDormitory();
}
- 控制器处理
java复制@GetMapping("/admin/statistics")
public String statistics(Model model) {
model.addAttribute("statusStats", orderRepository.countByStatus());
model.addAttribute("dormStats", orderRepository.countByDormitory());
return "admin/statistics";
}
4. 开发中的关键问题与解决方案
4.1 文件上传与存储
报修时可能需要上传损坏设备的照片,我们实现了以下方案:
- 配置文件上传
yaml复制spring:
servlet:
multipart:
max-file-size: 5MB
max-request-size: 10MB
- 上传处理逻辑
java复制@PostMapping("/repair/upload")
public String handleUpload(@RequestParam("file") MultipartFile file,
@RequestParam("orderId") Long orderId) {
if (!file.isEmpty()) {
String filename = storeFile(file);
repairService.addAttachment(orderId, filename);
}
return "redirect:/student/repair/" + orderId;
}
private String storeFile(MultipartFile file) {
String filename = UUID.randomUUID() + "_" + file.getOriginalFilename();
Path path = Paths.get(uploadDir, filename);
Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return filename;
}
4.2 分页查询优化
报修记录可能很多,我们实现了高效的分页查询:
- 分页参数封装
java复制@GetMapping("/worker/tasks")
public String listTasks(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
Model model) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
Page<RepairOrder> orders = orderRepository.findByStatusNot(
RepairStatus.COMPLETED, pageable);
model.addAttribute("orders", orders);
return "worker/tasks";
}
- 前端分页控件
html复制<div class="pagination">
<span th:if="${orders.hasPrevious()}">
<a th:href="@{/worker/tasks(page=${orders.number-1},size=${orders.size})}">上一页</a>
</span>
<span th:each="i : ${#numbers.sequence(0, orders.totalPages-1)}">
<a th:if="${i != orders.number}"
th:href="@{/worker/tasks(page=${i},size=${orders.size})}"
th:text="${i+1}"></a>
<span th:unless="${i != orders.number}" th:text="${i+1}"></span>
</span>
<span th:if="${orders.hasNext()}">
<a th:href="@{/worker/tasks(page=${orders.number+1},size=${orders.size})}">下一页</a>
</span>
</div>
4.3 系统性能优化
随着数据量增加,我们实施了以下优化措施:
- 数据库索引优化
sql复制CREATE INDEX idx_repair_order_status ON repair_order(status);
CREATE INDEX idx_repair_order_dormitory ON repair_order(dormitory_id);
- 缓存常用数据
java复制@Cacheable(value = "dormitories", unless = "#result == null or #result.isEmpty()")
public List<Dormitory> getAllDormitories() {
return dormitoryRepository.findAll();
}
- 启用Gzip压缩
yaml复制server:
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
min-response-size: 1024
5. 部署与运维实践
5.1 多环境配置
使用SpringBoot的Profile特性管理不同环境配置:
- 配置文件结构
code复制application.yml
application-dev.yml
application-prod.yml
- 激活特定Profile
yaml复制spring:
profiles:
active: dev
- 环境特定配置示例(application-prod.yml)
yaml复制spring:
datasource:
url: jdbc:mysql://prod-db:3306/repair_system
username: prod_user
password: ${DB_PASSWORD}
server:
port: 8080
servlet:
context-path: /repair
5.2 日志管理
完善的日志配置对系统运维至关重要:
- Logback配置(logback-spring.xml)
xml复制<configuration>
<property name="LOG_PATH" value="./logs"/>
<property name="LOG_FILE" value="repair-system"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
- 自定义业务日志
java复制@Slf4j
@Service
public class RepairService {
public void completeOrder(Long orderId) {
// 业务逻辑
log.info("报修单 {} 已完成", orderId);
}
}
5.3 健康检查与监控
SpringBoot Actuator提供了完善的监控端点:
- 添加依赖
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置暴露的端点
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
- 自定义健康检查
java复制@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
@Override
public Health health() {
try (Connection conn = dataSource.getConnection()) {
if (conn.isValid(1000)) {
return Health.up().build();
}
} catch (SQLException e) {
return Health.down(e).build();
}
return Health.unknown().build();
}
}
6. 项目扩展与改进方向
在实际使用过程中,我们发现系统还可以在以下方面进行扩展:
- 移动端适配:开发微信小程序或APP版本,方便学生随时报修
- 消息推送:集成短信或微信通知,及时告知报修状态变更
- 智能分配:根据维修工位置和技能自动分配工单
- 数据分析:使用ELK堆栈实现更强大的日志分析
- 微服务化:将系统拆分为用户服务、报修服务、通知服务等
对于源码的学习和使用,建议重点关注以下几个核心类:
- RepairApplication:SpringBoot主启动类
- RepairController:报修业务控制器
- RepairOrderRepository:数据访问层接口
- SecurityConfig:安全配置类
- RepairStatusEvent:状态变更事件类
在开发类似系统时,数据库设计要预留扩展字段,权限控制要细致规划,状态流转要考虑周全。我在实际开发中最大的体会是:简单的业务系统也要注重代码结构和可维护性,良好的分层设计和清晰的命名规范能为后续维护节省大量时间。
