@ -30,6 +30,7 @@ import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
import com.itextpdf.text.DocumentException ;
import com.itextpdf.text.pdf.PdfReader ;
import com.itextpdf.text.pdf.PdfStamper ;
import lombok.Data ;
import org.apache.commons.lang3.ObjectUtils ;
import org.apache.commons.lang3.StringUtils ;
import org.jfree.chart.ChartUtils ;
@ -41,6 +42,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Page ;
import org.springframework.data.domain.Sort ;
import org.springframework.stereotype.Service ;
import org.springframework.transaction.annotation.Transactional ;
import org.springframework.util.CollectionUtils ;
import javax.annotation.Resource ;
@ -313,124 +315,178 @@ public class EvaluationServiceImpl implements EvaluationService {
return buildTree ( appraiseRepository . save ( appraise ) ) ;
}
// 定义辅助类(可选)
@Data
private static class TargetInfo {
private String classId ;
private String className ;
private String periodId ;
private String name ;
private String avatar ;
private String gender ;
public TargetInfo ( Student student ) {
this . classId = student . getClassId ( ) ;
this . periodId = student . getPeriodId ( ) ;
this . name = student . getName ( ) ;
this . avatar = student . getPicture ( ) ;
this . gender = student . getGender ( ) ;
}
public TargetInfo ( ClassInfo classInfo ) {
this . classId = classInfo . getId ( ) ;
this . className = classInfo . getName ( ) ;
this . periodId = classInfo . getPeriodId ( ) ;
this . name = classInfo . getName ( ) ;
}
}
@Override
@Transactional ( rollbackFor = Exception . class )
public void vote ( AppraiseVoteDto appraiseVoteDto ) {
String targetId = appraiseVoteDto . getTargetId ( ) ;
// 1. 获取基础参数
List < String > targetIds = appraiseVoteDto . getTargetIds ( ) ;
List < String > appraiseIds = appraiseVoteDto . getAppraiseIds ( ) ;
boolean spread = appraiseVoteDto . isSpread ( ) ;
boolean pushParent = appraiseVoteDto . isPushParent ( ) ;
String targetType = appraiseVoteDto . getTargetType ( ) ;
String appraiseId = appraiseVoteDto . getAppraiseId ( ) ;
String from = appraiseVoteDto . getDevice ( ) ;
User loginUser = SecurityUtil . getLoginUser ( ) ;
String schoolId = loginUser . getSchoolId ( ) ;
String classId ;
String className ;
String periodId ;
String name ;
String avatar = null ;
String gender = null ;
// 分别对班级和学生的关键信息取值
// 2. 参数校验
if ( targetIds = = null | | targetIds . isEmpty ( ) ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "目标ID列表不能为空" ) ;
}
if ( appraiseIds = = null | | appraiseIds . isEmpty ( ) ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "评价项ID列表不能为空" ) ;
}
// 3. 批量查询目标信息
Map < String , TargetInfo > targetInfoMap = getTargetInfos ( targetIds , targetType , schoolId ) ;
// 4. 处理每个目标
for ( String targetId : targetIds ) {
TargetInfo targetInfo = targetInfoMap . get ( targetId ) ;
processSingleTarget (
targetId , targetInfo , appraiseIds , spread , pushParent , targetType ,
schoolId , loginUser , from
) ;
}
}
private Map < String , TargetInfo > getTargetInfos ( List < String > targetIds , String targetType , String schoolId ) {
if ( targetType . equals ( TARGET_STUDENT ) ) {
List < Student > list = studentRepository . findByIdAndCode ( targetId , String . format ( PK . STUDENT , schoolId ) ) ;
Student student = RepositoryUtil . findOne ( list , "该学生不存在" ) ;
classId = student . getClassId ( ) ;
periodId = student . getPeriodId ( ) ;
name = student . getName ( ) ;
avatar = student . getPicture ( ) ;
gender = student . getGender ( ) ;
// 获取班级信息
ClassInfo classInfo = classRepository . findClassByIdAndCode ( classId , String . format ( PK . CLASS , schoolId ) ) ;
className = classInfo . getName ( ) ;
Set < String > targetIdsSet = new HashSet < > ( targetIds ) ;
List < Student > students = studentRepository . findAllByCodeAndIdIn ( String . format ( PK . STUDENT , schoolId ) , targetIdsSet ) ;
return students . stream ( ) . collect ( Collectors . toMap ( Student : : getId , TargetInfo : : new ) ) ;
} else if ( targetType . equals ( TARGET_CLASS ) ) {
ClassInfo classInfo = classRepository . findClassByIdAndCode ( targetId , String . format ( PK . CLASS , schoolId ) ) ;
if ( classInfo = = null ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "班级不存在" ) ;
}
classId = targetId ;
periodId = classInfo . getPeriodId ( ) ;
name = classInfo . getName ( ) ;
className = classInfo . getName ( ) ;
List < ClassInfo > classes = classRepository . findAllByCodeAndIdIn ( String . format ( PK . CLASS , schoolId ) , targetIds ) ;
return classes . stream ( ) . collect ( Collectors . toMap ( ClassInfo : : getId , TargetInfo : : new ) ) ;
} else {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "不受支持的评价对象" ) ;
}
// 获取评价项节点
List < AppraiseTreeNode > nodes = appraiseRepository . findNodeById ( PK . PK_APPRAISE , appraiseId , periodId ) ;
AppraiseTreeNode appraiseTreeNode = RepositoryUtil . findOne ( nodes , "该评价项不存在" ) ;
if ( appraiseTreeNode . getPath ( ) = = null ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "仅能评价三级评价项" ) ;
throw new ServiceException ( "不受支持的评价对象类型" ) ;
}
// 通过 periodId 获取 semesters
List < School . Period > periodById = schoolRepository . findPeriodById ( schoolId , periodId ) ;
}
private void processSingleTarget (
String targetId , TargetInfo targetInfo , List < String > appraiseIds ,
boolean spread , boolean pushParent , String targetType ,
String schoolId , User loginUser , String from
) {
// 1. 获取学段和学年学期信息
List < School . Period > periodById = schoolRepository . findPeriodById ( schoolId , targetInfo . getPeriodId ( ) ) ;
School . Period period = RepositoryUtil . findOne ( periodById , "获取学段失败" ) ;
List < School . Semester > semesters = period . getSemesters ( ) ;
// 获取当前学年学期组合 ID
String academicYearId = SchoolDateUtil . calculateAcademicYearId ( semesters , LocalDate . now ( ) ) ;
// 查询是否存在记录
AppraiseRecord record = appraiseRecordRepository . findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode (
targetId ,
classId ,
academicYearId ,
String . format ( PK . PK_APPRAISE_RECORD , schoolId )
) ;
// 2. 批量查询评价项节点
Set < String > appraiseIdsSet = new HashSet < > ( appraiseIds ) ;
List < AppraiseTreeNode > allNodes = appraiseRepository . findAllByCodeAndIdIn ( PK . PK_APPRAISE , appraiseIdsSet , targetInfo . getPeriodId ( ) ) ;
Map < String , AppraiseTreeNode > nodeMap = allNodes . stream ( )
. collect ( Collectors . toMap ( AppraiseTreeNode : : getId , node - > node ) ) ;
// 3. 校验评价项有效性
for ( String appraiseId : appraiseIds ) {
AppraiseTreeNode node = nodeMap . get ( appraiseId ) ;
//RepositoryUtil.findOne(nodeMap, "获取评价项失败: " + appraiseId)
if ( node = = null ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "评价项不存在: " + appraiseId ) ;
}
if ( node . getPath ( ) = = null ) {
throw new ServiceException ( ErrorCode . PARAMS_ERROR . getCode ( ) , "仅能评价三级评价项: " + appraiseId ) ;
}
}
// 初始化新的评价节点
AppraiseRecordItem item = new AppraiseRecordItem ( ) ;
item . setId ( UUID . randomUUID ( ) . toString ( ) ) ;
item . setAppraiseNode ( appraiseTreeNode ) ;
item . setCreator ( loginUser . getName ( ) ) ;
item . setCreatorId ( loginUser . getId ( ) ) ;
item . setCreateTime ( Instant . now ( ) . toEpochMilli ( ) ) ;
item . setDevice ( from ) ;
// 处理学校与学生的差异
if ( targetType . equals ( TARGET_CLASS ) ) {
item . setSpread ( spread ) ;
} else {
item . setPushParent ( pushParent ) ;
// 4. 构建评价记录项并统计值
int totalPraise = 0 ;
int totalCriticize = 0 ;
int totalScoreIncrement = 0 ;
List < AppraiseRecordItem > items = new ArrayList < > ( ) ;
for ( String appraiseId : appraiseIds ) {
AppraiseTreeNode node = nodeMap . get ( appraiseId ) ;
AppraiseRecordItem item = new AppraiseRecordItem ( ) ;
item . setId ( UUID . randomUUID ( ) . toString ( ) ) ;
item . setAppraiseNode ( node ) ;
item . setCreator ( loginUser . getName ( ) ) ;
item . setCreatorId ( loginUser . getId ( ) ) ;
item . setCreateTime ( Instant . now ( ) . toEpochMilli ( ) ) ;
item . setDevice ( from ) ;
if ( TARGET_CLASS . equals ( targetType ) ) {
item . setSpread ( spread ) ;
} else {
item . setPushParent ( pushParent ) ;
}
items . add ( item ) ;
if ( node . isPraise ( ) ) {
totalPraise + + ;
totalScoreIncrement + = 1 ;
} else {
totalCriticize + + ;
totalScoreIncrement - = 1 ;
}
}
// 不存在或者学生的班级不一样则创建一条新的,存在则处理一下分值后再向其 nodes 中插入一条 item
// 5. 创建或更新评价记录
String classId = targetInfo . getClassId ( ) ;
AppraiseRecord record = appraiseRecordRepository . findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode (
targetId , classId , academicYearId , String . format ( PK . PK_APPRAISE_RECORD , schoolId )
) ;
if ( record = = null | | ! classId . equals ( record . getClassId ( ) ) ) {
List < AppraiseRecordItem > items = Collections . singletonList ( item ) ;
record = new AppraiseRecord ( ) ;
record . setTargetId ( targetId ) ;
record . setTargetType ( targetType ) ;
record . setClassId ( classId ) ;
record . setClassName ( className ) ;
record . setName ( name ) ;
record . setAvatar ( avatar ) ;
record . setGender ( gender ) ;
record . setClassName ( targetInfo. getClassName ( ) ) ;
record . setName ( targetI nfo. getN ame( ) ) ;
record . setAvatar ( t argetInfo. getA vatar( ) ) ;
record . setGender ( tar getInfo. getGe nder( ) ) ;
record . setAcademicYearId ( academicYearId ) ;
record . setPraiseCount ( appraiseTreeNode. isPraise ( ) ? 1 : 0 ) ;
record . setCriticizeCount ( appraiseTreeNode. isPraise ( ) ? 0 : 1 ) ;
record . setScore ( appraiseTreeNode. isPraise ( ) ? 1 : 0 ) ;
record . setPraiseCount ( totalPraise ) ;
record . setCriticizeCount ( totalCriticize ) ;
record . setScore ( Math. max ( totalScoreIncrement , 0 ) ) ;
record . setNodes ( items ) ;
record . setCode ( String . format ( PK . PK_APPRAISE_RECORD , schoolId ) ) ;
appraiseRecordRepository . save ( record ) ;
} else {
CosmosPatchOperations operations = CosmosPatchOperations . create ( ) ;
operations . add ( "/nodes/-" , item ) ;
// 表扬 (待改进不会减少表扬数)
if ( appraiseTreeNode . isPraise ( ) ) {
operations . increment ( "/praiseCount" , 1 ) ;
} else {
operations . increment ( "/criticizeCount" , 1 ) ;
}
// 加分
//nt scoreToPlus = ObjectUtils.isEmpty(appraiseTreeNode.getScore()) ? 0 : appraiseTreeNode.getScore();
int incrementValue = appraiseTreeNode . isPraise ( ) ? 1 : - 1 ;
if ( record . getScore ( ) + incrementValue < 0 ) {
operations . set ( "/score" , 0 ) ;
} else {
operations . increment ( "/score" , incrementValue ) ;
for ( AppraiseRecordItem item : items ) {
operations . add ( "/nodes/-" , item ) ;
}
// patch doc
operations . increment ( "/praiseCount" , totalPraise ) ;
operations . increment ( "/criticizeCount" , totalCriticize ) ;
int newScore = record . getScore ( ) + totalScoreIncrement ;
operations . set ( "/score" , Math . max ( newScore , 0 ) ) ;
appraiseRecordRepository . save ( record . getId ( ) , PK . buildOf ( PK . PK_APPRAISE_RECORD , schoolId ) , AppraiseRecord . class , operations ) ;
}
}