1. 项目概述:Spring Boot企业级在线办公平台
十年前我刚入行时,企业OA系统还停留在Struts2+JDBC的笨重架构上。如今基于Spring Boot的现代化办公平台,已经能实现分钟级部署和弹性扩展。这次要分享的毕业设计项目,正是运用Spring Boot最新技术栈构建的全功能在线办公系统。
这个系统定位为中小型企业提供开箱即用的协同办公解决方案,包含六大核心模块:工作流引擎(支持自定义审批流)、即时通讯(WebSocket长连接)、文档协作(Office在线编辑)、任务看板(敏捷项目管理)、数据报表(动态可视化)和系统监控(Prometheus集成)。我曾用类似架构为3家科技公司实施过OA系统升级,实测单服务器可承载300人同时在线协作。
2. 技术架构设计解析
2.1 为什么选择Spring Boot
2018年我在传统SSM架构上开发第一个OA系统时,仅XML配置就写了2000多行。现在用Spring Boot 3.1.5版本,配合starter依赖,基础配置不超过10个文件:
// 典型的多模块Maven结构 oa-parent ├── oa-common // 公共工具包 ├── oa-system // 核心业务 ├── oa-admin // 管理后台 └── oa-api // REST接口特别提醒:千万别直接继承spring-boot-starter-parent,这会导致依赖冲突。应该用dependencyManagement方式引入:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.1.5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>2.2 工作流引擎选型对比
我对比过三种方案:
- Activiti:功能全但太重,需要24张表
- Flowable:Activiti分支,支持DMN规则引擎
- Camunda:性能最优但商业授权复杂
最终选择Flowable 6.7.0,因其完美支持Spring Boot自动配置:
flowable: async-executor-activate: true database-schema-update: true history-level: audit实际开发中要注意:审批节点的变量传递必须用execution.setVariable()而非局部变量,否则跨节点会丢失数据。这是我在某次生产事故中得到的教训。
3. 核心模块实现细节
3.1 即时通讯的三种技术方案
| 方案 | 延迟 | 兼容性 | 开发成本 |
|---|---|---|---|
| 轮询 | >1s | 最好 | 低 |
| SSE | 500ms | 中等 | 中 |
| WebSocket | <100ms | IE10+ | 高 |
选择SockJS+STOMP协议实现降级方案:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOrigins("*") .withSockJS(); } }踩坑记录:Chrome浏览器在非活动标签页会限制WebSocket心跳,需要前端定期发送空消息保活。
3.2 文档协作的Office在线编辑
采用OnlyOffice+Redis实现实时协同:
- 前端集成onlyoffice-editor组件
- 文档变更通过Redis Pub/Sub广播
- 版本控制使用git-like的差异算法
关键代码片段:
@RedisListener(channel = "doc:update") public void onDocumentUpdate(DocChangeEvent event) { // 使用Operational Transformation算法解决冲突 OT.applyChanges(currentDoc, event.getChanges()); versionControl.commit(event.getUser(), event.getChanges()); }4. 性能优化实战
4.1 Nginx层缓存策略
静态资源采用指纹+永久缓存:
location /static { expires max; add_header Cache-Control public; access_log off; }API接口按业务特性分级缓存:
@GetMapping("/announcements") @Cacheable(cacheNames = "slowApiCache", key = "#root.methodName+#pageable") public Page<Announcement> getAnnouncements(Pageable pageable) { // 查询耗时>100ms的接口 }4.2 数据库分库分表
用户表按企业ID水平分片:
@DS("#session.getAttribute('tenantId')") public interface UserMapper extends BaseMapper<User> { // 自动路由到对应数据源 }分片策略配置示例:
spring: shardingsphere: datasource: names: ds0,ds1 sharding: tables: t_user: actual-data-nodes: ds$->{0..1}.t_user_$->{0..15} table-strategy: inline: sharding-column: tenant_id algorithm-expression: t_user_$->{tenant_id % 16}5. 安全防护体系
5.1 认证授权方案对比
| 方案 | 适合场景 | 实现复杂度 |
|---|---|---|
| Session | 传统Web应用 | 低 |
| JWT | 前后端分离 | 中 |
| OAuth2 | 第三方登录 | 高 |
最终采用双Token机制:
- access_token:30分钟过期
- refresh_token:7天有效期
public class JwtTokenUtil { private static final String SECRET = "oa-system-256-bit-secret"; public String generateToken(UserDetails details) { return Jwts.builder() .setSubject(details.getUsername()) .setExpiration(new Date(System.currentTimeMillis() + 1800_000)) .signWith(Keys.hmacShaKeyFor(SECRET.getBytes())) .compact(); } }5.2 敏感数据加密
采用国密SM4算法加密身份证号等字段:
@Convert(converter = CryptoConverter.class) private String idCardNumber; public class CryptoConverter implements AttributeConverter<String, String> { private static final SM4 sm4 = new SM4("密钥需存储在KMS中"); @Override public String convertToDatabaseColumn(String attribute) { return sm4.encrypt(attribute); } }6. 部署与监控方案
6.1 Docker Compose编排
典型的三层服务架构:
version: '3.8' services: frontend: image: nginx:1.23 ports: ["80:80"] backend: image: openjdk:17-jdk environment: - SPRING_PROFILES_ACTIVE=prod depends_on: - redis - mysql redis: image: redis:7.0 mysql: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql6.2 Prometheus监控指标
自定义业务指标采集:
@RestController public class MetricsController { private final Counter loginCounter = Counter.build() .name("oa_login_total") .help("用户登录次数统计") .register(); @PostMapping("/login") public ResponseEntity<?> login() { loginCounter.inc(); // 登录逻辑 } }Grafana看板配置关键指标:
- 系统级:CPU/Memory/Disk
- JVM:GC次数/堆内存
- 业务级:在线人数/审批耗时
7. 开发中的典型问题
7.1 跨域问题的三种解法
- 简单方案:@CrossOrigin注解
- 生产级方案:Nginx反向代理
- 特殊场景:Gateway统一处理
最佳实践是结合CORS过滤器:
@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("https://yourdomain.com"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }7.2 事务失效的五大场景
- 方法非public
- 自调用问题
- 异常被捕获
- 传播属性设置错误
- 数据库引擎不支持
最隐蔽的是自调用问题:
public class ApprovalService { public void submit() { this.validate(); // 事务失效点 } @Transactional public void validate() { // 不会回滚 } }解决方法是用AopContext获取代理对象:
((ApprovalService)AopContext.currentProxy()).validate();8. 前端工程化实践
8.1 Vue3+TypeScript组合
采用