feat: 实现课程包数据统计前后端对齐
- 新增 CoursePackageStatsService 及 GET /packages/{id}/stats 接口
- 数据统计页:总授课次数、教师数、学生数、平均评分、趋势、反馈、最近授课、学生表现
- 课程包列表数据统计列:实时计算 usageCount、teacherCount、avgRating
- 前端 getCourseStats 调用真实 API
Made-with: Cursor
This commit is contained in:
parent
7f0ea0daa4
commit
342456347e
@ -284,9 +284,9 @@ export function republishCourse(id: number): Promise<any> {
|
||||
return api.publishCourse(id) as any;
|
||||
}
|
||||
|
||||
// 获取课程包统计数据 (暂时返回空对象)
|
||||
export function getCourseStats(_id: number | string): Promise<any> {
|
||||
return Promise.resolve({});
|
||||
// 获取课程包统计数据
|
||||
export function getCourseStats(id: number | string): Promise<any> {
|
||||
return http.get(`/v1/admin/packages/${id}/stats`) as Promise<any>;
|
||||
}
|
||||
|
||||
// 获取版本历史 (暂时返回空数组)
|
||||
|
||||
@ -14,8 +14,10 @@ import com.reading.platform.dto.request.CourseCreateRequest;
|
||||
import com.reading.platform.dto.request.CoursePageQueryRequest;
|
||||
import com.reading.platform.dto.request.CourseUpdateRequest;
|
||||
import com.reading.platform.dto.request.CourseRejectRequest;
|
||||
import com.reading.platform.dto.response.CoursePackageStatsResponse;
|
||||
import com.reading.platform.dto.response.CourseResponse;
|
||||
import com.reading.platform.entity.CoursePackage;
|
||||
import com.reading.platform.service.CoursePackageStatsService;
|
||||
import com.reading.platform.service.CoursePackageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@ -24,7 +26,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -40,6 +44,7 @@ import java.util.stream.Collectors;
|
||||
public class AdminCourseController {
|
||||
|
||||
private final CoursePackageService courseService;
|
||||
private final CoursePackageStatsService coursePackageStatsService;
|
||||
|
||||
@PostMapping
|
||||
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.CREATE, description = "创建课程包")
|
||||
@ -96,6 +101,18 @@ public class AdminCourseController {
|
||||
.map(course -> courseService.getCourseByIdWithLessons(course.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 合并实时统计数据(使用次数、教师数、平均评分)
|
||||
List<Long> packageIds = responseList.stream().map(CourseResponse::getId).collect(Collectors.toList());
|
||||
Map<Long, CoursePackageStatsService.PackageStats> statsMap = coursePackageStatsService.getBatchStats(packageIds);
|
||||
for (CourseResponse resp : responseList) {
|
||||
CoursePackageStatsService.PackageStats stats = statsMap.get(resp.getId());
|
||||
if (stats != null) {
|
||||
resp.setUsageCount(stats.usageCount());
|
||||
resp.setTeacherCount(stats.teacherCount());
|
||||
resp.setAvgRating(stats.avgRating());
|
||||
}
|
||||
}
|
||||
|
||||
PageResult<CourseResponse> result = PageResult.of(responseList, page.getTotal(), page.getCurrent(), page.getSize());
|
||||
log.info("课程包列表查询结果,total={}, list={}", result.getTotal(), result.getList().size());
|
||||
return Result.success(result);
|
||||
@ -141,6 +158,12 @@ public class AdminCourseController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/stats")
|
||||
@Operation(summary = "获取课程包数据统计")
|
||||
public Result<CoursePackageStatsResponse> getCourseStats(@PathVariable Long id) {
|
||||
return Result.success(coursePackageStatsService.getStats(id));
|
||||
}
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "获取所有已发布的课程包")
|
||||
public Result<List<CourseResponse>> getAllPublishedCourses() {
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
package com.reading.platform.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程包数据统计响应
|
||||
* 用于超管端「数据统计」页面
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "课程包数据统计响应")
|
||||
public class CoursePackageStatsResponse {
|
||||
|
||||
@Schema(description = "课程包名称")
|
||||
private String courseName;
|
||||
|
||||
@Schema(description = "总授课次数")
|
||||
private Integer totalLessons;
|
||||
|
||||
@Schema(description = "参与教师数")
|
||||
private Integer totalTeachers;
|
||||
|
||||
@Schema(description = "参与学生数")
|
||||
private Integer totalStudents;
|
||||
|
||||
@Schema(description = "平均评分")
|
||||
private BigDecimal avgRating;
|
||||
|
||||
@Schema(description = "授课记录趋势 [{date, count}]")
|
||||
private List<LessonTrendItem> lessonTrend;
|
||||
|
||||
@Schema(description = "教师反馈分布")
|
||||
private FeedbackDistribution feedbackDistribution;
|
||||
|
||||
@Schema(description = "最近授课记录")
|
||||
private List<RecentLessonItem> recentLessons;
|
||||
|
||||
@Schema(description = "学生表现统计")
|
||||
private StudentPerformance studentPerformance;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class LessonTrendItem {
|
||||
private String date;
|
||||
private Integer count;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class FeedbackDistribution {
|
||||
private BigDecimal designQuality;
|
||||
private BigDecimal participation;
|
||||
private BigDecimal goalAchievement;
|
||||
private Integer totalFeedbacks;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class RecentLessonItem {
|
||||
private TeacherInfo teacher;
|
||||
@com.fasterxml.jackson.annotation.JsonProperty("class")
|
||||
private ClassInfo classInfo;
|
||||
private String startDatetime;
|
||||
private Integer actualDuration;
|
||||
private Integer duration;
|
||||
private String status;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TeacherInfo {
|
||||
private Long id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ClassInfo {
|
||||
private Long id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class StudentPerformance {
|
||||
private BigDecimal avgFocus;
|
||||
private BigDecimal avgParticipation;
|
||||
private BigDecimal avgInterest;
|
||||
private BigDecimal avgUnderstanding;
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ public interface CoursePackageMapper extends BaseMapper<CoursePackage> {
|
||||
" SELECT COUNT(DISTINCT l.teacher_id) " +
|
||||
" FROM lesson l " +
|
||||
" WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.status = 'completed' " +
|
||||
" AND l.status = 'COMPLETED' " +
|
||||
") " +
|
||||
"WHERE cp.id = #{coursePackageId}")
|
||||
void updateTeacherCount(@Param("coursePackageId") Long coursePackageId);
|
||||
|
||||
@ -0,0 +1,155 @@
|
||||
package com.reading.platform.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 课程包数据统计 Mapper
|
||||
* 用于超管端「数据统计」页面
|
||||
*/
|
||||
@Mapper
|
||||
public interface CoursePackageStatsMapper {
|
||||
|
||||
/**
|
||||
* 获取课程包概览统计(总授课、教师数、学生数、平均评分)
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @return {totalLessons, totalTeachers, totalStudents, avgRating}
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select(
|
||||
"SELECT " +
|
||||
" COUNT(l.id) AS totalLessons, " +
|
||||
" COUNT(DISTINCT l.teacher_id) AS totalTeachers, " +
|
||||
" COUNT(DISTINCT sr.student_id) AS totalStudents, " +
|
||||
" ROUND(AVG(CASE WHEN lf.id IS NOT NULL THEN " +
|
||||
" (COALESCE(lf.design_quality,0) + COALESCE(lf.participation,0) + COALESCE(lf.goal_achievement,0)) / 3.0 " +
|
||||
" ELSE NULL END), 2) AS avgRating " +
|
||||
"FROM lesson l " +
|
||||
"LEFT JOIN student_record sr ON l.id = sr.lesson_id AND sr.deleted = 0 " +
|
||||
"LEFT JOIN lesson_feedback lf ON l.id = lf.lesson_id AND lf.deleted = 0 " +
|
||||
"WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.deleted = 0 " +
|
||||
" AND l.status = 'COMPLETED'"
|
||||
)
|
||||
Map<String, Object> getOverviewStats(@Param("coursePackageId") Long coursePackageId);
|
||||
|
||||
/**
|
||||
* 获取授课记录趋势(按日期分组,最近 7 天或 30 天)
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return [{date, count}]
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select(
|
||||
"SELECT DATE_FORMAT(l.lesson_date, '%Y-%m-%d') AS date, COUNT(l.id) AS count " +
|
||||
"FROM lesson l " +
|
||||
"WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.deleted = 0 " +
|
||||
" AND l.status = 'COMPLETED' " +
|
||||
" AND l.lesson_date >= #{startDate} " +
|
||||
" AND l.lesson_date <= #{endDate} " +
|
||||
"GROUP BY l.lesson_date " +
|
||||
"ORDER BY l.lesson_date ASC"
|
||||
)
|
||||
List<Map<String, Object>> getLessonTrend(
|
||||
@Param("coursePackageId") Long coursePackageId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate);
|
||||
|
||||
/**
|
||||
* 获取教师反馈分布统计
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @return {designQuality, participation, goalAchievement, totalFeedbacks}
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select(
|
||||
"SELECT " +
|
||||
" ROUND(AVG(lf.design_quality), 2) AS designQuality, " +
|
||||
" ROUND(AVG(lf.participation), 2) AS participation, " +
|
||||
" ROUND(AVG(lf.goal_achievement), 2) AS goalAchievement, " +
|
||||
" COUNT(lf.id) AS totalFeedbacks " +
|
||||
"FROM lesson_feedback lf " +
|
||||
"INNER JOIN lesson l ON lf.lesson_id = l.id " +
|
||||
"WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.deleted = 0 " +
|
||||
" AND lf.deleted = 0"
|
||||
)
|
||||
Map<String, Object> getFeedbackDistribution(@Param("coursePackageId") Long coursePackageId);
|
||||
|
||||
/**
|
||||
* 获取最近授课记录(含教师、班级、时间、时长、状态)
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @param limit 数量限制
|
||||
* @return 授课记录列表
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select(
|
||||
"SELECT " +
|
||||
" l.id AS lessonId, " +
|
||||
" l.teacher_id AS teacherId, " +
|
||||
" t.name AS teacherName, " +
|
||||
" l.class_id AS classId, " +
|
||||
" c.name AS className, " +
|
||||
" l.start_datetime AS startDatetime, " +
|
||||
" l.actual_duration AS actualDuration, " +
|
||||
" l.status AS status " +
|
||||
"FROM lesson l " +
|
||||
"LEFT JOIN teacher t ON l.teacher_id = t.id " +
|
||||
"LEFT JOIN clazz c ON l.class_id = c.id " +
|
||||
"WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.deleted = 0 " +
|
||||
"ORDER BY COALESCE(l.start_datetime, l.created_at) DESC, l.id DESC " +
|
||||
"LIMIT #{limit}"
|
||||
)
|
||||
List<Map<String, Object>> getRecentLessons(
|
||||
@Param("coursePackageId") Long coursePackageId,
|
||||
@Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 获取学生表现统计(专注度、参与度、兴趣度、理解度)
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @return {avgFocus, avgParticipation, avgInterest, avgUnderstanding}
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select(
|
||||
"SELECT " +
|
||||
" ROUND(AVG(sr.focus), 2) AS avgFocus, " +
|
||||
" ROUND(AVG(sr.participation), 2) AS avgParticipation, " +
|
||||
" ROUND(AVG(sr.interest), 2) AS avgInterest, " +
|
||||
" ROUND(AVG(sr.understanding), 2) AS avgUnderstanding " +
|
||||
"FROM student_record sr " +
|
||||
"INNER JOIN lesson l ON sr.lesson_id = l.id " +
|
||||
"WHERE l.course_id = #{coursePackageId} " +
|
||||
" AND l.deleted = 0 " +
|
||||
" AND l.status = 'COMPLETED' " +
|
||||
" AND sr.deleted = 0"
|
||||
)
|
||||
Map<String, Object> getStudentPerformance(@Param("coursePackageId") Long coursePackageId);
|
||||
|
||||
/**
|
||||
* 批量获取课程包概览统计(用于列表页数据统计列)
|
||||
*
|
||||
* @param coursePackageIds 课程包 ID 列表
|
||||
* @return [{coursePackageId, usageCount, teacherCount, avgRating}]
|
||||
*/
|
||||
@org.apache.ibatis.annotations.Select("<script>" +
|
||||
"SELECT " +
|
||||
" l.course_id AS coursePackageId, " +
|
||||
" COUNT(l.id) AS usageCount, " +
|
||||
" COUNT(DISTINCT l.teacher_id) AS teacherCount, " +
|
||||
" ROUND(AVG(CASE WHEN lf.id IS NOT NULL THEN (COALESCE(lf.design_quality,0)+COALESCE(lf.participation,0)+COALESCE(lf.goal_achievement,0))/3.0 END), 2) AS avgRating " +
|
||||
"FROM lesson l " +
|
||||
"LEFT JOIN lesson_feedback lf ON l.id = lf.lesson_id AND lf.deleted = 0 " +
|
||||
"WHERE l.deleted = 0 AND l.status = 'COMPLETED' " +
|
||||
" AND l.course_id IN " +
|
||||
"<foreach item='id' collection='coursePackageIds' open='(' separator=',' close=')'>#{id}</foreach> " +
|
||||
"GROUP BY l.course_id" +
|
||||
"</script>")
|
||||
List<Map<String, Object>> getBatchOverviewStats(
|
||||
@Param("coursePackageIds") List<Long> coursePackageIds);
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.reading.platform.service;
|
||||
|
||||
import com.reading.platform.dto.response.CoursePackageStatsResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 课程包数据统计服务
|
||||
* 用于超管端「数据统计」页面及列表页数据统计列
|
||||
*/
|
||||
public interface CoursePackageStatsService {
|
||||
|
||||
/**
|
||||
* 获取课程包详细统计数据(数据统计页面)
|
||||
*
|
||||
* @param coursePackageId 课程包 ID
|
||||
* @return 统计数据
|
||||
*/
|
||||
CoursePackageStatsResponse getStats(Long coursePackageId);
|
||||
|
||||
/**
|
||||
* 批量获取课程包概览统计(列表页数据统计列)
|
||||
*
|
||||
* @param coursePackageIds 课程包 ID 列表
|
||||
* @return packageId -> {usageCount, teacherCount, avgRating}
|
||||
*/
|
||||
Map<Long, PackageStats> getBatchStats(java.util.List<Long> coursePackageIds);
|
||||
|
||||
/**
|
||||
* 课程包概览统计
|
||||
*/
|
||||
record PackageStats(int usageCount, int teacherCount, java.math.BigDecimal avgRating) {}
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
package com.reading.platform.service.impl;
|
||||
|
||||
import com.reading.platform.common.exception.BusinessException;
|
||||
import com.reading.platform.dto.response.CoursePackageStatsResponse;
|
||||
import com.reading.platform.entity.CoursePackage;
|
||||
import com.reading.platform.mapper.CoursePackageMapper;
|
||||
import com.reading.platform.mapper.CoursePackageStatsMapper;
|
||||
import com.reading.platform.service.CoursePackageStatsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 课程包数据统计服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CoursePackageStatsServiceImpl implements CoursePackageStatsService {
|
||||
|
||||
private final CoursePackageMapper coursePackageMapper;
|
||||
private final CoursePackageStatsMapper statsMapper;
|
||||
|
||||
private static final int TREND_DAYS = 14;
|
||||
private static final int RECENT_LESSONS_LIMIT = 10;
|
||||
|
||||
@Override
|
||||
public CoursePackageStatsResponse getStats(Long coursePackageId) {
|
||||
CoursePackage pkg = coursePackageMapper.selectById(coursePackageId);
|
||||
if (pkg == null) {
|
||||
throw new BusinessException("课程包不存在: " + coursePackageId);
|
||||
}
|
||||
|
||||
Map<String, Object> overview = statsMapper.getOverviewStats(coursePackageId);
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(TREND_DAYS);
|
||||
List<Map<String, Object>> trendRaw = statsMapper.getLessonTrend(coursePackageId, startDate, endDate);
|
||||
Map<String, Object> feedbackRaw = statsMapper.getFeedbackDistribution(coursePackageId);
|
||||
List<Map<String, Object>> recentRaw = statsMapper.getRecentLessons(coursePackageId, RECENT_LESSONS_LIMIT);
|
||||
Map<String, Object> studentPerfRaw = statsMapper.getStudentPerformance(coursePackageId);
|
||||
|
||||
return CoursePackageStatsResponse.builder()
|
||||
.courseName(pkg.getName())
|
||||
.totalLessons(getInt(overview, "totalLessons"))
|
||||
.totalTeachers(getInt(overview, "totalTeachers"))
|
||||
.totalStudents(getInt(overview, "totalStudents"))
|
||||
.avgRating(getBigDecimal(overview, "avgRating"))
|
||||
.lessonTrend(toLessonTrend(trendRaw))
|
||||
.feedbackDistribution(toFeedbackDistribution(feedbackRaw))
|
||||
.recentLessons(toRecentLessons(recentRaw))
|
||||
.studentPerformance(toStudentPerformance(studentPerfRaw))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, PackageStats> getBatchStats(List<Long> coursePackageIds) {
|
||||
if (coursePackageIds == null || coursePackageIds.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
List<Map<String, Object>> rows = statsMapper.getBatchOverviewStats(coursePackageIds);
|
||||
Map<Long, PackageStats> result = new HashMap<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
Long id = getLong(row, "coursePackageId");
|
||||
if (id != null) {
|
||||
result.put(id, new PackageStats(
|
||||
getInt(row, "usageCount"),
|
||||
getInt(row, "teacherCount"),
|
||||
getBigDecimal(row, "avgRating")
|
||||
));
|
||||
}
|
||||
}
|
||||
// 确保所有 ID 都有条目(无数据时为 0)
|
||||
for (Long id : coursePackageIds) {
|
||||
result.putIfAbsent(id, new PackageStats(0, 0, BigDecimal.ZERO));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<CoursePackageStatsResponse.LessonTrendItem> toLessonTrend(List<Map<String, Object>> raw) {
|
||||
if (raw == null) return List.of();
|
||||
return raw.stream()
|
||||
.map(r -> CoursePackageStatsResponse.LessonTrendItem.builder()
|
||||
.date(getString(r, "date"))
|
||||
.count(getInt(r, "count"))
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private CoursePackageStatsResponse.FeedbackDistribution toFeedbackDistribution(Map<String, Object> raw) {
|
||||
if (raw == null || getInt(raw, "totalFeedbacks") == 0) {
|
||||
return null;
|
||||
}
|
||||
return CoursePackageStatsResponse.FeedbackDistribution.builder()
|
||||
.designQuality(getBigDecimal(raw, "designQuality"))
|
||||
.participation(getBigDecimal(raw, "participation"))
|
||||
.goalAchievement(getBigDecimal(raw, "goalAchievement"))
|
||||
.totalFeedbacks(getInt(raw, "totalFeedbacks"))
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<CoursePackageStatsResponse.RecentLessonItem> toRecentLessons(List<Map<String, Object>> raw) {
|
||||
if (raw == null) return List.of();
|
||||
List<CoursePackageStatsResponse.RecentLessonItem> items = new ArrayList<>();
|
||||
for (Map<String, Object> r : raw) {
|
||||
Object startDt = r.get("startDatetime");
|
||||
String startDatetime = startDt != null ? startDt.toString() : null;
|
||||
items.add(CoursePackageStatsResponse.RecentLessonItem.builder()
|
||||
.teacher(CoursePackageStatsResponse.TeacherInfo.builder()
|
||||
.id(getLong(r, "teacherId"))
|
||||
.name(getString(r, "teacherName"))
|
||||
.build())
|
||||
.classInfo(CoursePackageStatsResponse.ClassInfo.builder()
|
||||
.id(getLong(r, "classId"))
|
||||
.name(getString(r, "className"))
|
||||
.build())
|
||||
.startDatetime(startDatetime)
|
||||
.actualDuration(getInt(r, "actualDuration"))
|
||||
.duration(null)
|
||||
.status(getString(r, "status"))
|
||||
.build());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private CoursePackageStatsResponse.StudentPerformance toStudentPerformance(Map<String, Object> raw) {
|
||||
if (raw == null) return null;
|
||||
return CoursePackageStatsResponse.StudentPerformance.builder()
|
||||
.avgFocus(getBigDecimal(raw, "avgFocus"))
|
||||
.avgParticipation(getBigDecimal(raw, "avgParticipation"))
|
||||
.avgInterest(getBigDecimal(raw, "avgInterest"))
|
||||
.avgUnderstanding(getBigDecimal(raw, "avgUnderstanding"))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static int getInt(Map<String, Object> m, String key) {
|
||||
Object v = m != null ? m.get(key) : null;
|
||||
if (v == null) return 0;
|
||||
if (v instanceof Number) return ((Number) v).intValue();
|
||||
try {
|
||||
return Integer.parseInt(v.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Long getLong(Map<String, Object> m, String key) {
|
||||
Object v = m != null ? m.get(key) : null;
|
||||
if (v == null) return null;
|
||||
if (v instanceof Number) return ((Number) v).longValue();
|
||||
try {
|
||||
return Long.parseLong(v.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getString(Map<String, Object> m, String key) {
|
||||
Object v = m != null ? m.get(key) : null;
|
||||
return v != null ? v.toString() : null;
|
||||
}
|
||||
|
||||
private static BigDecimal getBigDecimal(Map<String, Object> m, String key) {
|
||||
Object v = m != null ? m.get(key) : null;
|
||||
if (v == null) return BigDecimal.ZERO;
|
||||
if (v instanceof BigDecimal) return (BigDecimal) v;
|
||||
if (v instanceof Number) return BigDecimal.valueOf(((Number) v).doubleValue()).setScale(2, RoundingMode.HALF_UP);
|
||||
try {
|
||||
return new BigDecimal(v.toString()).setScale(2, RoundingMode.HALF_UP);
|
||||
} catch (NumberFormatException e) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user