@ -38,6 +38,10 @@ import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct ;
import javax.annotation.Resource ;
import javax.servlet.http.HttpServletRequest ;
import java.time.Instant ;
import java.time.LocalDate ;
import java.time.LocalDateTime ;
import java.time.ZoneId ;
import java.util.* ;
import java.util.concurrent.ExecutionException ;
import java.util.stream.Collectors ;
@ -529,24 +533,45 @@ public class LaborEducationServiceImpl implements LaborEducationService {
Map < String , Integer > parentNodeScoreCount = new HashMap < > ( ) ; // 记录每个父节点的知识点数量
for ( LaborEducationServiceImpl . KnowledgeScoreRate scoreRate : studentScoreRates ) {
AppraiseTreeNode node = findKnowledgeNode ( appraise . getNodes ( ) , scoreRate . getKnowledge ( ) ) ;
String knowledge = scoreRate . getKnowledge ( ) ;
AppraiseTreeNode node = findKnowledgeNode ( appraise . getNodes ( ) , knowledge ) ;
// 1. 查找知识点所属的知识块( 假设通过knowledgeMap映射)
String block = findKnowledgeBlock ( knowledgeMap , knowledge ) ; // 自定义方法: 从knowledgeMap中获取知识点所属的知识块
// 2. 查找知识块对应的父节点
AppraiseTreeNode parentNode = null ;
if ( block ! = null ) {
parentNode = findParentNodeByBlockName ( appraise . getNodes ( ) , block ) ; // 自定义方法:按知识块名称查找父节点
}
// 3. 处理匹配到节点的情况
if ( node ! = null ) {
AppraiseTreeNode parentNode = findParentNode ( appraise . getNodes ( ) , node . getId ( ) ) ;
if ( parentNode ! = null ) {
// 累加父节点的得分
double currentScore = parentNodeScoreRates . getOrDefault ( parentNode . getName ( ) , 0.0 ) ;
parentNodeScoreRates . put ( parentNode . getName ( ) , currentScore + scoreRate . getScoreRate ( ) ) ;
parentNode = findParentNode ( appraise . getNodes ( ) , node . getId ( ) ) ;
} else {
// 未匹配知识点: 默认60分, 归入其所属知识块的父节点
double defaultScore = 60.0 ;
if ( parentNode = = null ) {
// 若知识块无对应父节点,归入全局默认块
parentNodeScoreRates . merge ( "默认块" , defaultScore , Double : : sum ) ;
parentNodeScoreCount . merge ( "默认块" , 1 , Integer : : sum ) ;
} else {
// 归入知识块对应的父节点
parentNodeScoreRates . merge ( parentNode . getName ( ) , defaultScore , Double : : sum ) ;
parentNodeScoreCount . merge ( parentNode . getName ( ) , 1 , Integer : : sum ) ;
}
continue ; // 跳过后续匹配逻辑
}
// 累加父节点的知识点数量
int currentCount = parentNodeScoreCount . getOrDefault ( parentNode . getName ( ) , 0 ) ;
parentNodeScoreCount . put ( parentNode . getName ( ) , currentCount + 1 ) ;
// 4. 累加得分到父节点(原有逻辑)
if ( parentNode ! = null ) {
parentNodeScoreRates . merge ( parentNode . getName ( ) , scoreRate . getScoreRate ( ) , Double : : sum ) ;
parentNodeScoreCount . merge ( parentNode . getName ( ) , 1 , Integer : : sum ) ;
} else {
// 如果没有找到父节点,直接使用当前知识点的得分
parentNodeScoreRates . put ( node . getName ( ) , scoreRate . getScoreRate ( ) ) ;
parentNodeScoreCount . put ( node . getName ( ) , 1 ) ;
}
}
}
// 计算父节点的平均得分( 原始正确率) 后, 转换为基于60分的加分制
parentNodeScoreRates . replaceAll ( ( parentNodeName , totalScore ) - > {
@ -576,6 +601,30 @@ public class LaborEducationServiceImpl implements LaborEducationService {
return parentNodeScoreRates ;
}
// 从knowledgeMap中查找知识点所属的知识块
private static String findKnowledgeBlock ( Map < String , List < List < String > > > knowledgeMap , String knowledge ) {
for ( Map . Entry < String , List < List < String > > > entry : knowledgeMap . entrySet ( ) ) {
for ( List < String > subList : entry . getValue ( ) ) {
if ( subList . contains ( knowledge ) ) {
return entry . getKey ( ) ;
}
}
}
return null ;
}
// 按知识块名称查找父节点
private static AppraiseTreeNode findParentNodeByBlockName ( List < AppraiseTreeNode > nodes , String blockName ) {
for ( AppraiseTreeNode node : nodes ) {
if ( node . getName ( ) . equals ( blockName ) ) {
return node ;
}
AppraiseTreeNode found = findParentNodeByBlockName ( node . getChildren ( ) , blockName ) ;
if ( found ! = null ) return found ;
}
return null ;
}
/ * *
* 计 算 班 级 每 个 学 生 知 识 点 得 分 率 , 并 返 回 每 个 学 生 所 属 节 点 的 父 节 点 得 分 率
@ -826,7 +875,6 @@ public class LaborEducationServiceImpl implements LaborEducationService {
Map < String , List < List < String > > > knowledgeMap ,
Map < String , List < Double > > points ,
Map < String , Double > knowledgeTotalScore ) {
Map < String , Double > knowledgeTotalAverage = new HashMap < > ( ) ;
Map < String , Integer > knowledgeExamCount = new HashMap < > ( ) ;
@ -1321,4 +1369,673 @@ public class LaborEducationServiceImpl implements LaborEducationService {
return null ; // 未找到父节点
}
//获取每月各个知识库综合得分以及整体得分内容
public List < Map < String , Object > > getStudentMonthlyScores ( LaborDto laborDto ) {
// 1. 获取基础信息
String schoolId = SecurityUtil . getLoginUser ( ) . getSchoolId ( ) ;
String studentId = laborDto . getStudentId ( ) ;
// 2. 获取相关开课记录
List < LessonRecord > records = getLessonRecords ( laborDto , schoolId ) ;
//List<String> classIds = Collections.singletonList(laborDto.getClassId());
List < RecordVo > res = appraiseRecordRepository . getStudentRecords (
String . format ( PK . PK_APPRAISE_RECORD , schoolId ) ,
laborDto . getStudentId ( ) ,
laborDto . getStartTime ( ) ,
laborDto . getEndTime ( ) ,
"德育"
) ;
// 3. 获取考试及知识点映射
Map < String , List < List < String > > > examKnowledgeMap = new HashMap < > ( ) ;
Map < String , List < Double > > points = new HashMap < > ( ) ;
List < ExamVo > exams = getExamsWithKnowledge ( laborDto , records , examKnowledgeMap , points ) ;
// 4. 获取考试结果并按月份分组
Map < Integer , List < ExamResultWrapper > > monthlyResults = getMonthlyExamResults (
schoolId , laborDto . getClassId ( ) , exams , records , examKnowledgeMap , points , res
) ;
// 5. 获取知识块配置
Appraise appraise = appraiseRepository . findAppraiseBySchoolIdAndPeriodIdAndCode (
schoolId , laborDto . getPeriodId ( ) , PK . PK_APPRAISE
) ;
appraise = evaluationService . buildTree ( appraise ) ;
Map < String , List < String > > knowledgeBlockMap = getKnowledgeBlockToPointsMap ( appraise ) ;
// 在获取知识块配置后添加
Map < String , String > pointToBlockMap = new HashMap < > ( ) ;
knowledgeBlockMap . forEach ( ( block , ps ) - > {
ps . forEach ( point - > pointToBlockMap . put ( point , block ) ) ;
} ) ;
// 处理主观评价数据,统计每个月的次数
Map < String , Map < Integer , Integer > > blockMonthlyCounts = new HashMap < > ( ) ;
Map < String , Map < Integer , Integer > > finalBlockMonthlyCounts = blockMonthlyCounts ;
res . stream ( )
. filter ( vo - > studentId . equals ( vo . getTargetId ( ) ) )
. forEach ( vo - > {
// 获取知识点对应的知识块
String originalPoint = vo . getAppraiseName ( ) ;
String block = pointToBlockMap . getOrDefault ( originalPoint , "其他" ) ;
// 计算月份
int month = getMonthFromTimestamp ( vo . getCreateTime ( ) ) ;
// 更新计数器
Map < Integer , Integer > monthCounts = finalBlockMonthlyCounts . computeIfAbsent ( block , k - > new HashMap < > ( ) ) ;
int current = monthCounts . getOrDefault ( month , 0 ) ;
monthCounts . put ( month , current + ( vo . isPraise ( ) ? 1 : - 1 ) ) ;
} ) ;
// 处理负数和非分类数据
blockMonthlyCounts = blockMonthlyCounts . entrySet ( ) . stream ( )
. filter ( entry - > ! "未分类" . equals ( entry . getKey ( ) ) ) // 过滤掉未映射的评价
. collect ( Collectors . toMap (
Map . Entry : : getKey ,
entry - > entry . getValue ( ) . entrySet ( ) . stream ( )
. collect ( Collectors . toMap (
Map . Entry : : getKey ,
e - > Math . max ( e . getValue ( ) , 0 )
)
) ) ) ;
// 6. 处理每月数据,传入主观评价统计结果
List < Map < String , Object > > monthlyData = processMonthlyData ( studentId , monthlyResults , knowledgeBlockMap ,
getMonthsBetween ( laborDto . getStartTime ( ) , laborDto . getEndTime ( ) ) , blockMonthlyCounts ) ;
// 7. 计算综合得分并添加到结果
return addOverallScore ( monthlyData , knowledgeBlockMap . keySet ( ) ) ;
}
// 新增方法:添加综合得分
private List < Map < String , Object > > addOverallScore (
List < Map < String , Object > > monthlyData ,
Set < String > knowledgeBlocks
) {
// 创建综合得分容器
Map < String , Double > totalScores = new HashMap < > ( ) ;
Map < String , Integer > scoreCounts = new HashMap < > ( ) ;
// 累加所有月份得分
for ( Map < String , Object > monthData : monthlyData ) {
for ( String block : knowledgeBlocks ) {
Double score = ( Double ) monthData . get ( block ) ;
if ( score ! = null ) {
totalScores . put ( block , totalScores . getOrDefault ( block , 0.0 ) + score ) ;
scoreCounts . put ( block , scoreCounts . getOrDefault ( block , 0 ) + 1 ) ;
}
}
}
// 计算平均分
Map < String , Object > overall = new LinkedHashMap < > ( ) ;
overall . put ( "type" , "overall" ) ;
for ( String block : knowledgeBlocks ) {
int count = scoreCounts . getOrDefault ( block , 1 ) ;
double avg = totalScores . getOrDefault ( block , 0.0 ) / count ;
overall . put ( block , Double . parseDouble ( String . format ( "%.2f" , avg ) ) ) ;
}
// 创建新的结果列表(保持原数据不变)
List < Map < String , Object > > result = new ArrayList < > ( monthlyData ) ;
result . add ( overall ) ;
return result ;
}
private List < LessonRecord > getLessonRecords ( LaborDto laborDto , String schoolId ) {
LessonRecordQueryService queryService = new LessonRecordQueryService ( lessonRecordRepository ) ;
try {
return queryService . queryLessonsInParallel (
String . format ( PK . PK_LESSON_RECORD , schoolId ) ,
laborDto . getStartTime ( ) ,
laborDto . getEndTime ( ) ,
laborDto . getSubjectId ( ) ,
null ,
null ,
laborDto . getPeriodId ( )
) ;
} catch ( InterruptedException | ExecutionException e ) {
throw new ServiceException ( ErrorCode . SYSTEM_ERROR . getCode ( ) , "课程查询异常" ) ;
} finally {
queryService . shutdown ( ) ;
}
}
private List < ExamVo > getExamsWithKnowledge ( LaborDto laborDto , List < LessonRecord > records ,
Map < String , List < List < String > > > examKnowledgeMap ,
Map < String , List < Double > > points ) {
List < String > lessonRecordIds = records . stream ( ) . map ( LessonRecord : : getId ) . collect ( Collectors . toList ( ) ) ;
List < ExamVo > exams = examRepository . findExamsByIds ( laborDto . getSource ( ) , lessonRecordIds ) ;
exams . forEach ( exam - > {
if ( ! exam . getPapers ( ) . isEmpty ( ) & & exam . getPapers ( ) . get ( 0 ) . getKnowledge ( ) ! = null ) {
examKnowledgeMap . put ( exam . getId ( ) , exam . getPapers ( ) . get ( 0 ) . getKnowledge ( ) ) ;
points . put ( exam . getId ( ) , exam . getPapers ( ) . get ( 0 ) . getPoint ( ) ) ;
}
} ) ;
return exams . stream ( )
. filter ( exam - > ! exam . getClasses ( ) . isEmpty ( ) )
. collect ( Collectors . toList ( ) ) ;
}
private Map < Integer , List < ExamResultWrapper > > getMonthlyExamResults ( String schoolId , String classId ,
List < ExamVo > exams , List < LessonRecord > records ,
Map < String , List < List < String > > > examKnowledgeMap ,
Map < String , List < Double > > points ,
List < RecordVo > res ) {
Map < Integer , List < ExamResultWrapper > > monthlyResults = new HashMap < > ( ) ;
exams . forEach ( exam - > {
List < ExamClassResult > results = examClassResultRepository . findById (
String . format ( PK . CLASS_RESULT , schoolId ) , classId , exam . getId ( )
) ;
results . forEach ( result - > {
records . stream ( )
. filter ( r - > r . getId ( ) . equals ( exam . getLessonRecordId ( ) ) )
. findFirst ( )
. ifPresent ( record - > {
int month = getMonthFromTimestamp ( record . getStartTime ( ) ) ;
List < List < String > > knowledge = examKnowledgeMap . get ( exam . getId ( ) ) ;
List < Double > point = points . get ( exam . getId ( ) ) ;
monthlyResults . computeIfAbsent ( month , k - > new ArrayList < > ( ) )
. add ( new ExamResultWrapper ( exam . getId ( ) , result , knowledge , point ) ) ;
} ) ;
} ) ;
} ) ;
return monthlyResults ;
}
private List < Map < String , Object > > processMonthlyData ( String studentId ,
Map < Integer , List < ExamResultWrapper > > monthlyResults ,
Map < String , List < String > > knowledgeBlockMap ,
Set < Integer > months ,
Map < String , Map < Integer , Integer > > blockMonthlyCounts ) {
return months . stream ( ) . map ( month - > {
Map < String , Object > monthData = new LinkedHashMap < > ( ) ;
monthData . put ( "month" , month ) ;
List < ExamResultWrapper > wrappers = monthlyResults . getOrDefault ( month , Collections . emptyList ( ) ) ;
List < ExamClassResult > examResults = wrappers . stream ( )
. map ( ExamResultWrapper : : getResult )
. collect ( Collectors . toList ( ) ) ;
Map < String , List < List < String > > > monthlyKnowledgeMap = new HashMap < > ( ) ;
Map < String , List < Double > > monthlyPoints = new HashMap < > ( ) ;
wrappers . forEach ( wrapper - > {
String examId = wrapper . getExamId ( ) ;
monthlyKnowledgeMap . put ( examId , wrapper . getKnowledge ( ) ) ;
monthlyPoints . put ( examId , wrapper . getPoints ( ) ) ;
} ) ;
Map < String , Double > knowledgeScores = calculateMonthlyKnowledgeScores (
studentId , examResults , monthlyKnowledgeMap , monthlyPoints
) ;
Set < String > monthlyTestedKnowledges = knowledgeScores . keySet ( ) ;
knowledgeBlockMap . forEach ( ( block , knowledges ) - > {
List < String > testedKnowledges = knowledges . stream ( )
. filter ( monthlyTestedKnowledges : : contains )
. collect ( Collectors . toList ( ) ) ;
double finalScore ;
if ( testedKnowledges . isEmpty ( ) ) {
finalScore = 60.0 ;
} else {
double averageCorrectRate = testedKnowledges . stream ( )
. mapToDouble ( k - > knowledgeScores . get ( k ) )
. average ( )
. orElse ( 60.0 ) ;
finalScore = 60.0 + ( averageCorrectRate * 0.4 ) ;
finalScore = Math . min ( finalScore , 100.0 ) ;
}
finalScore = Double . parseDouble ( String . format ( "%.2f" , finalScore ) ) ;
// 处理主观评分(根据知识块获取对应次数)
Map < Integer , Integer > monthlyCounts = blockMonthlyCounts . getOrDefault ( block , new HashMap < > ( ) ) ;
int praiseCount = monthlyCounts . getOrDefault ( month , 0 ) ;
// 步骤1: 计算次数转换分数( 0-40分)
final double K = 50.0 ; // 调整系数控制分数增长速度
double convertedScore = ( praiseCount * 40.0 ) / ( praiseCount + K ) ;
convertedScore = Math . min ( convertedScore , 40.0 ) ;
convertedScore = Double . parseDouble ( String . format ( "%.2f" , convertedScore ) ) ;
// 步骤2: 计算主观总分( 60分基础 + 转换分)
double subjectiveTotal = 60.0 + convertedScore ;
subjectiveTotal = Math . min ( subjectiveTotal , 100.0 ) ;
subjectiveTotal = Double . parseDouble ( String . format ( "%.2f" , subjectiveTotal ) ) ;
// 步骤3: 主客观合并( 主观60% + 客观40%)
double combinedScore = ( subjectiveTotal * 0.6 ) + ( finalScore * 0.4 ) ;
combinedScore = Double . parseDouble ( String . format ( "%.2f" , combinedScore ) ) ;
monthData . put ( block , combinedScore ) ;
} ) ;
return monthData ;
} )
. sorted ( Comparator . comparingInt ( m - > ( Integer ) m . get ( "month" ) ) )
. collect ( Collectors . toList ( ) ) ;
}
private Map < String , Double > calculateMonthlyKnowledgeScores ( String studentId ,
List < ExamClassResult > examResults ,
Map < String , List < List < String > > > knowledgeMap ,
Map < String , List < Double > > points ) {
Map < String , Double > knowledgeTotalScore = new HashMap < > ( ) ;
Map < String , Double > knowledgeTotalAverage = new HashMap < > ( ) ;
Map < String , Integer > knowledgeExamCount = new HashMap < > ( ) ;
// 处理无考试数据的情况, 初始化为60分
if ( examResults . isEmpty ( ) ) {
knowledgeMap . values ( ) . stream ( )
. flatMap ( List : : stream )
. flatMap ( List : : stream )
. distinct ( )
. forEach ( knowledge - > knowledgeTotalScore . put ( knowledge , 60.0 ) ) ;
return knowledgeTotalScore ;
}
// 遍历每个考试结果
for ( ExamClassResult examResult : examResults ) {
int studentIndex = examResult . getStudentIds ( ) . indexOf ( studentId ) ;
if ( studentIndex = = - 1 ) continue ;
if ( examResult . getStatus ( ) . get ( studentIndex ) = = 1 ) continue ; // 跳过无效状态
List < List < String > > knowledgeList = knowledgeMap . get ( examResult . getExamId ( ) ) ;
List < Double > pointList = points . get ( examResult . getExamId ( ) ) ;
if ( knowledgeList = = null | | pointList = = null ) continue ;
List < Double > studentScores = examResult . getStudentScores ( ) . get ( studentIndex ) ;
if ( studentScores = = null | | knowledgeList . size ( ) ! = studentScores . size ( ) ) continue ;
// 计算当前考试的知识点得分
Map < String , Double > currentExamScores = new HashMap < > ( ) ;
Map < String , Integer > currentExamCounts = new HashMap < > ( ) ;
for ( int i = 0 ; i < studentScores . size ( ) ; i + + ) {
List < String > knowledges = knowledgeList . get ( i ) ;
double score = studentScores . get ( i ) ;
if ( knowledges . isEmpty ( ) | | score < 0 ) continue ;
double perKnowledgeScore = score / knowledges . size ( ) ;
knowledges . forEach ( knowledge - > {
currentExamScores . merge ( knowledge , perKnowledgeScore , Double : : sum ) ;
currentExamCounts . merge ( knowledge , 1 , Integer : : sum ) ;
} ) ;
}
// 更新累计数据
currentExamScores . forEach ( ( knowledge , total ) - > {
double average = total / currentExamCounts . get ( knowledge ) ;
knowledgeTotalAverage . merge ( knowledge , average , Double : : sum ) ;
knowledgeExamCount . merge ( knowledge , 1 , Integer : : sum ) ;
} ) ;
}
// 计算最终得分( 基础60 + 实际得分,限制范围)
knowledgeTotalAverage . forEach ( ( knowledge , total ) - > {
int count = knowledgeExamCount . get ( knowledge ) ;
double finalScore = 60.0 + Math . min ( total / count , 40.0 ) ;
knowledgeTotalScore . put ( knowledge , Math . min ( finalScore , 100.0 ) ) ;
} ) ;
// 确保所有知识点都有值(处理部分知识点未出现在考试中的情况)
knowledgeMap . values ( ) . stream ( )
. flatMap ( List : : stream )
. flatMap ( List : : stream )
. distinct ( )
. forEach ( knowledge - > knowledgeTotalScore . putIfAbsent ( knowledge , 60.0 ) ) ;
// 保留两位小数
knowledgeTotalScore . replaceAll ( ( k , v ) - > Double . parseDouble ( String . format ( "%.2f" , v ) ) ) ;
return knowledgeTotalScore ;
}
// 辅助类封装考试结果和知识点
@Data
@AllArgsConstructor
private static class ExamResultWrapper {
private String examId ;
private ExamClassResult result ;
private List < List < String > > knowledge ; // 每个题目对应的知识点列表
private List < Double > points ;
}
// 时间处理工具方法
private int getMonthFromTimestamp ( Long timestamp ) {
return LocalDateTime . ofInstant ( Instant . ofEpochMilli ( timestamp ) , ZoneId . systemDefault ( ) )
. getMonthValue ( ) ;
}
private Set < Integer > getMonthsBetween ( Long start , Long end ) {
Set < Integer > months = new TreeSet < > ( ) ;
LocalDateTime startDate = Instant . ofEpochMilli ( start ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDateTime ( ) ;
LocalDateTime endDate = Instant . ofEpochMilli ( end ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDateTime ( ) ;
while ( ! startDate . isAfter ( endDate ) ) {
months . add ( startDate . getMonthValue ( ) ) ;
startDate = startDate . plusMonths ( 1 ) ;
}
return months ;
}
// 学期配置实体( 对应JSON结构)
@Data
@AllArgsConstructor
public static class SemesterConfig {
private String name ;
private int start ; // 1表示学年开始学期
private int month ; // 月份( 1-12)
private int day ; // 日期( 1-31)
private String id ;
}
// 学期时间范围实体
@Data
@AllArgsConstructor
public static class SemesterPeriod {
private String name ;
private LocalDate startDate ;
private LocalDate endDate ;
// 转换为时间戳(用于查询)
public Long getStartTimestamp ( ) {
return startDate . atStartOfDay ( ZoneId . systemDefault ( ) )
. toInstant ( ) . toEpochMilli ( ) ;
}
public Long getEndTimestamp ( ) {
return endDate . atTime ( 23 , 59 , 59 )
. atZone ( ZoneId . systemDefault ( ) )
. toInstant ( ) . toEpochMilli ( ) ;
}
}
// 分学期比对,分析 学期数据
public Map < String , Object > getStudentSemesterScores ( LaborDto laborDto , HttpServletRequest request ) {
// 1. 获取学期配置
List < School . Semester > semesters = schoolRepository . findSemestersById ( laborDto . getCode ( ) , laborDto . getPeriodId ( ) ) ;
List < SemesterConfig > configs = new ArrayList < > ( ) ;
for ( School . Semester semester : semesters ) {
configs . add ( new SemesterConfig ( semester . getName ( ) , semester . getStart ( ) , semester . getMonth ( ) , semester . getDay ( ) , semester . getId ( ) ) ) ;
}
// 2. 计算学期时间范围
Map < String , SemesterPeriod > periods = calculateSemesters ( configs ) ;
SemesterPeriod current = periods . get ( "current" ) ;
SemesterPeriod last = periods . get ( "last" ) ;
// 3. 查询并处理数据
Map < String , Object > currentData = current ! = null ?
processSemesterData ( laborDto , current , request , true ) :
Collections . emptyMap ( ) ;
Map < String , Object > lastData = last ! = null ?
processSemesterData ( laborDto , last , request , true ) :
Collections . emptyMap ( ) ;
// 4. 组合最终结果
return combineResults ( laborDto , currentData , lastData ) ;
}
// 核心学期计算逻辑
private Map < String , SemesterPeriod > calculateSemesters ( List < SemesterConfig > semesters ) {
LocalDate now = LocalDate . now ( ) ;
int currentYear = now . getYear ( ) ;
// 1. 确定学年起始学期
SemesterConfig startSemester = semesters . stream ( )
. filter ( s - > s . start = = 1 )
. findFirst ( )
. orElseThrow ( ( ) - > new IllegalArgumentException ( "未配置起始学期" ) ) ;
// 2. 计算当前所属学年
int academicYear = calculateAcademicYear ( startSemester , LocalDate . now ( ) ) ;
// 3. 生成当前学年学期
List < SemesterPeriod > currentYearSemesters = generateYearSemesters (
semesters , academicYear , startSemester ) ;
// 4. 确定当前学期
SemesterPeriod current = currentYearSemesters . stream ( )
. filter ( sp - > ! now . isBefore ( sp . startDate ) & & ! now . isAfter ( sp . endDate ) )
. findFirst ( )
. orElse ( null ) ;
// 5. 确定上学期
SemesterPeriod last = findLastSemester ( current , semesters , academicYear , startSemester ) ;
return new HashMap < String , SemesterPeriod > ( ) { {
put ( "current" , current ) ;
put ( "last" , last ) ;
} } ;
}
// 增强学术年度计算方法
private int calculateAcademicYear ( SemesterConfig startSemester , LocalDate currentDate ) {
int currentYear = currentDate . getYear ( ) ;
int currentMonth = currentDate . getMonthValue ( ) ;
return ( currentMonth < startSemester . month ) ? currentYear - 1 : currentYear ;
}
// 生成学年学期时间范围
private List < SemesterPeriod > generateYearSemesters ( List < SemesterConfig > semesters ,
int academicYear ,
SemesterConfig startSemester ) {
List < SemesterPeriod > periods = new ArrayList < > ( ) ;
// 1. 生成各学期开始日期
for ( SemesterConfig sc : semesters ) {
int year = academicYear ;
if ( sc . month < startSemester . month ) {
year + = 1 ; // 跨年学期(如 2024 学年下学期是 2025 年 2 月)
}
LocalDate startDate = LocalDate . of ( year , sc . month , sc . day ) ;
periods . add ( new SemesterPeriod ( sc . name , startDate , null ) ) ;
}
periods . sort ( Comparator . comparing ( SemesterPeriod : : getStartDate ) ) ;
// 2. 计算结束日期
for ( int i = 0 ; i < periods . size ( ) ; i + + ) {
SemesterPeriod curr = periods . get ( i ) ;
if ( i < periods . size ( ) - 1 ) {
// 非最后学期:下一学期开始前 1 天
curr . setEndDate ( periods . get ( i + 1 ) . getStartDate ( ) . minusDays ( 1 ) ) ;
} else {
// 最后学期:学年结束年份的 7 月 31 日( 2025-07-31)
int endYear = academicYear + 1 ; // 关键修改:统一用学年结束年份
curr . setEndDate ( LocalDate . of ( endYear , 7 , 31 ) ) ;
}
// 3. 校验时间顺序
if ( curr . getStartDate ( ) . isAfter ( curr . getEndDate ( ) ) ) {
throw new IllegalStateException ( "学期日期无效: " + curr . getName ( )
+ " 开始日期(" + curr . getStartDate ( )
+ ") 不能晚于结束日期(" + curr . getEndDate ( ) + ")" ) ;
}
}
return periods ;
}
// 查找上学期(含跨学年处理)
private SemesterPeriod findLastSemester ( SemesterPeriod current ,
List < SemesterConfig > semesters ,
int academicYear ,
SemesterConfig startSemester ) {
if ( current = = null ) return null ;
List < SemesterPeriod > currentYearSemesters = generateYearSemesters (
semesters , academicYear , startSemester ) ;
int index = currentYearSemesters . indexOf ( current ) ;
if ( index > 0 ) {
return currentYearSemesters . get ( index - 1 ) ;
} else {
// 获取去年最后一个学期
List < SemesterPeriod > lastYear = generateYearSemesters (
semesters , academicYear - 1 , startSemester ) ;
return lastYear . isEmpty ( ) ? null : lastYear . get ( lastYear . size ( ) - 1 ) ;
}
}
// 处理学期数据
private Map < String , Object > processSemesterData ( LaborDto laborDto ,
SemesterPeriod semester ,
HttpServletRequest request ,
boolean isCurrent ) {
Map < String , Object > result = new HashMap < > ( ) ;
// 1. 设置时间范围查询条件
laborDto . setStartTime ( convertToTimestamp ( semester . startDate ) ) ;
laborDto . setEndTime ( convertToTimestamp ( semester . endDate ) ) ;
// 2. 获取原始分析数据
Map < String , Object > analysisData = getAnalysis ( laborDto , request ) ;
// 3. 提取知识点次数(仅当前学期)
if ( isCurrent ) {
List < Map < String , Object > > scores = ( List < Map < String , Object > > ) analysisData . get ( "scores" ) ;
Map < String , Integer > knowledgeCounts = scores . stream ( )
. filter ( s - > laborDto . getStudentId ( ) . equals ( s . get ( "studentId" ) ) )
. findFirst ( )
. map ( s - > ( List < Map < String , Object > > ) s . get ( "appraises" ) )
. map ( appraises - > appraises . stream ( )
. collect ( Collectors . toMap (
a - > ( String ) a . get ( "appraiseName" ) ,
a - > 1 ,
Integer : : sum
) ) )
. orElse ( new HashMap < > ( ) ) ;
result . put ( "knowledgeCounts" , knowledgeCounts ) ;
}
// 4. 提取综合得分
Map < String , Double > blockScores = extractBlockScores ( analysisData , laborDto . getStudentId ( ) ) ;
result . put ( "blockScores" , blockScores ) ;
// 5. 提取班级均分
Map < String , List < Map < String , Object > > > classScoreRate = ( Map < String , List < Map < String , Object > > > ) analysisData . get ( "classScoreRate" ) ;
List < Map < String , Object > > classScoreList = classScoreRate . get ( "classScoreRates" ) ;
Map < String , Double > classAverages = classScoreList . stream ( )
. findFirst ( )
. map ( c - > ( List < Map < String , Object > > ) c . get ( "blocks" ) )
. map ( blocks - > blocks . stream ( )
. collect ( Collectors . toMap (
b - > ( String ) b . get ( "name" ) ,
b - > ( Double ) b . get ( "score" )
) ) )
. orElse ( new HashMap < > ( ) ) ;
result . put ( "classAverage" , classAverages ) ;
return result ;
}
private Map < String , Double > extractBlockScores ( Map < String , Object > data , String studentId ) {
return ( ( List < Map < String , Object > > ) data . get ( "scores" ) ) . stream ( )
. filter ( s - > studentId . equals ( s . get ( "studentId" ) ) )
. findFirst ( )
. map ( scoreMap - > ( Map < String , Map < String , Object > > ) scoreMap . get ( "scores" ) ) // 获取知识块得分对象
. map ( scoresMap - > scoresMap . entrySet ( ) . stream ( )
. collect ( Collectors . toMap (
Map . Entry : : getKey , // 知识块名称(如"道德修养")
entry - > ( Double ) entry . getValue ( ) . get ( "compositeScore" ) // 提取复合分数
) )
)
. orElse ( new HashMap < > ( ) ) ;
}
// 辅助方法: 转换LocalDate为时间戳
private long convertToTimestamp ( LocalDate date ) {
return date . atStartOfDay ( ZoneId . systemDefault ( ) )
. toInstant ( )
. toEpochMilli ( ) ;
}
// 结果合并方法
private Map < String , Object > combineResults ( LaborDto laborDto ,
Map < String , Object > currentData ,
Map < String , Object > lastData ) {
Map < String , Object > result = new LinkedHashMap < > ( ) ;
result . put ( "studentId" , laborDto . getStudentId ( ) ) ;
Appraise appraise = appraiseRepository . findAppraiseBySchoolIdAndPeriodIdAndCode ( laborDto . getCode ( ) , laborDto . getPeriodId ( ) , PK . PK_APPRAISE ) ;
// 获取知识块配置
appraise = evaluationService . buildTree ( appraise ) ;
Set < String > knowledgeBlocks = loadKnowledgeBlocks ( appraise ) ;
List < Map < String , Object > > scores = new ArrayList < > ( ) ;
for ( String block : knowledgeBlocks ) {
Map < String , Object > blockData = new LinkedHashMap < > ( ) ;
blockData . put ( "name" , block ) ;
Map < String , Double > currentBlockScores = ( Map < String , Double > ) currentData . get ( "blockScores" ) ;
double moralScore = currentBlockScores . getOrDefault ( block , 0.0 ) ; // 精确获取目标知识块
blockData . put ( "TScore" , moralScore ) ;
Map < String , Double > lastBlockScores = ( Map < String , Double > ) lastData . get ( "blockScores" ) ;
double lastScore = lastBlockScores . getOrDefault ( block , 0.0 ) ;
blockData . put ( "LScore" , lastScore ) ;
// 班级均分
Map < String , Double > classAvg = ( Map < String , Double > ) currentData . get ( "classAverage" ) ;
blockData . put ( "classScore" , classAvg . getOrDefault ( block , 0.0 ) ) ;
// 知识点次数
if ( currentData . containsKey ( "knowledgeCounts" ) ) {
Map < String , Integer > counts = ( Map < String , Integer > ) currentData . get ( "knowledgeCounts" ) ;
Appraise finalAppraise = appraise ;
counts . entrySet ( ) . stream ( )
. filter ( e - > isBelongToBlock ( e . getKey ( ) , block , finalAppraise ) )
. forEach ( e - > blockData . put ( e . getKey ( ) , e . getValue ( ) ) ) ;
}
scores . add ( blockData ) ;
}
result . put ( "scores" , scores ) ;
return result ;
}
private Set < String > loadKnowledgeBlocks ( Appraise appraise ) {
// 从appraise配置获取所有德育知识块
Set < String > knowledgePoints = new HashSet < > ( ) ;
for ( AppraiseTreeNode node : appraise . getNodes ( ) ) {
if ( "德育" . equals ( node . getName ( ) ) ) {
for ( AppraiseTreeNode secondLevelNode : node . getChildren ( ) ) {
knowledgePoints . add ( secondLevelNode . getName ( ) ) ;
}
}
}
return knowledgePoints ;
}
private boolean isBelongToBlock ( String knowledge , String block , Appraise appraise ) {
// 实现知识点归属判断逻辑
Map < String , List < String > > knowledgeBlockToPointsMap = getKnowledgeBlockToPointsMap ( appraise ) ;
return knowledgeBlockToPointsMap . getOrDefault ( block , Collections . emptyList ( ) )
. contains ( knowledge ) ;
}
}