# 奖学金申请管理模块需求设计 v2.0 **版本**: v2.0 **更新时间**: 2025-01-01 **文档说明**: 基于班主任代学生申请模式的重新设计 --- ## 📋 1. 需求概述 ### 1.1 背景说明 基于实际业务需求调研,奖学金申请流程需要调整为: - **班主任代申请模式**:不再是学生自主申请,而是由班主任从班级学生中选择推荐 - **材料上传管理**:班主任可以为每个推荐学生上传申请材料(非必填) - **名额控制机制**:严格按照班级或学院分配的名额进行申请限制 - **时间窗口控制**:只能在批次开放的时间段内进行申请操作 ### 1.2 核心变更点 | 变更点 | 原设计 | 新设计 | |--------|--------|--------| | **申请主体** | 学生自主申请 | 班主任代学生申请 | | **申请方式** | 学生在线填写申请表 | 班主任从班级学生中选择 | | **材料上传** | 学生自行上传 | 班主任统一管理上传 | | **条件过滤** | 系统自动过滤 | 暂时不做申请条件过滤 | | **名额控制** | 按院系统计控制 | 按班级→学院层级控制 | --- ## 👥 2. 角色权限设计 ### 2.1 角色定义 | 角色 | 申请管理权限 | 主要操作 | |------|-------------|----------| | **班主任** | 本班级学生申请管理 | 选择学生、上传材料、提交申请 | | **学院管理员** | 本学院申请审核管理 | 查看申请、审核、统计 | | **学生处管理员** | 全校申请统计管理 | 全局统计、结果确认 | | **学生** | 查看自己申请状态 | 查看申请进度、结果 | ### 2.2 权限矩阵 | 功能模块 | 班主任 | 学院管理员 | 学生处管理员 | 学生 | |----------|--------|------------|-------------|------| | 学生选择推荐 | ✏️ | 🔍 | 🔍 | ❌ | | 材料上传管理 | ✏️ | 🔍 | 🔍 | ❌ | | 申请提交 | ✏️ | ❌ | ❌ | ❌ | | 申请审核 | 🔍 | ✏️ | ✏️ | ❌ | | 申请状态查看 | ✅ | ✅ | ✅ | 🔍 | | 统计报表 | 🔍 | ✏️ | ✅ | ❌ | **图例**: ✅全部权限 ✏️编辑权限 🔍查看权限 ❌无权限 --- ## 🔧 3. 功能模块设计 ### 3.1 班主任申请管理 #### 3.1.1 学生选择功能 **功能描述**: 班主任从本班级学生中选择推荐申请奖学金的学生 **核心逻辑**: ``` 1. 获取班级学生列表(显示基本信息:学号、姓名、专业) 2. 支持多选学生(暂不做申请条件自动过滤) 3. 实时显示已选择学生数量 vs 可申请名额 4. 名额控制:优先使用班级分配名额,无班级名额时使用学院名额 5. 提供学生基本信息预览(便于班主任选择判断) ``` **界面要求**: - 学生列表表格:学号、姓名、专业、在校状态 - 批量选择操作:全选、反选、清空 - 名额提示:`已选择 X 人,可申请名额 Y 人` - 学生详情弹窗:点击学生姓名显示详细信息 #### 3.1.2 材料上传管理 **功能描述**: 班主任为每个推荐学生上传申请材料 **核心逻辑**: ``` 1. 已选择学生的材料管理列表 2. 支持每个学生上传多个材料文件 3. 材料分类管理:成绩单、获奖证书、其他证明 4. 文件格式限制:PDF、JPG、PNG,单文件不超过10MB 5. 材料状态标识:已上传、未上传(材料上传为非必填) ``` **界面要求**: - 学生材料管理表格:学生信息、材料数量、上传状态、操作 - 材料上传弹窗:支持拖拽上传、批量上传 - 材料预览功能:支持在线预览已上传材料 - 进度提示:`X 人已上传材料,Y 人未上传` #### 3.1.3 申请提交控制 **功能描述**: 班主任统一提交本班级所有推荐学生的申请 **核心逻辑**: ``` 1. 时间窗口检查:必须在批次申请开放时间内 2. 名额数量检查:推荐学生数不能超过分配名额 3. 名额分配检查:班级有名额优先使用,无班级名额时使用学院名额 4. 批量生成申请记录:每个学生生成一条申请记录 5. 状态更新:申请提交后不能修改,除非撤回 ``` **提交前验证**: - ✅ 批次申请时间有效性 - ✅ 推荐学生数量 ≤ 可用名额 - ✅ 学生信息完整性 - ⚠️ 材料上传完整性(提示但不强制) ### 3.2 名额控制机制 #### 3.2.1 层级名额分配 ``` 全校总名额 ├── 学院A名额 │ ├── 班级A1名额 (优先使用) │ ├── 班级A2名额 │ └── 学院A机动名额 (班级无名额时使用) └── 学院B名额 ├── 班级B1名额 └── 学院B机动名额 ``` #### 3.2.2 名额使用逻辑 **申请时名额检查顺序**: 1. **班级名额优先**: 如果班级有分配名额,优先使用班级名额 2. **学院名额兜底**: 如果班级无名额,使用学院级别名额 3. **名额不足提示**: 超过可用名额时给出明确提示 4. **实时名额更新**: 申请提交后立即更新已用名额 **名额计算公式**: ```sql -- 班级可用名额计算 班级可用名额 = 班级总名额 - 班级已用名额 - 班级预留名额 -- 学院可用名额计算(当班级无名额时) 学院可用名额 = 学院总名额 - 学院已用名额 - 学院预留名额 ``` ### 3.3 时间窗口控制 #### 3.3.1 申请时间控制 ```java // 申请时间检查逻辑 public boolean checkApplicationTimeWindow(Long batchId) { ScholarshipBatch batch = batchService.getById(batchId); Date now = new Date(); // 检查批次状态 if (!"ACTIVE".equals(batch.getStatus())) { return false; } // 检查申请时间窗口 return now.after(batch.getApplicationStartTime()) && now.before(batch.getApplicationEndTime()); } ``` #### 3.3.2 操作权限控制 | 操作 | 申请开始前 | 申请期间 | 申请结束后 | |------|------------|----------|------------| | 选择学生 | ❌ | ✅ | ❌ | | 上传材料 | ❌ | ✅ | 🔍(仅查看) | | 提交申请 | ❌ | ✅ | ❌ | | 撤回申请 | ❌ | ✅ | ❌ | | 查看状态 | ✅ | ✅ | ✅ | --- ## 📊 4. 数据模型设计 ### 4.1 申请记录表调整 基于新需求,需要调整现有的申请记录表结构: ```sql -- 修改申请记录表 ALTER TABLE jcbt_scholarship_application ADD COLUMN `applicant_type` VARCHAR(20) DEFAULT 'TEACHER' COMMENT '申请人类型:STUDENT-学生申请,TEACHER-教师代申请'; ALTER TABLE jcbt_scholarship_application ADD COLUMN `applicant_id` BIGINT(20) DEFAULT NULL COMMENT '申请人ID(班主任ID)'; ALTER TABLE jcbt_scholarship_application ADD COLUMN `applicant_name` VARCHAR(100) DEFAULT NULL COMMENT '申请人姓名(班主任姓名)'; -- 修改申请类型枚举值 ALTER TABLE jcbt_scholarship_application MODIFY COLUMN `application_type` VARCHAR(20) NOT NULL COMMENT '申请类型:TEACHER_RECOMMEND-教师推荐,AUTO-自动评定,DIRECT-直接指定'; -- 添加班级信息索引 CREATE INDEX idx_class_batch ON jcbt_scholarship_application(class_id, batch_id); CREATE INDEX idx_applicant_batch ON jcbt_scholarship_application(applicant_id, batch_id); ``` ### 4.2 班级名额分配表 新增班级级别的名额分配管理: ```sql -- 班级名额分配表 CREATE TABLE jcbt_scholarship_class_quota ( id BIGINT(20) NOT NULL COMMENT '主键ID', batch_id BIGINT(20) NOT NULL COMMENT '批次ID', scholarship_type_id BIGINT(20) NOT NULL COMMENT '奖学金类型ID', dept_id BIGINT(20) NOT NULL COMMENT '学院ID', class_id BIGINT(20) NOT NULL COMMENT '班级ID', class_name VARCHAR(100) NOT NULL COMMENT '班级名称', total_quota INT DEFAULT 0 COMMENT '总名额', used_quota INT DEFAULT 0 COMMENT '已用名额', reserved_quota INT DEFAULT 0 COMMENT '预留名额', available_quota INT DEFAULT 0 COMMENT '可用名额', allocation_method VARCHAR(20) DEFAULT 'MANUAL' COMMENT '分配方式:MANUAL-手动分配,AUTO-自动分配', is_locked TINYINT(1) DEFAULT 0 COMMENT '是否锁定:0-否,1-是', status VARCHAR(20) DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE-有效,INACTIVE-无效', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', tenant_id BIGINT(20) NOT NULL COMMENT '租户ID', create_by BIGINT(20) DEFAULT NULL COMMENT '创建者', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_by BIGINT(20) DEFAULT NULL COMMENT '更新者', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', deleted TINYINT(1) DEFAULT 0 COMMENT '删除标记:0-正常,1-删除', PRIMARY KEY (id), UNIQUE KEY uk_batch_type_class (batch_id, scholarship_type_id, class_id, deleted), INDEX idx_dept_class (dept_id, class_id), INDEX idx_batch_dept (batch_id, dept_id), INDEX idx_quota_status (total_quota, status), INDEX idx_tenant (tenant_id, deleted) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='班级名额分配表'; ``` ### 4.3 申请材料表调整 调整材料表以支持班主任上传: ```sql -- 添加上传者信息 ALTER TABLE jcbt_scholarship_attachment ADD COLUMN `uploader_type` VARCHAR(20) DEFAULT 'TEACHER' COMMENT '上传者类型:STUDENT-学生,TEACHER-教师'; ALTER TABLE jcbt_scholarship_attachment ADD COLUMN `uploader_id` BIGINT(20) DEFAULT NULL COMMENT '上传者ID'; ALTER TABLE jcbt_scholarship_attachment ADD COLUMN `uploader_name` VARCHAR(100) DEFAULT NULL COMMENT '上传者姓名'; -- 修改必需字段逻辑 ALTER TABLE jcbt_scholarship_attachment MODIFY COLUMN `is_required` TINYINT(1) DEFAULT 0 COMMENT '是否必需:0-否,1-是(新需求中材料非必填)'; ``` ### 4.4 名额使用记录表 新增名额使用记录表,用于追踪名额使用情况: ```sql -- 名额使用记录表 CREATE TABLE jcbt_scholarship_quota_usage ( id BIGINT(20) NOT NULL COMMENT '主键ID', batch_id BIGINT(20) NOT NULL COMMENT '批次ID', scholarship_type_id BIGINT(20) NOT NULL COMMENT '奖学金类型ID', quota_type VARCHAR(20) NOT NULL COMMENT '名额类型:CLASS-班级名额,DEPT-学院名额', target_id BIGINT(20) NOT NULL COMMENT '目标ID(班级ID或学院ID)', application_id BIGINT(20) NOT NULL COMMENT '申请ID', student_id BIGINT(20) NOT NULL COMMENT '学生ID', operation_type VARCHAR(20) NOT NULL COMMENT '操作类型:USE-使用,RELEASE-释放', operation_time DATETIME NOT NULL COMMENT '操作时间', operator_id BIGINT(20) NOT NULL COMMENT '操作人ID', operator_name VARCHAR(100) NOT NULL COMMENT '操作人姓名', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', tenant_id BIGINT(20) NOT NULL COMMENT '租户ID', create_by BIGINT(20) DEFAULT NULL COMMENT '创建者', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_by BIGINT(20) DEFAULT NULL COMMENT '更新者', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', deleted TINYINT(1) DEFAULT 0 COMMENT '删除标记:0-正常,1-删除', PRIMARY KEY (id), INDEX idx_batch_type_target (batch_id, scholarship_type_id, target_id), INDEX idx_application_student (application_id, student_id), INDEX idx_operation_time (operation_type, operation_time), INDEX idx_tenant (tenant_id, deleted) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='名额使用记录表'; ``` --- ## 🔄 5. 业务流程设计 ### 5.1 申请提交流程 ```mermaid graph TD A[班主任登录] --> B[选择申请批次] B --> C[检查申请时间窗口] C --> D{时间窗口有效?} D -->|否| E[提示申请时间未开放] D -->|是| F[获取班级学生列表] F --> G[选择推荐学生] G --> H[检查名额限制] H --> I{名额足够?} I -->|否| J[提示名额不足] I -->|是| K[上传学生材料] K --> L[确认提交申请] L --> M[生成申请记录] M --> N[更新名额使用] N --> O[发送提交通知] ``` ### 5.2 名额检查流程 ```mermaid graph TD A[开始名额检查] --> B[获取班级分配名额] B --> C{班级有分配名额?} C -->|是| D[计算班级可用名额] C -->|否| E[获取学院分配名额] D --> F{班级名额足够?} F -->|是| G[使用班级名额] F -->|否| E E --> H[计算学院可用名额] H --> I{学院名额足够?} I -->|是| J[使用学院名额] I -->|否| K[提示名额不足] G --> L[允许申请] J --> L K --> M[拒绝申请] ``` ### 5.3 材料管理流程 ```mermaid graph TD A[进入材料管理] --> B[显示已选学生列表] B --> C[选择学生上传材料] C --> D[检查文件格式] D --> E{格式有效?} E -->|否| F[提示格式错误] E -->|是| G[上传文件到服务器] G --> H[保存材料记录] H --> I[更新材料状态] I --> J[显示上传成功] J --> K{还有其他学生?} K -->|是| C K -->|否| L[完成材料上传] ``` --- ## 📋 6. 接口设计 ### 6.1 学生选择接口 ```java /** * 获取班级学生列表(供选择推荐) */ @GetMapping("/class-students") public R> getClassStudents( @RequestParam Long classId, @RequestParam Long batchId, @RequestParam Long scholarshipTypeId) { // 返回班级学生列表,包含基本信息和是否已申请状态 } /** * 检查申请名额限制 */ @PostMapping("/check-quota") public R checkQuota( @RequestBody QuotaCheckBo quotaCheckBo) { // 检查选择的学生数量是否超过可用名额 } ``` ### 6.2 申请提交接口 ```java /** * 批量提交学生申请 */ @PostMapping("/batch-submit") public R batchSubmitApplications( @RequestBody BatchApplicationBo batchApplicationBo) { // 批量创建申请记录,更新名额使用情况 } /** * 撤回班级申请 */ @PostMapping("/withdraw-batch") public R withdrawBatchApplications( @RequestParam Long classId, @RequestParam Long batchId, @RequestParam Long scholarshipTypeId) { // 撤回班级所有申请,释放名额 } ``` ### 6.3 材料管理接口 ```java /** * 上传学生申请材料 */ @PostMapping("/upload-material") public R uploadStudentMaterial( @RequestParam Long applicationId, @RequestParam MultipartFile file, @RequestParam String materialType) { // 上传单个学生的申请材料 } /** * 获取学生材料列表 */ @GetMapping("/student-materials") public R> getStudentMaterials( @RequestParam Long applicationId) { // 获取指定申请的所有材料 } ``` --- ## 📊 7. 前端页面设计 ### 7.1 申请管理主页面 **页面结构**: ``` [批次选择] [奖学金类型选择] ========================================== 申请状态: [时间窗口状态] [名额统计] ========================================== 学生选择区域: ┌─────────────────────────────────────┐ │ [全选] [反选] [清空] 已选择X人/可申请Y人 │ ├─────────────────────────────────────┤ │ ☐ 张三 (2021001) 计算机科学与技术 │ │ ☑ 李四 (2021002) 软件工程 │ │ ☐ 王五 (2021003) 信息安全 │ └─────────────────────────────────────┘ ========================================== [上传材料] [提交申请] [撤回申请] ``` ### 7.2 材料管理页面 **页面结构**: ``` 材料上传管理 ========================================== ┌─学生─┬─材料数量─┬─上传状态─┬─操作─┐ │ 张三 │ 2 │ 已完成 │ 查看 │ │ 李四 │ 0 │ 未上传 │ 上传 │ │ 王五 │ 1 │ 部分 │ 管理 │ └─────┴────────┴────────┴──────┘ ========================================== 进度: 2人已上传材料,1人未上传 ``` ### 7.3 申请状态查看页面 **页面结构**: ``` 申请状态总览 ========================================== 批次: 2024年春季学期 | 类型: 国家奖学金 申请时间: 2024-03-01 ~ 2024-03-31 名额情况: 班级名额 2/3,学院名额 15/20 ========================================== 申请学生列表: ┌─学号───┬─姓名─┬─状态───┬─材料─┬─操作─┐ │2021001 │ 张三 │ 待审核 │ 2 │ 查看 │ │2021002 │ 李四 │ 已通过 │ 0 │ 查看 │ └────────┴─────┴────────┴──────┴──────┘ ``` --- ## 🔧 8. 技术实现要点 ### 8.1 名额控制实现 ```java @Service public class QuotaControlService { /** * 检查并分配名额 */ public QuotaAllocationResult allocateQuota(QuotaAllocationBo request) { // 1. 检查班级名额 QuotaVo classQuota = getClassQuota(request.getClassId(), request.getBatchId(), request.getScholarshipTypeId()); if (classQuota != null && classQuota.getAvailableQuota() >= request.getStudentCount()) { // 使用班级名额 return allocateClassQuota(request, classQuota); } // 2. 检查学院名额 QuotaVo deptQuota = getDeptQuota(request.getDeptId(), request.getBatchId(), request.getScholarshipTypeId()); if (deptQuota != null && deptQuota.getAvailableQuota() >= request.getStudentCount()) { // 使用学院名额 return allocateDeptQuota(request, deptQuota); } // 3. 名额不足 throw new BusinessException("可用名额不足"); } } ``` ### 8.2 时间窗口控制 ```java @Component public class ApplicationTimeValidator { @Autowired private ScholarshipBatchService batchService; /** * 验证申请时间窗口 */ public void validateApplicationTime(Long batchId) { ScholarshipBatch batch = batchService.getById(batchId); Date now = new Date(); // 检查批次状态 if (!"ACTIVE".equals(batch.getStatus())) { throw new BusinessException("当前批次未开放申请"); } // 检查申请开始时间 if (now.before(batch.getApplicationStartTime())) { throw new BusinessException("申请尚未开始,开始时间:" + DateUtils.formatDate(batch.getApplicationStartTime())); } // 检查申请结束时间 if (now.after(batch.getApplicationEndTime())) { throw new BusinessException("申请已结束,结束时间:" + DateUtils.formatDate(batch.getApplicationEndTime())); } } } ``` ### 8.3 批量申请提交 ```java @Service @Transactional(rollbackFor = Exception.class) public class BatchApplicationService { /** * 批量提交申请 */ public BatchSubmitResult batchSubmitApplications(BatchApplicationBo request) { // 1. 验证申请时间 applicationTimeValidator.validateApplicationTime(request.getBatchId()); // 2. 分配名额 QuotaAllocationResult quotaResult = quotaControlService.allocateQuota(request); // 3. 批量创建申请记录 List applications = request.getStudentIds().stream() .map(studentId -> buildApplication(request, studentId, quotaResult)) .collect(Collectors.toList()); scholarshipApplicationService.saveBatch(applications); // 4. 更新名额使用情况 updateQuotaUsage(quotaResult); // 5. 记录操作日志 logBatchOperation(request, applications); return BatchSubmitResult.success(applications.size()); } } ``` --- ## 📝 9. 测试用例设计 ### 9.1 功能测试用例 | 测试场景 | 测试步骤 | 预期结果 | |----------|----------|----------| | 正常申请流程 | 1.选择学生 2.上传材料 3.提交申请 | 申请成功,名额正确扣减 | | 名额不足 | 选择学生数量超过可用名额 | 提示名额不足,禁止提交 | | 时间窗口外申请 | 在申请时间外进行操作 | 提示时间窗口错误 | | 材料上传 | 上传各种格式文件 | 正确文件上传成功,错误格式拒绝 | | 申请撤回 | 提交后撤回申请 | 申请状态更新,名额释放 | ### 9.2 性能测试用例 | 测试场景 | 并发数 | 数据量 | 性能要求 | |----------|--------|--------|----------| | 批量申请提交 | 50 | 100人/班 | 响应时间<3秒 | | 学生列表查询 | 100 | 10000学生 | 响应时间<1秒 | | 材料上传 | 20 | 10MB文件 | 上传成功率>99% | --- ## 📚 10. 部署说明 ### 10.1 数据库升级脚本 1. 执行申请表结构调整脚本 2. 创建班级名额分配表 3. 创建名额使用记录表 4. 更新相关索引 ### 10.2 配置项调整 ```yaml # application.yml scholarship: application: # 材料上传配置 material: max-file-size: 10MB allowed-types: pdf,jpg,jpeg,png upload-path: /data/scholarship/materials/ # 名额控制配置 quota: check-enabled: true auto-allocation: true # 时间窗口配置 time-window: strict-check: true buffer-minutes: 30 ``` --- ## 📋 11. 总结 ### 11.1 主要改进点 1. **申请模式优化**: 从学生自申请改为班主任代申请,提高管理效率 2. **名额控制增强**: 实现班级→学院两级名额管理,更精确的控制 3. **材料管理简化**: 班主任统一管理,降低学生操作复杂度 4. **时间窗口严格控制**: 确保申请在规定时间内进行 ### 11.2 技术创新点 1. **层级名额算法**: 智能的班级→学院名额分配算法 2. **批量操作优化**: 高效的批量申请提交和撤回机制 3. **实时状态同步**: 名额使用情况实时更新 4. **操作日志完整**: 完整的操作追踪和审计功能 ### 11.3 下一步计划 1. **Phase 1**: 完成核心申请管理功能开发 2. **Phase 2**: 增加申请条件自动过滤功能 3. **Phase 3**: 完善统计报表和数据导出 4. **Phase 4**: 优化用户体验和性能调优 --- **文档维护**: 本文档将随着需求变更和系统开发进展持续更新 **最后更新**: 2025-01-01 **文档版本**: v2.0