1. 项目概述
"学生用品采购系统"是一个基于SpringBoot框架的JavaWeb应用,专为学校后勤管理部门设计。这个系统解决了传统手工登记采购流程效率低下、数据易丢失、统计困难等问题。我在实际开发中发现,这类系统在高校信息化建设中需求旺盛,但市面上成熟产品往往价格昂贵且功能冗余。
这个系统采用B/S架构,前端使用Thymeleaf模板引擎,后端基于SpringBoot 2.7.3开发,数据库选用MySQL 8.0。相比传统Servlet/JSP方案,SpringBoot的自动配置特性让项目搭建时间缩短了60%以上。系统包含采购申请、审批流、库存管理、供应商管理、报表统计等核心模块,适合5000人以下规模的中小学校使用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 框架选型考量
选择SpringBoot而非传统SSM框架主要基于三点考虑:
- 内嵌Tomcat简化部署,特别适合学校IT力量薄弱的环境
- Starter依赖自动管理JAR包版本,避免常见的依赖冲突问题
- Actuator端点提供运行监控,方便管理员掌握系统健康状况
数据库选型时对比了MySQL和PostgreSQL:
- MySQL社区资源丰富,学校现有运维人员更熟悉
- 采购系统事务复杂度中等,不需要PostgreSQL的高级特性
- 最终选择MySQL 8.0的InnoDB引擎,启用事务隔离级别READ_COMMITTED
2.2 分层架构实现
系统采用经典三层架构:
code复制com.example.procurement
├── config # 配置类
├── controller # 表现层
├── service # 业务逻辑层
│ ├── impl # 服务实现
├── repository # 数据访问层
├── model # 实体类
├── util # 工具类
└── exception # 异常处理
特别设计了采购业务专属包结构:
java复制com.example.procurement.purchase
├── PurchaseController.java
├── PurchaseService.java
├── PurchaseRepository.java
├── dto # 数据传输对象
├── vo # 视图对象
└── enums # 枚举类
3. 核心功能实现
3.1 采购审批流程
采用状态机模式实现审批流:
java复制public enum PurchaseStatus {
DRAFT("草稿"),
SUBMITTED("已提交"),
DEPARTMENT_APPROVED("部门审核通过"),
FINANCE_APPROVED("财务审核通过"),
REJECTED("已驳回"),
COMPLETED("已完成");
// 状态流转规则
private static final Map<PurchaseStatus, Set<PurchaseStatus>> transitions = Map.of(
DRAFT, Set.of(SUBMITTED),
SUBMITTED, Set.of(DEPARTMENT_APPROVED, REJECTED),
DEPARTMENT_APPROVED, Set.of(FINANCE_APPROVED, REJECTED)
);
public static boolean canTransition(PurchaseStatus from, PurchaseStatus to) {
return transitions.getOrDefault(from, Collections.emptySet()).contains(to);
}
}
审批业务逻辑实现要点:
- 使用@Transactional确保状态变更和操作记录的原子性
- 审批链采用责任链模式,便于后续扩展审批层级
- 每个状态变更生成操作日志,记录操作人和时间
3.2 库存管理设计
采用乐观锁解决并发修改问题:
java复制@Repository
public interface ItemRepository extends JpaRepository<Item, Long> {
@Modifying
@Query("UPDATE Item i SET i.stock = i.stock - :quantity, i.version = i.version + 1 " +
"WHERE i.id = :id AND i.version = :version")
int deductStock(@Param("id") Long id,
@Param("quantity") Integer quantity,
@Param("version") Integer version);
}
库存预警实现方案:
- 使用Spring Scheduler每天凌晨检查库存量
- 对低于安全库存的物品自动生成采购建议
- 预警信息通过WebSocket实时推送给管理员
4. 关键技术实现
4.1 文件导入导出
使用Apache POI处理Excel导入:
java复制public List<PurchaseItem> importExcel(MultipartFile file) {
try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = workbook.getSheetAt(0);
return StreamSupport.stream(sheet.spliterator(), false)
.skip(1) // 跳过标题行
.map(row -> PurchaseItem.builder()
.name(row.getCell(0).getStringCellValue())
.spec(row.getCell(1).getStringCellValue())
.quantity((int)row.getCell(2).getNumericCellValue())
.build())
.collect(Collectors.toList());
} catch (Exception e) {
throw new ImportException("Excel解析失败", e);
}
}
导出PDF使用OpenPDF库:
java复制@GetMapping("/export/pdf")
public void exportPdf(HttpServletResponse response) {
response.setContentType("application/pdf");
try (PdfWriter writer = new PdfWriter(response.getOutputStream());
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf)) {
// 添加表格数据
Table table = new Table(UnitValue.createPercentArray(5)).useAllAvailableWidth();
purchaseService.findAll().forEach(item ->
table.addCell(item.getName())
.addCell(item.getSpec())
.addCell(String.valueOf(item.getQuantity()))
);
document.add(table);
} catch (IOException e) {
throw new ExportException("PDF生成失败", e);
}
}
4.2 安全防护措施
XSS防护方案:
- 前端使用DOMPurify过滤富文本内容
- 后端配置Jackson的JsonSerializer进行二次过滤
- 对PDF导出内容使用HTMLSanitizer处理
SQL注入防护:
- 全程使用JPA/Hibernate参数化查询
- 对原生SQL语句使用PreparedStatement
- 禁止拼接SQL语句
5. 部署与优化
5.1 生产环境部署
Docker部署方案:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/procurement-system.jar .
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "procurement-system.jar"]
启动参数优化:
bash复制java -jar procurement-system.jar \
-Xms512m -Xmx1024m \
-XX:MaxMetaspaceSize=256m \
-Dspring.profiles.active=prod
5.2 性能调优经验
数据库优化措施:
- 为采购单号建立唯一索引
- 大表分页查询使用覆盖索引
- 定期执行ANALYZE TABLE更新统计信息
缓存策略实现:
java复制@Cacheable(value = "suppliers", key = "#root.methodName")
public List<Supplier> findAllSuppliers() {
return supplierRepository.findAll();
}
@CacheEvict(value = "suppliers", allEntries = true)
public Supplier addSupplier(Supplier supplier) {
return supplierRepository.save(supplier);
}
6. 常见问题解决
6.1 事务失效场景
发现并解决的典型问题:
- 同类方法调用导致@Transactional失效 → 改用AopContext.currentProxy()
- 异常类型未正确配置 → 明确指定rollbackFor
- 多数据源未正确绑定 → 配置TransactionManager
6.2 并发问题处理
采购单并发提交解决方案:
java复制public PurchaseOrder submitOrder(Long orderId) {
return transactionTemplate.execute(status -> {
PurchaseOrder order = orderRepository.findByIdWithLock(orderId);
if (order.getStatus() != DRAFT) {
throw new IllegalStateException("订单状态异常");
}
order.setStatus(SUBMITTED);
return orderRepository.save(order);
});
}
6.3 日志排查技巧
关键日志配置:
properties复制logging.level.com.example.procurement=DEBUG
logging.level.org.hibernate.SQL=WARN
logging.level.org.springframework.web=INFO
日志分析经验:
- 使用MDC记录请求追踪ID
- 敏感数据脱敏处理
- 异步记录操作日志避免影响主流程
7. 扩展与改进
7.1 后续优化方向
- 引入Redis缓存热点数据
- 使用Elasticsearch实现全文检索
- 增加移动端审批功能
- 对接财务系统自动生成凭证
7.2 监控方案建议
Prometheus监控配置示例:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configureMetrics() {
return registry -> registry.config().commonTags("application", "procurement-system");
}
健康检查端点:
java复制@GetMapping("/health")
public ResponseEntity<Health> healthCheck() {
boolean dbHealthy = databaseHealthChecker.check();
return dbHealthy ?
ResponseEntity.ok(Health.up().build()) :
ResponseEntity.status(503).body(Health.down().build());
}
在实际部署中发现,学校网络环境往往存在不稳定情况,建议增加断点续传功能应对大文件上传中断问题。对于审批流程,后续可考虑集成工作流引擎如Flowable,但需要评估学校IT人员的维护能力。系统运行半年来,平均每周处理采购单约120份,高峰期需特别注意数据库连接池配置,建议设置最大连接数不低于20。
