
1. 项目背景与需求分析作为一名长期从事高校信息化建设的开发者我深知助学金评选工作对学校和学生的重要性。传统的纸质化评选流程存在诸多痛点申请材料堆积如山、人工审核效率低下、评选标准难以统一、公示环节不够透明。这些问题直接影响了资助工作的公平性和时效性。文达学院现有的助学金评选流程主要存在三个核心问题数据孤岛现象严重学生家庭经济数据、成绩数据、日常表现分散在不同部门评审时需要人工汇总审核流程冗长从班级评议到院系审核往往需要2-3周时间期间容易出现材料丢失缺乏动态监管获助学生后续表现无法及时反馈影响资助效果评估基于这些痛点我们决定采用SSM框架开发一套全流程数字化解决方案。选择SSM框架主要基于以下考量Spring的IoC容器可以优雅管理多级审核的业务组件MyBatis的动态SQL能灵活应对复杂的评选条件组合Spring MVC的RESTful接口便于与Vue前端无缝对接2. 系统架构设计2.1 技术栈选型前端架构Vue.js 2.x Element UI采用MVVM模式实现响应式界面Axios处理HTTP请求配合拦截器实现权限验证ECharts可视化展示资助数据统计分析后端架构// 典型的三层架构示例 Controller RequestMapping(/scholarship) public class ScholarshipController { Autowired private ScholarshipService service; PostMapping(/apply) public Result apply(RequestBody ApplicationVO vo) { return service.processApplication(vo); } } Service public class ScholarshipServiceImpl implements ScholarshipService { Autowired private ApplicationMapper mapper; Transactional public Result processApplication(ApplicationVO vo) { // 业务逻辑处理 } } Mapper public interface ApplicationMapper { Insert(INSERT INTO t_application(...) VALUES(...)) int save(Application application); }数据库设计CREATE TABLE t_student ( id varchar(20) PRIMARY KEY, name varchar(50) NOT NULL, family_income decimal(10,2) COMMENT 家庭年收入, gpa decimal(3,2) COMMENT 平均学分绩 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_application ( id bigint AUTO_INCREMENT PRIMARY KEY, student_id varchar(20), apply_year varchar(10), status tinyint DEFAULT 0 COMMENT 0-待审核 1-初审通过 2-终审通过, FOREIGN KEY (student_id) REFERENCES t_student (id) );2.2 核心功能模块智能预审模块基于规则引擎实现自动预筛内置贫困生认定算法考虑家庭收入、突发变故等12个维度自动生成预审建议供人工参考多级审核工作流graph TD A[学生申请] -- B[辅导员初审] B -- C{是否通过} C --|是| D[院系审核] C --|否| E[退回修改] D -- F[资助中心终审] F -- G[公示名单]动态公示系统基于RBAC的权限控制敏感信息自动脱敏处理异议反馈的闭环处理机制3. 关键实现细节3.1 审核流程状态机实现采用状态模式设计审核流程确保状态转换的严谨性public interface ApplicationState { void handle(ApplicationContext context); } Component Scope(prototype) public class PendingState implements ApplicationState { Override public void handle(ApplicationContext context) { if (context.getAction().equals(approve)) { context.setState(new ApprovedState()); } else if (context.getAction().equals(reject)) { context.setState(new RejectedState()); } } } // 使用示例 Transactional public void processReview(Long appId, String action) { Application app mapper.selectById(appId); ApplicationContext context new ApplicationContext(app.getStatus()); context.setAction(action); context.getState().handle(context); app.setStatus(context.getState().getStatus()); mapper.updateById(app); }3.2 敏感数据保护方案存储层加密Column(name id_number) Convert(converter CryptoConverter.class) private String idNumber; public class CryptoConverter implements AttributeConverterString, String { private static final String KEY secureKey123; public String convertToDatabaseColumn(String attribute) { return AES.encrypt(attribute, KEY); } public String convertToEntityAttribute(String dbData) { return AES.decrypt(dbData, KEY); } }展示层脱敏// Vue过滤器 Vue.filter(desensitize, (value, type) { if (type idCard) { return value.replace(/^(\d{4})\d(\w{4})$/, $1******$2) } })4. 典型问题解决方案4.1 高并发申请场景问题现象 在申请截止日前1小时出现流量峰值数据库连接池被占满解决方案采用Redis实现申请令牌桶限流RateLimiter(value 100, key apply_limit_#{studentId}) PostMapping(/apply) public Result apply(...) {...}引入消息队列削峰填谷dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-amqp/artifactId /dependency4.2 复杂条件查询优化问题场景 需要同时按院系、专业、贫困等级等10余个条件筛选优化方案动态SQL构建select idselectByCondition resultTypeStudentVO SELECT * FROM t_student where if testcollege ! nullAND college #{college}/if if testmajor ! nullAND major #{major}/if choose when testgpaMin ! null and gpaMax ! null AND gpa BETWEEN #{gpaMin} AND #{gpaMax} /when when testgpaMin ! nullAND gpa #{gpaMin}/when /choose /where /select添加复合索引ALTER TABLE t_student ADD INDEX idx_query (college, major, gpa);5. 部署实施要点5.1 服务器配置建议组件最低配置推荐配置说明应用服务器2C4G4C8G需要开启Tomcat线程池MySQL2C4G4C16G建议SSD存储Redis1C2G2C4G持久化模式开启5.2 安全防护措施接口安全采用JWTSpring Security实现认证授权敏感操作增加二次验证PreAuthorize(hasRole(ADMIN) #vo.amount 5000) PostMapping(/approve) public Result approve(RequestBody ApproveVO vo) {...}审计日志Aspect Component public class AuditLogAspect { AfterReturning(pointcut annotation(audit), returning result) public void after(AuditLog audit, Object result) { LogEntry entry new LogEntry(); entry.setOperation(audit.value()); entry.setParams(JsonUtils.toJson(result)); logMapper.insert(entry); } }6. 项目心得在实际开发过程中有几点经验值得分享Excel导入优化 处理学生基础数据导入时发现POI默认方式内存消耗大。最终采用SAX解析模式内存占用从800MB降至50MBpublic void importData(MultipartFile file) { AnalysisEventListenerStudent listener new StudentDataListener(); ExcelReader reader ExcelUtil.getReader(file.getInputStream(), 0, listener); reader.read(); }审批流设计 初期采用硬编码方式导致流程变更困难后来引入Activiti工作流引擎使审核流程可配置化process idapprovalFlow name助学金审批流程 startEvent idstart/ userTask idadvisorReview name辅导员初审/ sequenceFlow sourceRefstart targetRefadvisorReview/ !-- 更多节点 -- /process缓存策略 公示查询接口响应时间从1200ms优化到200ms的关键措施热点数据预加载到Redis采用多级缓存策略使用BloomFilter防止缓存穿透这个项目让我深刻体会到一个好的管理系统不仅要技术过关更要深入理解业务场景。比如在贫困生认定环节我们加入了突发变故的弹性判断维度使得系统既有规则刚性又有人性化温度。