十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue企业OA系统架构设计与实践

SpringBoot+Vue企业OA系统架构设计与实践 1. 企业OA管理系统架构解析这套基于SpringBootVueMySQL的企业OA管理系统采用了典型的前后端分离架构这种设计模式在现代Web开发中已经成为主流选择。前后端分离的核心优势在于解耦和并行开发——前端团队可以专注于用户交互体验后端团队则集中精力处理业务逻辑和数据安全。1.1 技术栈选型分析后端技术栈SpringBoot 2.7.x作为Spring生态的约定优于配置实现大幅减少了XML配置。我选择这个版本是因为它在JDK兼容性和社区支持度之间取得了良好平衡。MyBatis-Plus 3.5.x在原生MyBatis基础上增强了CRUD操作其Lambda表达式写法让代码更易维护。实测表明相比JPA它在复杂查询场景下性能优势明显。Spring Security提供完善的认证授权机制特别是与JWTJSON Web Token的集成方案非常成熟。前端技术栈Vue 3.x组合式API相比Options API组合式API的逻辑复用能力更强。项目中大量使用setup语法糖代码可读性提升显著。Element Plus作为Vue 3的组件库其Form和Table组件对OA系统的表单类界面支持极佳。特别推荐其动态表单验证功能。Axios拦截器统一处理401超时等异常情况配合后端定义的Result包装类错误处理流程标准化。数据库设计 MySQL 8.0的JSON字段类型非常适合存储审批流程中的动态表单数据。所有表都设置了utf8mb4字符集以支持完整emoji存储这是很多初期设计容易忽略的细节。关键经验在SpringBoot中配置Druid连接池时务必设置合理的initialSize和maxActive参数。我们的生产环境配置是initialSize5maxActive50这个配置在压力测试中表现最优。2. 核心模块实现细节2.1 权限管理系统设计RBAC基于角色的访问控制模型是本系统的安全基石。数据库设计包含五个核心表sys_user用户基础信息sys_role角色定义sys_menu菜单/权限点user_role用户-角色关联role_menu角色-权限关联// 权限校验核心逻辑示例 PreAuthorize(ss.hasPermi(system:user:list)) GetMapping(/list) public TableDataInfo list(SysUser user) { startPage(); ListSysUser list userService.selectUserList(user); return getDataTable(list); }前端路由通过meta.perms属性与后端权限码绑定动态生成可访问菜单。这里有个实用技巧在Vue Router的全局前置守卫中添加权限校验逻辑router.beforeEach((to, from, next) { if (to.meta.perms !store.getters.permissions.includes(to.meta.perms)) { next(/403) } else { next() } })2.2 工作流引擎实现审批流程采用状态机模式实现核心类包括ApprovalContext审批上下文ApprovalState状态接口DraftState/ApprovingState/CompletedState具体状态实现数据库设计上approval_flow表通过current_state字段跟踪审批进度配合approval_log表记录完整审批轨迹。特别要注意的是乐观锁设计UPDATE approval_flow SET current_state APPROVED, version version 1 WHERE approval_id ? AND version ?踩坑记录初期没有考虑并发审批场景导致状态覆盖问题。后来通过version字段实现乐观锁控制这是企业级应用必须考虑的问题。3. 性能优化实践3.1 数据库优化针对员工信息表的查询优化为dept_id和position_id添加复合索引使用覆盖索引避免回表大字段如员工照片单独存表CREATE INDEX idx_dept_position ON staff_info(dept_id, position_id);3.2 前端性能提升路由懒加载Vue的异步组件特性大幅减少首屏加载时间表格虚拟滚动Element Plus的el-table-v2组件处理万级数据毫无压力接口缓存高频但数据变化少的接口如部门树做内存缓存// 接口缓存实现示例 const cacheMap new Map(); async function getCachedData(url) { if (cacheMap.has(url)) { return cacheMap.get(url); } const res await axios.get(url); cacheMap.set(url, res.data); return res.data; }4. 典型问题解决方案4.1 文件上传断点续传前端采用文件分片通常2MB一片MD5校验的方案const chunkSize 2 * 1024 * 1024; const chunks Math.ceil(file.size / chunkSize); for (let i 0; i chunks; i) { const chunk file.slice(i * chunkSize, (i 1) * chunkSize); const formData new FormData(); formData.append(chunk, chunk); formData.append(hash, md5(chunk)); await axios.post(/upload, formData); }后端用Redis记录上传进度关键数据结构HSET file:progress {fileHash} {chunkIndex}4.2 消息推送方案采用WebSocket本地缓存的混合方案建立长连接接收实时消息本地IndexedDB存储历史消息心跳检测机制保持连接活跃const socket new WebSocket(wss://${location.host}/ws); socket.onmessage (event) { const message JSON.parse(event.data); if (message.type NOTIFICATION) { store.commit(addMessage, message); saveToIndexedDB(message); } };5. 部署实践指南5.1 容器化部署Docker Compose文件示例version: 3 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - mysql_data:/var/lib/mysql backend: build: ./backend ports: - 8080:8080 depends_on: - mysql frontend: build: ./frontend ports: - 80:80 volumes: mysql_data:5.2 性能监控配置SpringBoot ActuatorPrometheusGrafana监控方案添加依赖spring-boot-starter-actuator和micrometer-registry-prometheus配置application.ymlmanagement: endpoints: web: exposure: include: health,metrics,prometheus metrics: tags: application: ${spring.application.name}这套系统在实际部署时Nginx配置需要特别注意静态资源缓存策略。我们的最佳实践是带hash的资源文件设置1年长期缓存HTML文件设置no-cacheAPI接口全部禁用缓存location / { try_files $uri $uri/ /index.html; expires -1; } location ~* \.(js|css|png)$ { expires 1y; add_header Cache-Control public; }在开发过程中我们发现Element Plus的按需引入能显著减小打包体积。推荐使用unplugin-vue-components插件实现自动导入// vite.config.js import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default { plugins: [ Components({ resolvers: [ElementPlusResolver()], }), ], }对于需要支持国际化的大型企业我们在Vue中实现了动态语言包加载方案。语言文件按模块拆分通过Webpack魔法注释实现懒加载const messages { en: () import(/* webpackChunkName: lang-en */ ./locales/en.json), zh: () import(/* webpackChunkName: lang-zh */ ./locales/zh.json) }系统安全方面有几个关键实践值得分享所有接口必须通过Spring Security的CSRF保护密码存储使用BCryptPasswordEncoder强度因子设为12敏感操作日志全量记录包括操作人、时间、IP和具体内容Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); }在项目交接或团队协作时我们建立了完善的Swagger文档规范。推荐使用knife4j增强UI并通过分组管理不同模块的APIBean public Docket restApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName(1.0版本) .select() .apis(RequestHandlerSelectors.basePackage(com.oa.modules)) .paths(PathSelectors.any()) .build(); }这套系统经过三个版本迭代后我们总结出几个关键升级点将JSP页面全部重构为Vue组件首屏加载时间降低60%引入Redis缓存后高频接口响应时间从300ms降至50ms使用WebSocket替代轮询服务器负载降低40%对于需要处理Excel导出的模块我们放弃了传统的POI方案改用阿里巴巴的EasyExcel。实测显示导出10万行数据时内存占用从1.2GB降至200MB左右// 使用EasyExcel导出示例 ExcelWriter excelWriter EasyExcel.write(response.getOutputStream()) .head(Employee.class) .build(); excelWriter.write(dataList, EasyExcel.writerSheet(员工列表).build()); excelWriter.finish();在移动端适配方面我们通过viewportrem方案实现了响应式布局。关键CSS配置如下html { font-size: calc(100vw / 7.5); /* 设计稿750px基准 */ } media screen and (max-width: 480px) { html { font-size: calc(100vw / 4.8); /* 小屏调整比例 */ } }系统对接第三方服务时我们抽象出统一的API网关层具有以下特性请求签名验证自动重试机制熔断降级策略调用监控统计FeignClient(name sms-service, fallback SmsServiceFallback.class) public interface SmsService { PostMapping(/send) ResultString sendSms(RequestBody SmsRequest request); }对于数据敏感的企业我们在MySQL基础上增加了数据脱敏模块。实现方案是在MyBatis的TypeHandler层进行转换public class SensitiveTypeHandler extends BaseTypeHandlerString { Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) { ps.setString(i, SensitiveUtil.encrypt(parameter)); } Override public String getNullableResult(ResultSet rs, String columnName) { return SensitiveUtil.decrypt(rs.getString(columnName)); } }项目质量保障方面我们建立了完整的自动化测试体系后端JUnit5 Mockito TestContainers前端Jest Testing LibraryE2E测试Cypress代码质量SonarQube扫描Test DisplayName(员工创建测试) Transactional void testCreateStaff() { StaffDTO dto new StaffDTO(张三, 研发部); ResultLong result staffController.create(dto); assertNotNull(result.getData()); assertEquals(1, staffMapper.selectCount(null)); }在系统国际化实践中我们发现日期处理是个易错点。推荐统一使用Java 8的DateTime APIpublic static final DateTimeFormatter DATE_FORMATTER DateTimeFormatter.ofPattern(yyyy-MM-dd) .withZone(ZoneId.systemDefault()); public String formatDate(LocalDate date) { return DATE_FORMATTER.format(date); }前端错误监控我们采用Sentry方案通过Vue的errorHandler全局捕获异常app.config.errorHandler (err, vm, info) { Sentry.captureException(err, { extra: { component: vm.$options.name, lifecycleHook: info } }) }对于高并发场景下的库存扣减等操作我们采用Redis分布式锁数据库乐观锁的双重保障public boolean reduceInventory(Long productId, int num) { String lockKey lock:product: productId; try { // 获取分布式锁 boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 30, TimeUnit.SECONDS); if (!locked) return false; // 业务操作 return productMapper.updateInventory(productId, num) 0; } finally { redisTemplate.delete(lockKey); } }系统升级维护时Flyway数据库迁移工具是我们的首选。每个变更脚本都遵循命名规范V20230501_1__create_staff_table.sql V20230501_2__add_staff_index.sql在开发环境配置方面我们使用Profile机制隔离不同环境# application-dev.yml spring: datasource: url: jdbc:mysql://localhost:3306/oa_dev username: devuser password: dev123 # application-prod.yml spring: datasource: url: jdbc:mysql://prod-db:3306/oa_prod username: ${DB_USER} password: ${DB_PASSWORD}前端性能分析推荐使用Chrome的Lighthouse工具重点关注首屏渲染时间可交互时间(TTI)累计布局偏移(CLS)最大内容绘制(LCP)对于需要生成PDF的场景我们对比多种方案后选择pdf-lib支持动态页眉页脚精确的文本定位良好的中文支持const { PDFDocument } require(pdf-lib); async function generatePDF() { const pdfDoc await PDFDocument.create(); const page pdfDoc.addPage([550, 750]); page.drawText(员工信息表, { x: 50, y: 700 }); return await pdfDoc.save(); }系统通知模块采用发布-订阅模式实现关键设计事件中心统一管理消息支持多种通知渠道站内信、邮件、短信消息模板可配置化EventListener public void handleTaskEvent(TaskAssignEvent event) { Notification notification new Notification(); notification.setType(TASK_ASSIGN); notification.setContent(buildTaskContent(event.getTask())); notificationService.send(notification); }在代码规范方面我们配置了完整的CheckstyleSpotBugsPMD检查并通过Git预提交钩子强制执行。特别推荐使用Git的commit message规范feat(任务模块): 增加任务导出功能 fix(审批流程): 修复多级审批状态错误 docs(用户手册): 更新权限配置说明对于需要处理大文件上传的场景我们开发了分片上传断点续传功能。前端使用spark-md5计算文件指纹后端用Redis记录上传进度public boolean checkChunk(String fileMd5, int chunkIndex) { String key upload: fileMd5; return redisTemplate.opsForSet().isMember(key, chunkIndex); }系统对接微信企业号时我们封装了统一的消息适配器支持文本消息图文消息模板卡片文件消息public interface WechatSender { void sendText(String content); void sendNews(ListArticle articles); void sendFile(File file); }在压力测试阶段我们使用JMeter模拟了1000并发用户场景发现并解决了几个性能瓶颈Nginx调优调整worker_connections和keepalive_timeoutMySQL优化增加innodb_buffer_pool_sizeJVM参数设置合理的堆内存和GC策略对于需要长期维护的项目我们建立了完整的文档体系架构设计文档ADRAPI接口文档Swagger补充说明部署手册含应急回滚步骤运维白皮书监控指标常见问题在团队协作方面我们采用Git Flow工作流配合Jira进行任跟踪。代码审查是强制流程每个PR必须至少经过两位核心成员的review。
返回列表