|  | @@ -66,6 +66,7 @@ import java.time.*;
 | 
	
		
			
				|  |  |  import java.time.format.DateTimeFormatter;
 | 
	
		
			
				|  |  |  import java.time.temporal.TemporalAdjusters;
 | 
	
		
			
				|  |  |  import java.util.*;
 | 
	
		
			
				|  |  | +import java.util.concurrent.CompletableFuture;
 | 
	
		
			
				|  |  |  import java.util.stream.Collectors;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static com.ym.mec.biz.dal.entity.CourseSchedule.CourseScheduleType.MUSIC_NETWORK;
 | 
	
	
		
			
				|  | @@ -3925,6 +3926,7 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
 | 
	
		
			
				|  |  |          courseAttendanceDetailHeadInfoDto.setLatestAttendanceTime(studentAttendanceDao.findLatestAttendanceDate(courseScheduleId));
 | 
	
		
			
				|  |  |          courseAttendanceDetailHeadInfoDto.setLeaveStudentNum(studentAttendanceDao.getStudentNumByStatus(courseScheduleId, StudentAttendanceStatusEnum.LEAVE));
 | 
	
		
			
				|  |  |          courseAttendanceDetailHeadInfoDto.setTruantStudentNum(studentAttendanceDao.getStudentNumByStatus(courseScheduleId, StudentAttendanceStatusEnum.TRUANT));
 | 
	
		
			
				|  |  | +        courseAttendanceDetailHeadInfoDto.setLateStudentNum(studentAttendanceDao.getStudentNumByStatus(courseScheduleId, StudentAttendanceStatusEnum.LATE));
 | 
	
		
			
				|  |  |          return courseAttendanceDetailHeadInfoDto;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -5982,12 +5984,10 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
 | 
	
		
			
				|  |  |  		if (courseSchedule.getTeachMode() == TeachModeEnum.OFFLINE) {
 | 
	
		
			
				|  |  |  			throw new BizException("请前往线下教室");
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		List<CourseScheduleWrapper.CourseScheduleTime> courseScheduleTimes = Lists.newArrayList();
 | 
	
		
			
				|  |  | -		courseScheduleTimes.add(CourseScheduleWrapper.CourseScheduleTime.builder()
 | 
	
		
			
				|  |  | -				.startTime(getDateTime(courseSchedule.getClassDate(), courseSchedule.getStartClassTime()).getMillis())
 | 
	
		
			
				|  |  | -				.endTime(getDateTime(courseSchedule.getClassDate(), courseSchedule.getEndClassTime()).getMillis())
 | 
	
		
			
				|  |  | -				.build());
 | 
	
		
			
				|  |  | +		// 课程开始时间
 | 
	
		
			
				|  |  | +		courseSchedule.setStartClassTime(getDateTime(courseSchedule.getClassDate(), courseSchedule.getStartClassTime()).toDate());
 | 
	
		
			
				|  |  | +		// 课程结束时间
 | 
	
		
			
				|  |  | +		courseSchedule.setEndClassTime(getDateTime(courseSchedule.getClassDate(), courseSchedule.getEndClassTime()).toDate());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		// 是否是连堂课
 | 
	
		
			
				|  |  |  		String continueCourseTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME, courseSchedule.getTenantId());
 | 
	
	
		
			
				|  | @@ -5996,36 +5996,129 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		// 连堂课标准:同一个老师,同一天,同一课程组,连续上课
 | 
	
		
			
				|  |  | +		List<CourseSchedule> continuousCourse = courseScheduleDao.getTeacherContinuousCourse(courseSchedule.getClassGroupId(),
 | 
	
		
			
				|  |  | +				courseSchedule.getClassDate(), courseSchedule.getActualTeacherId());
 | 
	
		
			
				|  |  | +		for (CourseSchedule item : continuousCourse) {
 | 
	
		
			
				|  |  | +			// 设置开始时间
 | 
	
		
			
				|  |  | +			item.setStartClassTime(getDateTime(item.getClassDate(), item.getStartClassTime()).toDate());
 | 
	
		
			
				|  |  | +			// 设置结束时间
 | 
	
		
			
				|  |  | +			item.setEndClassTime(getDateTime(item.getClassDate(), item.getEndClassTime()).toDate());
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// 课程按开始时间排序
 | 
	
		
			
				|  |  | +		continuousCourse = continuousCourse.stream()
 | 
	
		
			
				|  |  | +				.sorted(Comparator.comparing(CourseSchedule::getStartClassTime))
 | 
	
		
			
				|  |  | +				.collect(Collectors.toList());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		// 以当前课程为分界线,分为两个集合
 | 
	
		
			
				|  |  | +		List<CourseSchedule> beforeCourse = Lists.newArrayList();
 | 
	
		
			
				|  |  | +		List<CourseSchedule> afterCourse = Lists.newArrayList();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		CourseSchedule schedule;
 | 
	
		
			
				|  |  | +		for (CourseSchedule item : continuousCourse) {
 | 
	
		
			
				|  |  | +			if (item.getId().longValue() == courseSchedule.getId()) {
 | 
	
		
			
				|  |  | +				continue;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		CourseSchedule schedule = courseSchedule;
 | 
	
		
			
				|  |  | -		// 如果当前课程是连堂课,那么获取第一节课的课程编号
 | 
	
		
			
				|  |  | -		while (true) {
 | 
	
		
			
				|  |  | -			String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
 | 
	
		
			
				|  |  | -			String startClassTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
 | 
	
		
			
				|  |  | -			schedule = courseScheduleDao.getFirstCourse(schedule.getClassGroupId(), classDate + " " + startClassTime, schedule.getActualTeacherId(), continueCourseTime);
 | 
	
		
			
				|  |  | -			if (schedule != null) {
 | 
	
		
			
				|  |  | -				courseScheduleTimes.add(CourseScheduleWrapper.CourseScheduleTime.builder()
 | 
	
		
			
				|  |  | -						.startTime(getDateTime(schedule.getClassDate(), schedule.getStartClassTime()).getMillis())
 | 
	
		
			
				|  |  | -						.endTime(getDateTime(schedule.getClassDate(), schedule.getEndClassTime()).getMillis())
 | 
	
		
			
				|  |  | -						.build());
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | +			schedule = JSON.parseObject(JSON.toJSONString(item), CourseSchedule.class);
 | 
	
		
			
				|  |  | +			if (item.getEndClassTime().getTime() <= courseSchedule.getStartClassTime().getTime()) {
 | 
	
		
			
				|  |  | +				beforeCourse.add(schedule);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			if (courseSchedule.getEndClassTime().getTime() <= item.getStartClassTime().getTime()) {
 | 
	
		
			
				|  |  | +				afterCourse.add(schedule);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (courseScheduleTimes.size() <= 1) {
 | 
	
		
			
				|  |  | +		// 当前课程之前连堂课
 | 
	
		
			
				|  |  | +		beforeCourse.add(JSON.parseObject(JSON.toJSONString(courseSchedule), CourseSchedule.class));
 | 
	
		
			
				|  |  | +		// 当前课程之后连堂课
 | 
	
		
			
				|  |  | +		afterCourse.add(0, JSON.parseObject(JSON.toJSONString(courseSchedule), CourseSchedule.class));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			// 定时任务更新课程状态为进行中,判断当前课程状态是否匹配
 | 
	
		
			
				|  |  | -			if (CourseStatusEnum.NOT_START == courseSchedule.getStatus()) {
 | 
	
		
			
				|  |  | -				throw new BizException("直播课暂未开启,请稍后重试");
 | 
	
		
			
				|  |  | +		// 课程前连堂状态
 | 
	
		
			
				|  |  | +		updateContinuousCourseStatus(continueCourseTime, beforeCourse);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		List<CourseSchedule> newCourseSchedules = Lists.newArrayList();
 | 
	
		
			
				|  |  | +		// 课程连堂状态
 | 
	
		
			
				|  |  | +		if (beforeCourse.get(beforeCourse.size() - 1).getContinuousCourse()) {
 | 
	
		
			
				|  |  | +			List<CourseSchedule> collect = beforeCourse.stream()
 | 
	
		
			
				|  |  | +					.filter(CourseSchedule::getContinuousCourse)
 | 
	
		
			
				|  |  | +					.collect(Collectors.toList());
 | 
	
		
			
				|  |  | +			if (CollectionUtils.isNotEmpty(collect)) {
 | 
	
		
			
				|  |  | +				newCourseSchedules.addAll(collect);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			if (CourseStatusEnum.OVER == courseSchedule.getStatus()) {
 | 
	
		
			
				|  |  | -				throw new BizException("直播课已结束");
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// 课程后连堂状态
 | 
	
		
			
				|  |  | +		updateContinuousCourseStatus(continueCourseTime, afterCourse);
 | 
	
		
			
				|  |  | +		if (afterCourse.get(0).getContinuousCourse()) {
 | 
	
		
			
				|  |  | +			List<CourseSchedule> collect = afterCourse.stream()
 | 
	
		
			
				|  |  | +					.filter(CourseSchedule::getContinuousCourse)
 | 
	
		
			
				|  |  | +					.collect(Collectors.toList());
 | 
	
		
			
				|  |  | +			if (CollectionUtils.isNotEmpty(collect)) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				if (CollectionUtils.isEmpty(newCourseSchedules)) {
 | 
	
		
			
				|  |  | +					newCourseSchedules.addAll(collect);
 | 
	
		
			
				|  |  | +				} else {
 | 
	
		
			
				|  |  | +					newCourseSchedules.addAll(collect.subList(1, collect.size()));
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		List<CourseScheduleWrapper.CourseScheduleTime> courseScheduleTimes = Lists.newArrayList();
 | 
	
		
			
				|  |  | +		if (CollectionUtils.isNotEmpty(newCourseSchedules)) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			// 连党课程信息
 | 
	
		
			
				|  |  | +			for (CourseSchedule item : newCourseSchedules) {
 | 
	
		
			
				|  |  | +				courseScheduleTimes.add(CourseScheduleWrapper.CourseScheduleTime.builder()
 | 
	
		
			
				|  |  | +						.startTime(item.getStartClassTime().getTime())
 | 
	
		
			
				|  |  | +						.endTime(item.getEndClassTime().getTime())
 | 
	
		
			
				|  |  | +						.build());
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			// 获取连堂课第一节信息
 | 
	
		
			
				|  |  | +			courseSchedule = newCourseSchedules.get(0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			CourseSchedule finalCourseSchedule = courseSchedule;
 | 
	
		
			
				|  |  | +			CompletableFuture.runAsync(() -> {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				List<Long> collect = newCourseSchedules.stream()
 | 
	
		
			
				|  |  | +						.map(CourseSchedule::getId)
 | 
	
		
			
				|  |  | +						.distinct().collect(Collectors.toList());
 | 
	
		
			
				|  |  | +				try {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					// 关联直播间编号
 | 
	
		
			
				|  |  | +					courseScheduleDao.batchUpdateLiveRoomId(collect, finalCourseSchedule.getLiveRoomId());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					collect = newCourseSchedules.subList(0, newCourseSchedules.size() - 1).stream()
 | 
	
		
			
				|  |  | +							.map(CourseSchedule::getId)
 | 
	
		
			
				|  |  | +							.distinct().collect(Collectors.toList());
 | 
	
		
			
				|  |  | +					// 更新连堂课状态
 | 
	
		
			
				|  |  | +					courseScheduleDao.batchUpdateContinuousCourse(collect, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				} catch (Exception e) {
 | 
	
		
			
				|  |  | +				    log.error("getLiveCourseScheduleTime courseScheduleIds={}", collect, e);
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			// 当前课程信息
 | 
	
		
			
				|  |  | +			courseScheduleTimes.add(CourseScheduleWrapper.CourseScheduleTime.builder()
 | 
	
		
			
				|  |  | +					.startTime(courseSchedule.getStartClassTime().getTime())
 | 
	
		
			
				|  |  | +					.endTime(courseSchedule.getEndClassTime().getTime())
 | 
	
		
			
				|  |  | +					.build());
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// 定时任务更新课程状态为进行中,判断当前课程状态是否匹配
 | 
	
		
			
				|  |  | +		if (CourseStatusEnum.NOT_START == courseSchedule.getStatus()) {
 | 
	
		
			
				|  |  | +			throw new BizException("直播课暂未开启,请稍后重试");
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if (CourseStatusEnum.OVER == courseSchedule.getStatus()) {
 | 
	
		
			
				|  |  | +			throw new BizException("直播课已结束");
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		// 直播间配置
 | 
	
		
			
				|  |  |  		ImLiveBroadcastRoom liveBroadcastRoom = imLiveBroadcastRoomService.lambdaQuery()
 | 
	
		
			
				|  |  |  				.eq(ImLiveBroadcastRoom::getRoomUid, courseSchedule.getLiveRoomId())
 | 
	
	
		
			
				|  | @@ -6050,6 +6143,39 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  | +	 * 更新当前课程连堂状态
 | 
	
		
			
				|  |  | +	 * @param continueCourseTime 连堂时间间隔
 | 
	
		
			
				|  |  | +	 * @param continuousCourse 连堂课程
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	private void updateContinuousCourseStatus(String continueCourseTime, List<CourseSchedule> continuousCourse) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// continuousCourse对象相邻两个时间比较,如果时间间隔小于5分钟,那么合并为连堂课
 | 
	
		
			
				|  |  | +		DateTime startTime;
 | 
	
		
			
				|  |  | +		DateTime endTime;
 | 
	
		
			
				|  |  | +		for (int i = 0; i < continuousCourse.size(); i++) {
 | 
	
		
			
				|  |  | +			CourseSchedule current = continuousCourse.get(i);
 | 
	
		
			
				|  |  | +			CourseSchedule next = null;
 | 
	
		
			
				|  |  | +			if (i + 1 < continuousCourse.size()) {
 | 
	
		
			
				|  |  | +				next = continuousCourse.get(i + 1);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			if (next != null) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				startTime = getDateTime(current.getClassDate(), current.getEndClassTime());
 | 
	
		
			
				|  |  | +				endTime = getDateTime(next.getClassDate(), next.getStartClassTime());
 | 
	
		
			
				|  |  | +				// 如果当前课程结束时间和下一节课开始时间间隔小于5分钟,那么合并时间
 | 
	
		
			
				|  |  | +				if (startTime.isBefore(endTime)
 | 
	
		
			
				|  |  | +						&& startTime.plusMinutes(Integer.parseInt(continueCourseTime) + 1).isAfter(endTime)) {
 | 
	
		
			
				|  |  | +					// 上节连堂状态
 | 
	
		
			
				|  |  | +					current.setContinuousCourse(true);
 | 
	
		
			
				|  |  | +					// 下节连堂状态
 | 
	
		
			
				|  |  | +					next.setContinuousCourse(true);
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  |  	 * 课程开始时间
 | 
	
		
			
				|  |  |  	 * @param day 开始天
 | 
	
		
			
				|  |  |  	 * @param time 开始时间
 |