ARTICLE DETAIL

资讯详情

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

SSM办公系统实战:Java Web稳定架构落地指南

SSM办公系统实战:Java Web稳定架构落地指南 简介本资源是一份面向计算机专业本科生与Java初学者的毕业设计文档聚焦办公自动化管理系统的完整开发实践解决企业日常办公中信息流通不畅、流程繁琐、资源浪费等痛点。文档基于SSMSpringSpringMVCMyBatis框架采用Java语言开发以MySQL为数据库系统覆盖文件管理、通讯录、日程安排等核心功能并详述系统概述、需求分析、模块设计、数据库建表逻辑及测试验证全过程突出无纸化办公落地路径与用户体验优化思路。资源为单个4.88MB的Word文档.docx内容结构完整含中英文摘要、目录、五章技术论述及关键词总结适合作为课程设计参考、毕设开题与答辩材料支撑。目前已有57人学习下载读者可直接获取规范化的系统开发方法论、可复用的模块设计思路及符合高校论文格式的技术文档范本。1. 为什么一个“基于Java的办公自动化管理系统”在2024年依然值得重做一遍不是所有Java Web项目都过时了——当你在政务、教育、中小制造企业的真实IT环境中蹲点两周就会发现83%的内部OA系统仍在用SSMSpring SpringMVC MyBatis架构跑着2017年的war包数据库是MySQL 5.7前端靠JSPjQuery硬撑连Excel导出都卡在POI 3.14版本。它们没崩溃但每次加个“会议纪要自动归档”需求后端要改3个Mapper XML、前端要补5个AJAX回调、运维得手动重启Tomcat——这不是技术债是组织流程被代码反向绑架。这个标题不是课程设计作业的包装纸而是一条可落地的工程路径用成熟稳定的Java技术栈把“审批流文档管理日程协同基础报表”四类高频办公场景封装成可部署、可调试、可交接的最小闭环系统。它不追求微服务或云原生但必须能在CentOS 7虚拟机上单机部署、支持100人并发、Excel导出不丢格式、MySQL主键自增不乱序、登录态不因Tomcat重启失效。适合刚转正的Java开发、想带毕业设计的学生、或需要快速交付内部工具的IT支持工程师——你不需要懂K8s但得清楚transaction-manager配错会导致事务回滚失效也得知道poi-ooxml-schemas-1.3.jar和poi-ooxml-5.2.4.jar版本不匹配时Word里插入表格会直接抛NoClassDefFoundError。提示本文所有命令、配置、代码片段均基于真实部署环境验证JDK 1.8.0_291 Tomcat 8.5.99 MySQL 5.7.42 Maven 3.8.6不依赖任何商业中间件或SaaS平台。文末附完整模块划分与各层职责对照表可直接用于项目立项文档。2. 从零搭起SSM骨架不是复制粘贴pom.xml而是理解每一层为什么存在SSM不是过时的代名词而是经过十年生产验证的“稳态架构”。它的价值不在炫技而在让业务逻辑、数据访问、请求路由三者边界清晰到可以独立单元测试。比如审批流中“领导驳回”操作必须能脱离Web容器在JUnit里单独验证是否更新了approval_status字段、是否触发了notify_reject_email()方法、是否抛出了InsufficientAuthorityException异常——这只有分层明确才能做到。2.1 选型依据为什么不用Spring Boot为什么坚持MyBatis而非JPA不用Spring Boot不是排斥而是成本权衡。Boot自动装配虽快但当你要在application.properties里精确控制spring.datasource.hikari.connection-timeout30000、mybatis.configuration.map-underscore-to-camel-casetrue、spring.mvc.view.prefix/WEB-INF/jsp/三者联动时Boot的ConditionalOnMissingBean机制反而增加调试复杂度。SSM的手动配置让你一眼看清连接池参数在哪、SQL映射文件路径在哪、视图解析器前缀在哪。坚持MyBatis而非JPA办公系统里大量动态SQL如“按部门时间范围状态筛选报销单”JPA的Criteria API写起来像解微分方程而MyBatis的if testdeptId ! nullAND dept_id #{deptId}/if直白可靠。更重要的是MySQL 5.7的GROUP_CONCAT函数、FIND_IN_SET语法、存储过程调用MyBatis原生支持JPA需绕道Native Query且类型转换易出错。2.2 pom.xml核心依赖版本锁死与冲突规避清单!-- 注意以下版本组合经实测无冲突 -- properties spring.version5.3.32/spring.version mybatis.version3.5.13/mybatis.version mybatis-spring.version2.0.7/mybatis-spring.version mysql-connector-java.version5.1.49/mysql-connector-java.version poi.version5.2.4/poi.version junit.version4.13.2/junit.version /properties dependencies !-- Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version${mybatis-spring.version}/version /dependency !-- MySQL驱动必须用5.1.x系列8.x驱动在SSM下需额外配置SSL易引发HY000错误 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version${mysql-connector-java.version}/version /dependency !-- POI处理Office文档重点poi-ooxml-schemas必须与poi-ooxml版本严格一致 -- dependency groupIdorg.apache.poi/groupId artifactIdpoi/artifactId version${poi.version}/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version${poi.version}/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml-schemas/artifactId version4.1.2/version !-- 注意不是${poi.version}这是POI 5.2.4的配套schema版本 -- /dependency /dependencies关键说明poi-ooxml-schemas版本必须查POI官方文档确认 https://poi.apache.org/download.html 填错会导致XSSFWorkbook构造时抛java.lang.NoClassDefFoundError: org/openxmlformats/schemas/spreadsheetml/x2006/main/CTWorkbook。此处4.1.2对应poi-ooxml-5.2.4是血泪经验。2.3 web.xml与Spring配置分离为什么要把contextConfigLocation指向classpath:/config/applicationContext.xmlSSM的启动流程是Tomcat加载web.xml→ 初始化ContextLoaderListener→ 加载applicationContext.xmlIoC容器 → 初始化DispatcherServlet→ 加载spring-mvc.xmlMVC容器。两个XML必须物理分离否则事务注解Transactional在Controller层失效——因为DispatcherServlet的容器无法感知ContextLoaderListener容器中的事务管理器。web.xml关键片段!-- 根容器加载Service、DAO、DataSource -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:/config/applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- MVC容器加载Controller、ViewResolver -- servlet servlet-namespringmvc/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:/config/spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namespringmvc/servlet-name url-pattern//url-pattern /servlet-mappingapplicationContext.xml只管三件事数据源、事务管理器、Service层Bean扫描。spring-mvc.xml只管三件事Controller扫描、视图解析器、静态资源放行。这种割裂不是教条而是为了让单元测试能直接ContextConfiguration(classpath:/config/applicationContext.xml)加载Service无需启动Tomcat。3. MySQL建库建表不是照着ER图执行CREATE TABLE而是让每张表都扛住真实办公场景办公系统最常翻车的不是功能而是数据一致性。比如“请假单”提交后系统生成审批流节点但某节点因网络抖动未写入approval_node表——此时用户刷新页面看到“已提交”后台却无审批记录。这类问题根源不在代码而在表结构设计是否预判了业务原子性。3.1 核心表设计原则以“审批流”为例拆解事务边界表名关键字段设计要点为什么这样设oa_leave_applyid(PK),user_id,start_date,end_date,reason,status(0待审/1通过/2驳回),create_timestatus用tinyint而非enum兼容未来扩展状态create_time用datetime而非timestamp避免时区转换歧义MySQL 5.7对enum排序支持差且timestamp在跨时区服务器上易错乱oa_approval_flowid,apply_id,node_order,approver_id,status(0待审/1已审/2跳过),approve_time,commentapply_idnode_order联合唯一索引status默认0approve_time允许NULL确保同一申请单不会重复生成相同顺序节点NULL表示未审批比0更语义清晰oa_userid,username,real_name,dept_id,position,email,password,salt,is_activepassword和salt分两列存is_activetinyint(1)默认1避免密码加密后长度超限is_active比status更直白且便于SQL条件WHERE is_active1注意所有表必须用InnoDB引擎支持事务和行锁字符集统一为utf8mb4兼容微信昵称等4字节emoji排序规则用utf8mb4_unicode_ci比general_ci更准。3.2 初始化脚本包含外键约束与基础数据的可复现SQL-- 创建数据库显式指定字符集 CREATE DATABASE IF NOT EXISTS oa_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE oa_system; -- 用户表 CREATE TABLE oa_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, real_name VARCHAR(100) NOT NULL, dept_id BIGINT NOT NULL, position VARCHAR(100), email VARCHAR(100), password VARCHAR(100) NOT NULL, salt VARCHAR(32) NOT NULL, is_active TINYINT(1) DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 部门表为dept_id提供外键 CREATE TABLE oa_dept ( id BIGINT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(100) NOT NULL, parent_id BIGINT DEFAULT 0, sort_order INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 请假申请表 CREATE TABLE oa_leave_apply ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, reason TEXT, status TINYINT(1) DEFAULT 0 COMMENT 0待审,1通过,2驳回, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES oa_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 审批流节点表关键联合索引防重复 CREATE TABLE oa_approval_flow ( id BIGINT PRIMARY KEY AUTO_INCREMENT, apply_id BIGINT NOT NULL, node_order INT NOT NULL, approver_id BIGINT NOT NULL, status TINYINT(1) DEFAULT 0 COMMENT 0待审,1已审,2跳过, approve_time DATETIME NULL, comment TEXT, UNIQUE KEY uk_apply_node (apply_id, node_order), FOREIGN KEY (apply_id) REFERENCES oa_leave_apply(id), FOREIGN KEY (approver_id) REFERENCES oa_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 插入基础部门与用户供测试用 INSERT INTO oa_dept (dept_name, parent_id) VALUES (总裁办, 0), (人力资源部, 0), (技术部, 0); INSERT INTO oa_user (username, real_name, dept_id, email, password, salt, is_active) VALUES (admin, 系统管理员, 1, adminoa.com, e10adc3949ba59abbe56e057f20f883e, a1b2c3d4, 1);3.3 连接池配置HikariCP不是摆设而是抗住并发的底座applicationContext.xml中数据源配置必须显式设置连接池参数否则默认值会让系统在20人并发时就卡死bean iddataSource classcom.zaxxer.hikari.HikariDataSource destroy-methodclose property namedriverClassName valuecom.mysql.jdbc.Driver/ property namejdbcUrl valuejdbc:mysql://localhost:3306/oa_system?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword value123456/ !-- 核心参数宁可保守不可激进 -- property namemaximumPoolSize value20/ !-- 并发用户数×2100人系统设20足够 -- property nameminimumIdle value5/ !-- 空闲连接保底数防冷启动慢 -- property nameconnectionTimeout value30000/ !-- 30秒获取连接超时 -- property nameidleTimeout value600000/ !-- 10分钟空闲连接回收 -- property namemaxLifetime value1800000/ !-- 30分钟连接最大存活防MySQL wait_timeout踢出 -- !-- 关键必须开启prepared statement缓存 -- property namedataSourceProperties props prop keycachePrepStmtstrue/prop prop keyprepStmtCacheSize250/prop prop keyprepStmtCacheSqlLimit2048/prop /props /property /bean血泪经验maxLifetime必须小于MySQL的wait_timeout默认8小时否则连接池会复用已失效连接报错Communications link failure。线上MySQL务必执行SHOW VARIABLES LIKE wait_timeout;并据此调整。4. POI生成Word与Excel不是调用XSSFWorkbook.write()就完事而是让导出结果能直接打印盖章办公系统里“导出”不是锦上添花而是刚需。领导要拿Excel去财务报销HR要拿Word版《员工手册》去盖章——这意味着导出文件必须保留样式、支持中文、不乱码、图表可编辑、页眉页脚完整。POI 5.x系列对此有质变提升但配置陷阱极多。4.1 Word导出用XWPFDocument生成带样式的正式文件需求导出《会议纪要》含标题黑体小二、参会人列表表格、讨论内容正文段落、页脚“OA系统生成”。public void exportMeetingMinutes(Long meetingId, HttpServletResponse response) throws IOException { // 1. 查询数据省略Service调用 Meeting meeting meetingService.getById(meetingId); // 2. 创建文档 XWPFDocument doc new XWPFDocument(); // 3. 设置标题关键必须用XWPFParagraph不能直接setText XWPFParagraph titlePara doc.createParagraph(); titlePara.setAlignment(ParagraphAlignment.CENTER); // 居中 XWPFRun titleRun titlePara.createRun(); titleRun.setText(会议纪要); titleRun.setFontSize(22); // 小二号≈22磅 titleRun.setBold(true); titleRun.setFontFamily(微软雅黑); // 4. 插入表格参会人 XWPFTable table doc.createTable(1, 3); table.getRows().get(0).getCell(0).setText(姓名); table.getRows().get(0).getCell(1).setText(部门); table.getRows().get(0).getCell(2).setText(职务); for (Attendee a : meeting.getAttendees()) { XWPFTableRow row table.createRow(); row.getCell(0).setText(a.getName()); row.getCell(1).setText(a.getDept()); row.getCell(2).setText(a.getPosition()); } // 5. 设置页脚关键必须先创建页脚再添加段落 CTP ctp CTP.Factory.newInstance(); XWPFParagraph footerPara new XWPFParagraph(ctp, doc); footerPara.setAlignment(ParagraphAlignment.CENTER); XWPFRun footerRun footerPara.createRun(); footerRun.setText(OA系统生成 new SimpleDateFormat(yyyy年MM月dd日).format(new Date())); footerRun.setFontSize(10); footerRun.setFontFamily(宋体); // 添加页脚到文档 XWPFHeaderFooterPolicy policy doc.getHeaderFooterPolicy(); if (policy null) policy doc.createHeaderFooterPolicy(); policy.createFooter(XWPFHeaderFooterPolicy.DEFAULT, footerPara); // 6. 输出响应 response.setContentType(application/vnd.openxmlformats-officedocument.wordprocessingml.document); response.setHeader(Content-Disposition, attachment; filename会议纪要_ meetingId .docx); doc.write(response.getOutputStream()); }参数说明XWPFRun.setFontFamily(微软雅黑)必须用中文字体名不能用SimSun宋体等英文名否则Linux服务器上渲染为方块footerPara必须通过policy.createFooter()注入直接doc.createFooter()无效。4.2 Excel导出用SXSSFWorkbook解决大数据量OOM但必须关掉自动列宽需求导出全部报销单可能上万条要求金额列右对齐、日期列格式化、列宽自适应。public void exportAllReimbursements(HttpServletResponse response) throws IOException { ListReimbursement list reimbursementService.findAll(); // 关键用SXSSFWorkbook流式写入非XSSFWorkbook SXSSFWorkbook workbook new SXSSFWorkbook(1000); // 每1000行刷盘一次 Sheet sheet workbook.createSheet(报销单汇总); // 创建表头 Row headerRow sheet.createRow(0); String[] headers {编号, 申请人, 金额(元), 日期, 状态}; for (int i 0; i headers.length; i) { Cell cell headerRow.createCell(i); cell.setCellValue(headers[i]); // 设置表头样式粗体、居中 CellStyle headerStyle workbook.createCellStyle(); Font font workbook.createFont(); font.setBold(true); headerStyle.setFont(font); headerStyle.setAlignment(HorizontalAlignment.CENTER); cell.setCellStyle(headerStyle); } // 写入数据 for (int i 0; i list.size(); i) { Row row sheet.createRow(i 1); Reimbursement r list.get(i); row.createCell(0).setCellValue(r.getId()); row.createCell(1).setCellValue(r.getUserName()); // 金额列右对齐货币格式 Cell amountCell row.createCell(2); amountCell.setCellValue(r.getAmount()); CellStyle amountStyle workbook.createCellStyle(); amountStyle.setAlignment(HorizontalAlignment.RIGHT); DataFormat format workbook.createDataFormat(); amountStyle.setDataFormat(format.getFormat(#,##0.00)); amountCell.setCellStyle(amountStyle); // 日期列格式化 Cell dateCell row.createCell(3); dateCell.setCellValue(r.getApplyDate()); CellStyle dateStyle workbook.createCellStyle(); dateStyle.setDataFormat(format.getFormat(yyyy-mm-dd)); dateCell.setCellStyle(dateStyle); // 状态列中文映射 Cell statusCell row.createCell(4); statusCell.setCellValue(r.getStatus() 0 ? 待审 : r.getStatus() 1 ? 通过 : 驳回); } // 关键禁用自动列宽否则SXSSFWorkbook会OOM // sheet.autoSizeColumn(i) 绝对不能调用 // 手动设置列宽单位是1/256字符宽度 sheet.setColumnWidth(0, 20 * 256); // 编号列20字符 sheet.setColumnWidth(1, 30 * 256); // 姓名列30字符 sheet.setColumnWidth(2, 15 * 256); // 金额列15字符 sheet.setColumnWidth(3, 12 * 256); // 日期列12字符 sheet.setColumnWidth(4, 10 * 256); // 状态列10字符 // 输出 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filename报销单汇总.xlsx); workbook.write(response.getOutputStream()); workbook.dispose(); // 必须调用释放临时文件 }血泪经验SXSSFWorkbook的autoSizeColumn()会将整列数据加载到内存计算宽度导致OOM。必须手动setColumnWidth()。workbook.dispose()不调用会导致/tmp/poi-*临时文件堆积占满磁盘。5. 避坑指南SSMMySQLPOI组合下90%开发者踩过的5个深坑这些不是理论问题而是我在三个客户现场亲手修复的故障。每个都曾导致系统上线延期或用户投诉。5.1 现象MySQL报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock原因MySQL 5.7默认socket路径是/var/lib/mysql/mysql.sock但Java驱动默认找/tmp/mysql.sock。尤其在CentOS 7上mysqld服务启动后socket文件位置与配置不一致。解决查看MySQL实际socket路径sudo mysql -u root -p -e SHOW VARIABLES LIKE socket;在JDBC URL中显式指定jdbc:mysql://localhost:3306/oa_system?...socket/var/lib/mysql/mysql.sock或修改MySQL配置/etc/my.cnf[mysqld] socket/var/lib/mysql/mysql.sock [client] socket/var/lib/mysql/mysql.sock5.2 现象Word导出后中文显示为方块或Linux服务器上字体丢失原因POI在Linux上找不到中文字体回退到默认字体通常是DejaVu Sans不支持中文。解决将Windows的simsun.ttc宋体或msyh.ttc微软雅黑复制到Linux/usr/share/fonts/目录执行sudo mkfontscale sudo mkfontdir sudo fc-cache -fv刷新字体缓存Java启动参数加-Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-85.3 现象审批流中Transactional注解失效部分节点写入成功、部分失败数据不一致原因事务管理器未正确注入到Service层。常见于applicationContext.xml中tx:annotation-driven未配置transaction-manager属性或bean idtransactionManager的class写错如用了DataSourceTransactionManager但实际配的是JpaTransactionManager。解决确认applicationContext.xml中有bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/Service实现类上必须用Transactional不能只在接口上JDK代理限制方法不能是private或finalCGLIB代理限制5.4 现象Excel导出后数字列显示为科学计数法如1382000000显示为1.38E09原因POI默认将长数字识别为数值类型Excel自动格式化。解决方案1推荐强制设为文本类型Cell cell row.createCell(i); cell.setCellType(CellType.STRING); cell.setCellValue(String.valueOf(number));方案2用DataFormat设为文本格式CellStyle textStyle workbook.createCellStyle(); textStyle.setDataFormat(workbook.createDataFormat().getFormat()); cell.setCellStyle(textStyle);5.5 现象Tomcat重启后用户登录态丢失需重新登录原因Session默认存储在Tomcat内存中重启即清空。办公系统要求会话持久化。解决启用Tomcat Session持久化修改conf/context.xmlManager classNameorg.apache.catalina.session.PersistentManager saveOnRestarttrue Store classNameorg.apache.catalina.session.FileStore directory/opt/tomcat/session/ /Manager或更优方案改用Redis存储Session需引入tomcat-redis-session-managerManager classNamecom.orangefunction.tomcat.redissessions.RedisSessionManager hostlocalhost port6379 database0 maxInactiveInterval60/注意此方案需额外部署Redis但可靠性远高于文件存储。6. 让系统真正“可用”的3个硬核技巧从能跑通到能交付交付给业务部门的不是WAR包而是“打开浏览器就能用、填完表单就能走流程、导出文件就能盖章”的确定性。这需要超越代码层面的工程动作。6.1 把MySQL配置做成可外部覆盖的profile而不是写死在XML里生产环境绝不能让DB密码明文躺在applicationContext.xml里。正确做法是用Maven profile properties文件在src/main/resources/config/下建db-prod.propertiesjdbc.urljdbc:mysql://10.0.1.100:3306/oa_system?... jdbc.usernameoa_app jdbc.passwordStrongPassw0rd!applicationContext.xml中用占位符context:property-placeholder locationclasspath:/config/db-${env}.properties/ bean iddataSource ... property namejdbcUrl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean打包时指定环境mvn clean package -Denvprod这样运维只需替换db-prod.properties无需动代码。比Jenkins参数化构建更可控。6.2 用JUnitH2内存数据库做Service层单元测试隔离MySQL依赖每个Service方法必须有对应测试验证业务逻辑而非数据库连接。例如LeaveApplyService.submit()应测试提交后oa_leave_apply.status是否为0是否生成了正确数量的oa_approval_flow记录是否抛出BusinessException当请假天数超限RunWith(SpringJUnit4ClassRunner.class) ContextConfiguration(locations {classpath:/config/applicationContext.xml}) Transactional public class LeaveApplyServiceTest { Autowired private LeaveApplyService service; Autowired private LeaveApplyMapper mapper; // MyBatis Mapper Test public void testSubmitValidLeave() { LeaveApply apply new LeaveApply(); apply.setUserId(1L); apply.setStartDate(LocalDate.of(2024, 6, 1)); apply.setEndDate(LocalDate.of(2024, 6, 3)); // 3天 apply.setReason(事假); service.submit(apply); // 调用被测方法 // 验证数据库状态 LeaveApply saved mapper.selectById(apply.getId()); assertEquals(0, saved.getStatus().intValue()); // 状态为待审 // 验证审批流生成假设部门领导为approver_id2 ListApprovalFlow flows flowMapper.selectByApplyId(apply.getId()); assertEquals(1, flows.size()); assertEquals(2L, flows.get(0).getApproverId().longValue()); } }关键Transactional确保测试后自动回滚不影响其他测试H2内存库由Spring自动创建无需本地MySQL实例。6.3 制作一键部署脚本3个文件搞定从零到运行交付物不是“请按文档部署”而是deploy.sh——让运维双击就能跑deploy.sh核心逻辑#!/bin/bash # 检查Java、MySQL、Tomcat if ! command -v java /dev/null; then echo Java not installed; exit 1; fi if ! command -v mysql /dev/null; then echo MySQL not installed; exit 1; fi # 创建数据库 mysql -uroot -p123456 -e CREATE DATABASE IF NOT EXISTS oa_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入初始化SQL mysql -uroot -p123456 oa_system ./sql/init.sql # 配置Tomcat复制WAR、修改server.xml端口 cp ./target/oa-system.war $TOMCAT_HOME/webapps/ sed -i s/8080/8081/g $TOMCAT_HOME/conf/server.xml # 启动 $TOMCAT_HOME/bin/startup.sh echo OA系统已启动访问 http://$(hostname -I | awk {print $1}):8081/oa-system配套文件sql/init.sql含建库、建表、插基础数据的完整SQLconfig/db-prod.properties生产环境数据库配置README.md仅3行说明“1. 安装JDK8、MySQL5.7、Tomcat8.52. 设置环境变量TOMCAT_HOME3. 执行./deploy.sh”这就是我交付给客户的标准——没有“可能”“建议”“通常”只有确定的输入和确定的输出。当业务方说“我们IT只会复制粘贴”这套方案依然能跑通。技术的价值不是炫技而是把不确定性压缩到最小。我带过的所有毕业设计学生最后都卡在“怎么让导师在自己电脑上一键跑起来”。后来我把deploy.sh和README.md塞进他们项目根目录导师双击运行看到登录页就签字通过。这比讲一百遍Spring生命周期都有力。希望帮到你。本文还有配套的精品资源点击获取
返回列表