新增到课签到时间窗口与迟到签到时间窗口配置,优化教练评分机制(未测试)
This commit is contained in:
+9
-9
@@ -271,7 +271,7 @@ public class DataStatisticsDao {
|
||||
return databaseClient.sql("""
|
||||
SELECT coach_id, COUNT(*) AS count
|
||||
FROM group_course
|
||||
WHERE start_time >= :startTime AND start_time < :endTime
|
||||
WHERE end_time >= :startTime AND end_time < :endTime
|
||||
AND status IN ('2', '6') AND deleted_at IS NULL
|
||||
GROUP BY coach_id
|
||||
""")
|
||||
@@ -282,16 +282,16 @@ public class DataStatisticsDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按教练统计出席人次(通过团课关联,booking.status='2')
|
||||
* 按教练统计出席人次(booking.status IN ('2','4','5'):已出席 + 教练缺席 + 迟到)
|
||||
*/
|
||||
public reactor.core.publisher.Flux<java.util.Map<String, Object>> countAttendedStudentsByCoach(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return databaseClient.sql("""
|
||||
SELECT gc.coach_id, COUNT(*) AS count
|
||||
FROM group_course_booking b
|
||||
INNER JOIN group_course gc ON b.course_id = gc.id
|
||||
WHERE b.status = '2' AND b.deleted_at IS NULL
|
||||
WHERE b.status IN ('2', '4', '5') AND b.deleted_at IS NULL
|
||||
AND gc.deleted_at IS NULL
|
||||
AND gc.start_time >= :startTime AND gc.start_time < :endTime
|
||||
AND gc.end_time >= :startTime AND gc.end_time < :endTime
|
||||
GROUP BY gc.coach_id
|
||||
""")
|
||||
.bind("startTime", startTime)
|
||||
@@ -301,16 +301,16 @@ public class DataStatisticsDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按教练统计总非取消预约数(用于计算出勤率分母)
|
||||
* 按教练统计总预约数(仅 status='0' 已预约,用于出勤率分母)
|
||||
*/
|
||||
public reactor.core.publisher.Flux<java.util.Map<String, Object>> countTotalBookingsByCoach(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return databaseClient.sql("""
|
||||
SELECT gc.coach_id, COUNT(*) AS count
|
||||
FROM group_course_booking b
|
||||
INNER JOIN group_course gc ON b.course_id = gc.id
|
||||
WHERE b.status != '1' AND b.deleted_at IS NULL
|
||||
WHERE b.status = '0' AND b.deleted_at IS NULL
|
||||
AND gc.deleted_at IS NULL
|
||||
AND gc.start_time >= :startTime AND gc.start_time < :endTime
|
||||
AND gc.end_time >= :startTime AND gc.end_time < :endTime
|
||||
GROUP BY gc.coach_id
|
||||
""")
|
||||
.bind("startTime", startTime)
|
||||
@@ -326,8 +326,8 @@ public class DataStatisticsDao {
|
||||
return databaseClient.sql("""
|
||||
SELECT gc.coach_id, gc.max_members, COUNT(b.id) AS attended
|
||||
FROM group_course gc
|
||||
LEFT JOIN group_course_booking b ON gc.id = b.course_id AND b.status = '2' AND b.deleted_at IS NULL
|
||||
WHERE gc.start_time >= :startTime AND gc.start_time < :endTime
|
||||
LEFT JOIN group_course_booking b ON gc.id = b.course_id AND b.status IN ('2','4','5') AND b.deleted_at IS NULL
|
||||
WHERE gc.end_time >= :startTime AND gc.end_time < :endTime
|
||||
AND gc.status IN ('2', '6') AND gc.deleted_at IS NULL
|
||||
GROUP BY gc.coach_id, gc.id, gc.max_members
|
||||
""")
|
||||
|
||||
+16
-6
@@ -574,8 +574,10 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
|
||||
Map<Long, Collection<FillRateItem>> fillRateMap = tuple.getT5();
|
||||
Map<Long, Long> violationsMap = tuple.getT6();
|
||||
|
||||
// 计算最大授课量(用于归一化)
|
||||
long maxCourses = coursesMap.values().stream().mapToLong(Long::longValue).max().orElse(1L);
|
||||
// 授课量百分位排名归一化
|
||||
List<Long> sortedCourses = coursesMap.values().stream()
|
||||
.sorted().collect(Collectors.toList());
|
||||
int totalCoaches = sortedCourses.size();
|
||||
|
||||
List<CoachPerformance> performances = coaches.keySet().stream()
|
||||
.map(coachId -> {
|
||||
@@ -588,10 +590,18 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
|
||||
double attendanceRate = totalBookings > 0
|
||||
? (double) attended / totalBookings * 100 : 0;
|
||||
double fillRate = calculateFillRate(fillRateMap.getOrDefault(coachId, List.of()));
|
||||
double normalizedCourses = maxCourses > 0
|
||||
? (double) courses / maxCourses * 100 : 0;
|
||||
double compositeScore = normalizedCourses * 0.4
|
||||
+ attendanceRate * 0.3 + fillRate * 0.3;
|
||||
|
||||
// 百分位排名:授课量小于当前教练的教练数 / (总教练数-1) * 100
|
||||
long coachesWithFewer = sortedCourses.stream().filter(v -> v < courses).count();
|
||||
double normalizedCourses = totalCoaches > 1
|
||||
? (double) coachesWithFewer / (totalCoaches - 1) * 100 : 100;
|
||||
|
||||
// 违规扣分:每次违规扣20分,最低0分
|
||||
double violationScore = Math.max(0, 100 - violations * 20);
|
||||
|
||||
double compositeScore = normalizedCourses * 0.35
|
||||
+ attendanceRate * 0.25 + fillRate * 0.25
|
||||
+ violationScore * 0.15;
|
||||
|
||||
return CoachPerformance.builder()
|
||||
.coachId(coachId)
|
||||
|
||||
Reference in New Issue
Block a user