ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+Vue校园疫情防控系统:企业级全栈开发实战解析

SpringBoot+Vue校园疫情防控系统:企业级全栈开发实战解析 简介这是一套面向计算机专业本科生的毕业设计级校园疫情防控管理系统源码基于Spring Boot与Vue实现前后端分离架构解决高校常态化防疫中师生健康申报、轨迹追踪、风险预警与数据统计等核心管理需求。资源包共424个文件含101个Java后端逻辑文件、60个Vue前端组件、161个SVG图标资源、23个JPG/PNG素材及关键SQL建库脚本、YML配置、BAT一键部署脚本和论文文档整体30.18MB结构清晰便于模块化学习与二次开发。已有2668人下载学习适合毕设选题参考与全栈技术实践——开箱即用所有代码经严格调试可直接运行配套功能文档详述业务流程BAT脚本支持快速安装、构建与启动降低环境配置门槛论文.doc提供完整撰写框架助力答辩材料准备。1. 项目概述与核心价值最近在整理过往的项目资料翻到了一个前两年做的校园疫情防控系统项目代号是“springboot9291”。这是一个典型的基于Spring Boot和Vue的前后端分离项目当时是为了应对校园常态化管理需求而开发的。虽然现在疫情的大环境已经变化但这个项目里涉及的技术栈选型、业务逻辑设计以及应对高并发场景的解决方案对于想学习企业级全栈开发的朋友来说依然有很高的参考价值。它不是一个简单的增删改查CRUDdemo而是包含了从权限控制、数据可视化、实时状态同步到文件处理等一系列实战要点。简单来说这个系统主要服务于高校的师生和管理员核心目标是实现人员健康信息的精准上报、校内场所的出入管控、疫情数据的实时统计与可视化以及异常情况的快速预警。对于开发者而言通过复现或学习这个项目你不仅能巩固Spring Boot和Vue的核心技术更能接触到诸如WebSocket实时通信、ECharts数据可视化、JWT无状态认证、POI报表导出等在实际工作中高频使用的技能点。接下来我就把这个项目的设计思路、关键技术实现以及我踩过的一些“坑”详细拆解一下。2. 技术栈选型与架构设计解析2.1 为什么是Spring Boot Vue这是当时经过综合评估后的选择核心考量是高效开发与清晰解耦。后端选择Spring Boot理由很直接约定大于配置它能快速搭建一个稳健的后端服务避免了传统Spring项目繁琐的XML配置。像数据库连接池如HikariCP、内嵌Web服务器Tomcat都开箱即用让我们能聚焦业务逻辑。生态丰富Spring Security用于做权限控制Spring Data JPA或MyBatis-Plus操作数据库Spring Cache做缓存还有一大堆Starter可以轻松集成Redis、RabbitMQ等中间件生态成熟遇到问题社区资料也多。易于维护和部署通过SpringBootApplication一个注解就能启动配合Maven或Gradle打包成一个可执行的JAR文件部署到服务器上非常方便无论是Windows还是Linux环境。前端选择Vue.js特别是Vue 2.x根据项目时间推断则是基于以下考虑渐进式与易上手对于当时团队里前端水平参差不齐的情况Vue的学习曲线相对平缓。它的核心库只关注视图层容易集成到现有项目也可以通过Vue Router、Vuex、Axios等官方维护的库逐步扩展成完整的前端解决方案。组件化开发校园疫情防控系统的前端页面有很多重复的模块比如信息填报表单、数据表格展示、图表卡片等。Vue的组件化能力能让这些模块被高效复用极大提升了开发效率和代码的可维护性。强大的生态系统配合Element UI或Ant Design Vue这类成熟的UI组件库可以快速搭建出美观且交互一致的管理后台界面省去了大量从零编写CSS和基础组件的时间。前后端通过RESTful API进行通信后端只负责提供数据接口前端负责渲染和交互这种分离模式让前后端开发可以并行也便于后续独立升级和扩展。2.2 整体架构设计思路系统的架构可以概括为“前后端分离 模块化业务层”。后端采用经典的三层架构Controller-Service-Dao前端采用基于Vue Router和Vuex的单页面应用SPA结构。后端模块划分权限认证模块基于Spring Security和JWTJSON Web Token实现。用户登录后后端生成一个加密的Token返回给前端前端后续请求都在HTTP Header中携带此Token。后端通过过滤器Filter或拦截器Interceptor验证Token的有效性和权限实现无状态的认证与授权。这样设计的好处是服务器无需保存会话状态易于水平扩展。核心业务模块这是系统的重心包括学生/教职工健康上报管理每日体温、行程、健康码状态上报。校内场所如图书馆、食堂、教学楼的出入扫码登记与校验。疫情数据统计与分析如各院系健康异常人数、出入记录汇总。通知公告发布与管理。数据持久层使用MyBatis-Plus作为ORM框架它是对MyBatis的增强提供了通用的CRUD方法避免了大量简单SQL的编写。数据库选用MySQL对于需要快速查询的统计类数据会考虑使用Redis做缓存。文件处理模块负责处理健康码图片、行程码截图的上传与存储以及将统计数据导出为Excel或PDF报表。这里通常使用Apache POI或EasyExcel来处理Excel文件存储可以选择本地磁盘、FastDFS或云存储OSS。实时通信模块可选但重要对于需要实时推送通知如紧急公告或展示实时在场人数的场景引入了WebSocket。Spring Boot提供了对WebSocket的Starter支持可以相对简单地实现服务端向指定客户端推送消息。前端项目结构src/ ├── api/ # 所有后端接口的axios封装 ├── assets/ # 静态资源 ├── components/ # 公共组件如通用搜索框、图表卡片 ├── router/ # Vue Router路由配置 ├── store/ # Vuex状态管理 ├── utils/ # 工具函数如日期格式化、请求拦截器 └── views/ # 页面视图组件这种结构清晰地将接口调用、状态管理、路由和UI组件分离符合Vue官方推荐的最佳实践。3. 核心功能模块实现细节3.1 人员健康信息上报与审批流这是最基础也是最核心的功能。学生或教职工每日通过前端页面填写表单提交体温、是否咳嗽、健康码颜色、当前所在位置等信息。后端关键实现实体设计设计一个HealthReport实体类包含用户ID、上报日期、体温、症状、健康码状态枚举类型如GREEN, YELLOW, RED、地理位置等字段。通过JPA注解与数据库表映射。接口防重提交为防止用户短时间内重复提交在Service层需要做校验。一种简单有效的方式是使用Redis记录用户当天是否已上报。例如用户提交时以health:report:${userId}:${today}为key设置一个有过期时间如到当天23:59:59的值。下次提交前先检查该key是否存在。// 伪代码示例 public String submitReport(HealthReport report) { String today LocalDate.now().toString(); String key health:report: report.getUserId() : today; if (redisTemplate.hasKey(key)) { throw new BusinessException(今日已上报请勿重复提交); } // 保存上报记录到数据库 healthReportRepository.save(report); // 设置Redis标记过期时间到今晚12点 redisTemplate.opsForValue().set(key, 1, Duration.between(LocalDateTime.now(), LocalDateTime.now().withHour(23).withMinute(59).withSecond(59))); return 上报成功; }审批流程对于健康码为“红码”或“黄码”的上报系统会自动生成一条待处理的异常记录进入管理员审批流程。这里可以设计一个简单的状态机状态包括PENDING待处理、PROCESSING处理中、RESOLVED已解决等。管理员在后台可以查看详情并更新处理状态和备注。前端关键实现表单验证使用Element UI的el-form组件结合rules属性进行前端验证。例如体温必须是数字且在合理范围内如35.0-42.0健康码为必选项。地理位置获取利用浏览器的Geolocation API获取用户当前位置需用户授权并转换为具体地址。可以集成腾讯地图或高德地图的JavaScript API进行逆地理编码将经纬度转换为省市区街道信息提升用户体验。图片上传健康码、行程码截图的上传使用Element UI的el-upload组件。需要注意限制文件格式如.jpg, .png和大小如不超过2MB。上传前可进行客户端预览上传成功后后端返回文件的访问URL前端将其URL保存在表单数据中一并提交。注意文件上传的安全至关重要。后端必须对上传的文件进行严格校验包括文件头校验防止伪装文件、病毒扫描如果有条件、以及重命名存储避免原始文件名可能带来的安全问题。绝对不要将用户上传的文件直接以原始文件名保存在可访问目录下。3.2 校内场所出入扫码与核验这个功能模拟了现实中的场所码。每个场所如图书馆入口有一个唯一的二维码二维码中编码了该场所的ID。流程如下生成场所码后端提供一个接口根据场所ID生成一个对应的二维码图片可以使用Google的ZXing库。该二维码信息可以是一个包含场所ID和基础信息的短链接或者直接是一个携带参数的URL如https://your-domain.com/checkin?locationId123。用户扫码用户使用微信或浏览器扫描二维码跳转到系统的签到页面页面自动获取URL中的locationId。提交签到前端页面获取到locationId后结合当前登录用户的ID从本地存储的Token中解析或通过接口获取调用后端的签到接口。后端核验后端接口收到请求后执行以下逻辑验证用户Token获取用户当前的健康状态从最新的上报记录或缓存中获取。检查该用户在当前场所是否已有“未签出”的记录防止重复进入。检查用户健康码状态。如果为绿码则创建一条AccessRecord记录状态为“已进入”并返回成功信息。如果为红码或黄码则拒绝进入并可能触发告警通知管理员。签出用户离开时同样扫描一个“签出”二维码或在前端页面手动点击“签出”后端更新对应的AccessRecord记录状态为“已离开”并记录离开时间。技术要点并发控制在高峰时段如上下课同一场所可能有多人同时扫码。在创建“进入”记录时需要保证对同一用户在同一场所的并发操作是安全的。可以在数据库层面使用唯一索引用户ID 场所ID 状态‘已进入’或者在Service层使用分布式锁如基于Redis的锁来防止同一用户生成多条“在场”记录。离线备用方案考虑到网络可能不稳定前端在提交签到/签出请求时如果失败应将数据暂存到本地如LocalStorage并提示用户待网络恢复后自动重试提交。3.3 数据统计可视化与报表导出管理员最关心的就是数据看板。我们需要将分散的上报、出入记录汇总成直观的图表和报表。后端统计实现定时任务聚合对于一些实时性要求不极高的统计数据如各学院过去7天的平均上报率、异常人数趋势可以使用Spring Boot的Scheduled注解创建定时任务在每天凌晨低峰期执行将计算结果聚合到专门的统计表中。这样前端查询时直接查聚合表速度极快避免了对大量原始表的复杂联查。Component public class StatisticsTask { Autowired private ReportService reportService; Autowired private StatisticsDao statisticsDao; // 每天凌晨2点执行 Scheduled(cron 0 0 2 * * ?) public void aggregateDailyData() { LocalDate yesterday LocalDate.now().minusDays(1); // 调用Service计算昨天的各类统计数据 DailyStats stats reportService.calculateDailyStats(yesterday); // 存入统计表 statisticsDao.save(stats); } }实时数据查询对于需要实时查看的数据如当前在校人数则直接查询AccessRecord表中状态为“已进入”且未签出的记录数。这类查询需要确保数据库相关字段有索引例如在status和check_out_time字段上建立复合索引。前端可视化实现 使用ECharts这个强大的图表库。在Vue中可以封装一个通用的图表组件。安装与引入npm install echarts vue-echarts。封装组件创建一个Chart.vue组件接收option图表配置项和theme主题作为props。按需引入为了减小打包体积在utils/echarts.js文件中按需引入需要的ECharts模块。// utils/echarts.js import * as echarts from echarts/core; import { BarChart, LineChart, PieChart } from echarts/charts; import { TitleComponent, TooltipComponent, GridComponent, LegendComponent } from echarts/components; import { CanvasRenderer } from echarts/renderers; import echarts/theme/macarons; // 引入一个主题 echarts.use([ BarChart, LineChart, PieChart, TitleComponent, TooltipComponent, GridComponent, LegendComponent, CanvasRenderer ]); export default echarts;在页面中使用在数据看板页面通过axios获取后端统计接口的数据然后动态生成ECharts所需的option配置对象传递给图表组件。报表导出 管理员经常需要将统计数据导出为Excel。后端使用Apache POI或更高效的EasyExcel来生成Excel文件。定义数据模型创建一个与Excel表头对应的Java类DTO使用EasyExcel的ExcelProperty注解标注字段和表头名。编写导出接口Controller中提供一个接口根据查询条件从数据库或统计表中获取数据列表然后通过EasyExcel的EasyExcel.write(response.getOutputStream(), DataDTO.class).sheet(“sheet1”).doWrite(dataList)方法直接将数据流写入HttpServletResponse的输出流中。前端触发前端通过一个按钮调用导出接口。注意对于数据量大的导出最好做成异步任务后端生成文件后提供下载链接避免请求超时。4. 关键技术难点与解决方案实录4.1 权限管理的精细化控制系统涉及多种角色超级管理员、院系管理员、辅导员、学生/教职工。他们的数据查看和操作权限差异很大。例如学生只能看自己的信息和上报辅导员能看到所带班级所有学生的信息院系管理员能看到本院系所有数据。解决方案基于RBAC角色-权限模型结合数据权限过滤。菜单/接口权限使用Spring Security。我们将每个API接口的访问路径与所需的权限标识如health:report:view关联。用户登录后其角色所拥有的权限标识会被加载。在访问接口时通过PreAuthorize(“hasAuthority(‘health:report:view’)”)注解或自定义的权限校验逻辑进行拦截。数据行级权限这是难点。例如查询“健康上报记录”列表时不同角色看到的数据范围不同。我们通过在Service层的查询方法中动态添加查询条件WHERE子句来实现。实现思路创建一个DataScopeAspect切面在查询方法执行前根据当前登录用户的角色和部门信息动态修改查询参数或SQL。例如辅导员角色的用户会自动在其查询条件上附加class_id IN (他所管理的班级ID列表)。工具选择如果使用MyBatis-Plus可以利用其提供的TenantLineInnerInterceptor多租户插件的思路进行改造实现数据范围拦截。也可以直接使用MyBatis的Plugin机制拦截Executor的查询方法重写SQL。实操心得数据权限的配置最好做到可配置化。我们将“数据权限规则”如“本人”、“本部门”、“本部门及下属部门”、“全部”作为元数据存储在数据库中。系统管理员可以在后台为不同的角色分配不同的数据权限规则这样更加灵活无需修改代码。4.2 实时数据推送与状态同步在管理员后台我们希望“今日实时在校人数”这个数字能自动更新无需手动刷新页面。解决方案WebSocket 发布/订阅模式。服务端实现使用Spring Boot的spring-boot-starter-websocket。创建一个WebSocketConfig配置类启用WebSocket并定义一个WebSocketHandler来处理连接、消息和关闭事件。我们通常使用STOMP子协议因为它提供了更高级的消息模式如发布/订阅。频道设计我们定义一个广播频道例如/topic/onlineCount用于向所有订阅了该频道的客户端广播当前在线人数。当有用户成功签到或签出时后端业务逻辑会触发一个事件事件监听器计算最新的在线人数并通过SimpMessagingTemplate发送到/topic/onlineCount频道。Service public class CheckInService { Autowired private SimpMessagingTemplate messagingTemplate; Autowired private AccessRecordService recordService; public void handleCheckIn(CheckInRequest request) { // 1. 处理签到逻辑保存记录... // 2. 计算新的在线人数 long currentOnlineCount recordService.countCurrentOnline(); // 3. 广播消息 messagingTemplate.convertAndSend(“/topic/onlineCount”, currentOnlineCount); } }前端订阅前端使用SockJS客户端和STOMP.js库连接WebSocket端点。在Vue组件的mounted生命周期中建立连接并订阅/topic/onlineCount频道。当收到消息时更新页面上的对应数据。mounted() { this.connectWebSocket(); }, methods: { connectWebSocket() { const socket new SockJS(‘/your-websocket-endpoint’); const stompClient Stomp.over(socket); stompClient.connect({}, (frame) { stompClient.subscribe(‘/topic/onlineCount’, (message) { this.onlineCount JSON.parse(message.body); }); }); } }注意事项WebSocket连接是长连接需要考虑断线重连机制。前端可以在连接断开时触发onclose事件尝试定时重连。同时对于非关键性实时数据如在线人数也可以采用降级方案即WebSocket失败后前端改为定时轮询setInterval后端的一个HTTP接口来获取数据。4.3 大文件如批量导入的上传与处理有时管理员需要批量导入学生信息或历史数据这涉及到Excel文件的上传与解析。解决方案分片上传 后端流式解析。前端分片对于超过一定大小如10MB的文件前端使用库如simple-uploader.js或自行实现将文件切割成多个小块chunk依次上传。每上传一个分片都携带文件MD5用于标识唯一文件、当前分片索引、总分片数等信息。后端接收与合并后端提供一个接口接收分片。接口逻辑根据文件MD5在服务器临时目录创建一个文件夹。将上传的分片文件以索引号命名存入该文件夹。检查是否所有分片都已上传完成通过对比已接收分片数和总分片数。如果全部完成则按索引顺序将所有分片合并成完整的文件。流式解析合并成完整文件后使用EasyExcel的read方法并注册一个AnalysisEventListener监听器来逐行读取数据。这种方式不会将整个文件加载到内存而是以流的方式处理非常适合大文件。EasyExcel.read(file.getInputStream(), StudentImportDTO.class, new AnalysisEventListenerStudentImportDTO() { Override public void invoke(StudentImportDTO data, AnalysisContext context) { // 处理每一行数据可以在这里进行校验并批量存入数据库 batchList.add(data); if (batchList.size() 100) { saveBatch(batchList); // 批量保存 batchList.clear(); } } Override public void doAfterAllAnalysed(AnalysisContext context) { // 处理最后一批数据 if (!batchList.isEmpty()) { saveBatch(batchList); } } }).sheet().doRead();进度反馈后端在处理过程中可以将处理进度如已处理行数/总行数写入Redis以处理文件的MD5为key。前端可以轮询另一个接口来获取这个进度并实时显示在进度条上。5. 部署、运维与性能优化考量5.1 多环境配置与部署一个规范的项目需要区分开发dev、测试test、生产prod环境。Spring Boot通过application-{profile}.properties/yml文件来支持多环境配置。实践方案配置文件分离在resources目录下创建application-dev.yml开发环境、application-prod.yml生产环境。application.yml作为主配置文件使用spring.profiles.active属性来激活特定环境。# application.yml spring: profiles: active: activatedProperties # 这里会被Maven的profile动态替换Maven Profile在pom.xml中定义不同的profile配合spring-boot-maven-plugin的profiles配置在打包时替换占位符。profiles profile iddev/id properties activatedPropertiesdev/activatedProperties /properties activationactiveByDefaulttrue/activeByDefault/activation /profile profile idprod/id properties activatedPropertiesprod/activatedProperties /properties /profile /profiles打包命令mvn clean package -P prod将使用生产环境配置打包。前端环境变量Vue项目使用.env.development和.env.production文件来管理环境变量如API基础地址。通过VUE_APP_前缀定义变量在代码中通过process.env.VUE_APP_API_BASE_URL访问。容器化部署Docker编写Dockerfile和docker-compose.yml文件将后端Jar包、前端静态资源Nginx、MySQL、Redis等服务容器化。这能保证环境一致性极大简化部署流程。生产环境通常使用CI/CD工具如Jenkins、GitLab CI自动化完成构建、打包、部署到服务器的过程。5.2 数据库性能与缓存策略随着数据量增长数据库查询可能变慢。优化措施索引优化这是最有效的手段。通过分析慢查询日志MySQL的slow_query_log对频繁作为查询条件WHERE、连接JOIN和排序ORDER BY的字段建立索引。例如health_report表的user_id和report_date字段经常联合查询可以建立复合索引idx_user_date。查询优化**避免SELECT ***只查询需要的字段。合理使用JOIN明确关联关系避免多表关联时产生笛卡尔积。分页查询对于列表页务必使用分页。MyBatis-Plus提供了强大的分页插件能自动优化分页查询。引入缓存热点数据缓存使用Redis缓存变化不频繁但访问频繁的数据。例如学校的院系列表、班级列表。在Spring Boot中使用Cacheable注解可以轻松实现方法级缓存。Cacheable(value “departments”, key “‘all’“) public ListDepartment getAllDepartments() { return departmentMapper.selectList(null); // 第一次查数据库后续查缓存 }缓存更新策略更新或删除数据时使用CacheEvict注解清除对应的缓存保证数据一致性。分布式Session如果使用了Session虽然本项目用JWT但其他模块可能用到可以将Session存储到Redis实现多服务实例间的Session共享。5.3 前端性能与用户体验优化打包优化路由懒加载在Vue Router配置中使用动态import()语法来定义组件这样每个路由对应的组件会被打包成独立的块chunk只在访问该路由时才加载。const UserList () import(‘./views/user/UserList.vue’)公共代码抽离使用SplitChunksPluginVue CLI已默认配置将node_modules中的第三方库打包到单独的vendorchunk中利用浏览器缓存。Gzip压缩在Nginx配置中开启Gzip压缩可以显著减小传输文件体积。图片与静态资源优化对用户上传的图片在后端使用工具如Thumbnailator进行压缩和生成缩略图。将前端静态资源JS、CSS、图片部署到CDN加速不同地域用户的访问速度。错误监控与日志前端接入Sentry等前端监控平台自动捕获JavaScript运行时错误、资源加载失败等并上报到平台方便定位线上问题。后端使用SLF4J Logback记录日志合理设置日志级别INFO, ERROR。生产环境将日志收集到ELKElasticsearch, Logstash, Kibana或类似平台便于集中查询和分析。6. 常见问题排查与调试技巧在实际开发和部署中总会遇到各种问题。这里记录几个典型问题的排查思路。问题一前端调用后端接口出现CORS跨域错误。表现浏览器控制台报错Access-Control-Allow-Originheader is present on the requested resource。原因前端运行在localhost:8080后端运行在localhost:9090端口不同浏览器出于安全策略阻止了跨域请求。解决方案开发环境在Spring Boot后端配置全局CORS。创建一个配置类添加Configuration注解并定义一个WebMvcConfigurerBean来添加CORS映射。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(“/**“) // 对所有接口 .allowedOrigins(“http://localhost:8080”) // 允许的前端地址 .allowedMethods(“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”) .allowCredentials(true) .maxAge(3600); } }生产环境更常见的做法是在Nginx反向代理层解决。将前后端配置在同一个域名下通过Nginx的location路径规则进行转发这样就不存在跨域问题了。问题二Vue页面刷新后登录状态丢失Vuex数据清空。表现用户登录后刷新页面页面跳转回登录页。原因Vuex的状态存储在内存中页面刷新会重新初始化JavaScript环境导致状态丢失。解决方案结合本地持久化存储。用户登录成功后端返回Token后前端不仅将Token存入Vuex也同步存入localStorage或sessionStorage。在Vue应用的入口文件如main.js或根组件如App.vue的created钩子中尝试从localStorage读取Token。如果存在且有效可简单判断是否存在则将其提交commit到Vuex的state中并可能重新获取一次用户信息。在路由守卫router.beforeEach中判断Vuex中是否有用户登录状态或Token如果没有则跳转到登录页。问题三MyBatis-Plus分页查询总数total不准或性能慢。表现分页查询时total字段返回的值不对或者查询速度很慢。原因与排查SQL中有LEFT JOIN或GROUP BYMyBatis-Plus自动生成的COUNT语句可能很复杂导致性能低下或结果错误。需要自定义分页查询的COUNT语句。自定义了selectPage的查询Wrapper如果Wrapper中包含了复杂的自定义SQL片段也可能影响计数。解决方案在Service层或Mapper层使用MyBatis-Plus的Page对象时设置page.setOptimizeCountSql(false)来禁用优化或者直接提供一个自定义的SELECT COUNT(*)查询语句。Select(“SELECT COUNT(*) FROM health_report WHERE …你的条件…”) Long countByCustomCondition(Param(…) …); // 然后在分页查询中先查询总数再查询数据 PageHealthReport page new Page(current, size); page.setTotal(countByCustomCondition(…)); if (page.getTotal() 0) { page.setRecords(baseMapper.selectPageList(page, …)); } return page;问题四Spring Boot项目打包后读取src/main/resources下的文件失败。表现在IDE里运行正常打成JAR包后运行读取模板文件或配置文件时报FileNotFoundException。原因JAR包中的资源文件不是标准的文件系统路径不能直接用new File(“…”)来读取。解决方案使用Spring提供的ResourceLoader或者ClassPathResource来读取类路径下的资源。Autowired private ResourceLoader resourceLoader; // 或者 Resource resource new ClassPathResource(“templates/report_template.xlsx”); InputStream inputStream resource.getInputStream(); // 使用InputStream进行操作回顾这个校园疫情防控系统的开发最大的体会是一个成功的项目不仅仅是功能的堆砌更是对业务逻辑的深刻理解、对技术方案的合理选型以及对细节的持续打磨。从权限设计到数据统计从实时推送到大文件处理每一个环节都考验着开发者的综合能力。虽然项目的业务背景特殊但其背后用到的前后端分离架构、模块化设计思想、性能优化手段以及问题排查方法都是通用的、可迁移的宝贵经验。对于学习者而言不妨以这个项目为蓝本尝试替换或增加一些模块比如加入消息队列RabbitMQ来解耦耗时的报表生成任务或者尝试用Vue 3和Composition API重构前端都是非常好的进阶练习。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进