|
|
@@ -0,0 +1,839 @@
|
|
|
+package com.sckw.system.service;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.sckw.core.exception.SystemException;
|
|
|
+import com.sckw.core.model.constant.Global;
|
|
|
+import com.sckw.core.web.constant.HttpStatus;
|
|
|
+import com.sckw.core.web.context.LoginUserHolder;
|
|
|
+import com.sckw.core.web.response.result.PageDataResult;
|
|
|
+import com.sckw.system.dao.KwsUserDao;
|
|
|
+import com.sckw.system.model.*;
|
|
|
+import com.sckw.system.model.pojo.CourseTypeEnum;
|
|
|
+import com.sckw.system.model.pojo.QuestionTypeEnum;
|
|
|
+import com.sckw.system.model.pojo.TrainStatusEnum;
|
|
|
+import com.sckw.system.model.vo.req.TrainAppExamSubmitReqVo;
|
|
|
+import com.sckw.system.model.vo.req.TrainAppListReqVo;
|
|
|
+import com.sckw.system.model.vo.req.TrainCourseStudyReqVo;
|
|
|
+import com.sckw.system.model.vo.res.*;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class KwsTrainAppService {
|
|
|
+
|
|
|
+ private final TTrainService tTrainService;
|
|
|
+ private final TTrainRecordService tTrainRecordService;
|
|
|
+ private final TTrainCourseService tTrainCourseService;
|
|
|
+ private final TTrainExamService tTrainExamService;
|
|
|
+ private final TCourseService tCourseService;
|
|
|
+ private final TCourseRecordService tCourseRecordService;
|
|
|
+ private final TExamService tExamService;
|
|
|
+ private final TExamLogService tExamLogService;
|
|
|
+ private final TExamRecordService tExamRecordService;
|
|
|
+ private final TQuestionService tQuestionService;
|
|
|
+ private final TQuestionItemService tQuestionItemService;
|
|
|
+ private final KwsCourseService kwsCourseService;
|
|
|
+ private final KwsUserDao kwsUserDao;
|
|
|
+
|
|
|
+ private Long getDriverId() {
|
|
|
+ KwsUser kwsUser = kwsUserDao.selectById(LoginUserHolder.getUserId());
|
|
|
+ if (Objects.nonNull(kwsUser)) {
|
|
|
+ return Long.parseLong(kwsUser.getDriverId());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public TrainAppListResVo listTrainTasks(TrainAppListReqVo reqVo) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+ if (Objects.isNull(driverId)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "未找到司机id");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<TTrainRecord> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(TTrainRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TTrainRecord::getDriverId, driverId)
|
|
|
+ .orderByDesc(TTrainRecord::getCreateTime);
|
|
|
+ if (reqVo.getStatus() != null && reqVo.getStatus() > 0) {
|
|
|
+ wrapper.eq(TTrainRecord::getStatus, reqVo.getStatus());
|
|
|
+ }
|
|
|
+
|
|
|
+ Page<TTrainRecord> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
|
|
|
+ Page<TTrainRecord> recordPage = tTrainRecordService.page(page, wrapper);
|
|
|
+ List<TTrainRecord> records = recordPage.getRecords();
|
|
|
+
|
|
|
+ Map<Long, TTrain> trainMap = loadTrainMap(records.stream().map(TTrainRecord::getTrainId).toList());
|
|
|
+ Map<Long, List<Long>> trainCourseIdsMap = loadTrainCourseIdsMap(trainMap.keySet());
|
|
|
+ Map<Long, TCourse> courseMap = loadCourseMap(trainCourseIdsMap.values().stream().flatMap(List::stream).distinct().toList());
|
|
|
+ Map<String, Integer> learnStatusMap = loadLearnStatusMap(driverId, trainMap.keySet());
|
|
|
+
|
|
|
+ List<TrainAppTaskResVo> taskVos = records.stream().map(r -> toTask(r, trainMap.get(r.getTrainId()), trainCourseIdsMap.getOrDefault(r.getTrainId(), Collections.emptyList()), courseMap, learnStatusMap)).toList();
|
|
|
+
|
|
|
+ TrainAppListResVo resVo = new TrainAppListResVo();
|
|
|
+ resVo.setProgressRate(calcOverallProgressRate(driverId));
|
|
|
+ resVo.setTotalTask((int) recordPage.getTotal());
|
|
|
+ resVo.setPage(PageDataResult.of(page, taskVos));
|
|
|
+ return resVo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public TrainAppDetailResVo trainDetail(Long trainId) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+
|
|
|
+ TTrainRecord trainRecord = getDriverTrainRecord(trainId, driverId);
|
|
|
+ TTrain train = tTrainService.getById(trainId);
|
|
|
+ if (train == null || !Objects.equals(train.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "培训计划不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> courseIds = tTrainCourseService.lambdaQuery()
|
|
|
+ .select(TTrainCourse::getCourseId)
|
|
|
+ .eq(TTrainCourse::getTrainId, trainId)
|
|
|
+ .list()
|
|
|
+ .stream()
|
|
|
+ .map(TTrainCourse::getCourseId)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .toList();
|
|
|
+
|
|
|
+ Map<Long, TCourse> courseMap = loadCourseMap(courseIds);
|
|
|
+ Map<String, Integer> learnStatusMap = loadLearnStatusMap(driverId, Set.of(trainId));
|
|
|
+ List<TrainAppTaskCourseResVo> courses = buildCourseList(courseIds, courseMap, learnStatusMap, trainId);
|
|
|
+
|
|
|
+ TrainAppDetailResVo res = new TrainAppDetailResVo();
|
|
|
+ res.setTrainId(trainId);
|
|
|
+ res.setTrainName(train.getName());
|
|
|
+ res.setStartDate(train.getStartDate());
|
|
|
+ res.setEndDate(train.getEndDate());
|
|
|
+ res.setStatus(trainRecord.getStatus());
|
|
|
+ res.setStatusName(TrainStatusEnum.getNameByCode(trainRecord.getStatus()));
|
|
|
+ res.setLearnedCourse(defaultInt(trainRecord.getFinishCourse()));
|
|
|
+ res.setSumCourse(defaultInt(trainRecord.getSumCourse()));
|
|
|
+ res.setCourses(courses);
|
|
|
+
|
|
|
+ TrainAppDetailResVo.Exam exam = resolveTrainExam(trainId);
|
|
|
+ res.setExam(exam);
|
|
|
+
|
|
|
+ boolean allCourseDone = Objects.equals(res.getLearnedCourse(), res.getSumCourse()) && res.getSumCourse() > 0;
|
|
|
+ res.setShowExam(Objects.equals(train.getExamFlag(),1) && allCourseDone && exam != null);
|
|
|
+
|
|
|
+ if (Objects.equals(train.getExamFlag(),1) && exam != null) {
|
|
|
+ boolean passed = checkExamPassed(trainId, exam.getExamId(), driverId, exam.getPassRate());
|
|
|
+ res.setExamPassed(passed);
|
|
|
+ } else {
|
|
|
+ res.setExamPassed(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Boolean startCourse(TrainCourseStudyReqVo reqVo) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+ ensureCourseInTrain(reqVo.getTrainId(), reqVo.getCourseId());
|
|
|
+
|
|
|
+ TTrainRecord trainRecord = getDriverTrainRecord(reqVo.getTrainId(), driverId);
|
|
|
+ if (Objects.equals(trainRecord.getStatus(), 3)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "培训任务已结束");
|
|
|
+ }
|
|
|
+
|
|
|
+ TCourseRecord record = tCourseRecordService.lambdaQuery()
|
|
|
+ .eq(TCourseRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TCourseRecord::getTrainId, reqVo.getTrainId())
|
|
|
+ .eq(TCourseRecord::getCourseId, reqVo.getCourseId())
|
|
|
+ .eq(TCourseRecord::getDriverId, driverId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+
|
|
|
+ if (record == null) {
|
|
|
+ record = new TCourseRecord();
|
|
|
+ record.setTrainId(reqVo.getTrainId());
|
|
|
+ record.setCourseId(reqVo.getCourseId());
|
|
|
+ record.setDriverId(driverId);
|
|
|
+ record.setStartTime(now);
|
|
|
+ record.setEndTime(null);
|
|
|
+ record.setStatus(0);
|
|
|
+ record.setCreateBy(userId);
|
|
|
+ record.setCreateTime(now);
|
|
|
+ record.setUpdateBy(userId);
|
|
|
+ record.setUpdateTime(now);
|
|
|
+ record.setDelFlag(Global.UN_DELETED);
|
|
|
+ if (!tCourseRecordService.save(record)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "开始学习失败");
|
|
|
+ }
|
|
|
+ } else if (Objects.equals(record.getStatus(), 1)) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ boolean updated = tCourseRecordService.lambdaUpdate()
|
|
|
+ .eq(TCourseRecord::getId, record.getId())
|
|
|
+ .set(TCourseRecord::getStartTime, record.getStartTime() == null ? now : record.getStartTime())
|
|
|
+ .set(TCourseRecord::getUpdateBy, userId)
|
|
|
+ .set(TCourseRecord::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ if (!updated) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "开始学习失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTrainRecordStatusOnLearning(reqVo.getTrainId(), driverId);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Boolean finishCourse(TrainCourseStudyReqVo reqVo) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+// ensureDriverInTrain(reqVo.getTrainId(), driverId);
|
|
|
+ ensureCourseInTrain(reqVo.getTrainId(), reqVo.getCourseId());
|
|
|
+
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+
|
|
|
+ TCourseRecord record = tCourseRecordService.lambdaQuery()
|
|
|
+ .eq(TCourseRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TCourseRecord::getTrainId, reqVo.getTrainId())
|
|
|
+ .eq(TCourseRecord::getCourseId, reqVo.getCourseId())
|
|
|
+ .eq(TCourseRecord::getDriverId, driverId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+
|
|
|
+ if (record == null) {
|
|
|
+ record = new TCourseRecord();
|
|
|
+ record.setTrainId(reqVo.getTrainId());
|
|
|
+ record.setCourseId(reqVo.getCourseId());
|
|
|
+ record.setDriverId(driverId);
|
|
|
+ record.setStartTime(now);
|
|
|
+ record.setEndTime(now);
|
|
|
+ record.setStatus(1);
|
|
|
+ record.setCreateBy(userId);
|
|
|
+ record.setCreateTime(now);
|
|
|
+ record.setUpdateBy(userId);
|
|
|
+ record.setUpdateTime(now);
|
|
|
+ record.setDelFlag(Global.UN_DELETED);
|
|
|
+ if (!tCourseRecordService.save(record)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "完成课程失败");
|
|
|
+ }
|
|
|
+ } else if (!Objects.equals(record.getStatus(), 1)) {
|
|
|
+ boolean updated = tCourseRecordService.lambdaUpdate()
|
|
|
+ .eq(TCourseRecord::getId, record.getId())
|
|
|
+ .set(TCourseRecord::getStatus, 1)
|
|
|
+ .set(TCourseRecord::getEndTime, now)
|
|
|
+ .set(TCourseRecord::getUpdateBy, userId)
|
|
|
+ .set(TCourseRecord::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ if (!updated) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "完成课程失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ syncTrainProgress(reqVo.getTrainId(), driverId, now, userId);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public TrainAppExamStartResVo startExam(Long trainId) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+// ensureDriverInTrain(trainId, driverId);
|
|
|
+
|
|
|
+ TTrain train = tTrainService.getById(trainId);
|
|
|
+ if (train == null || !Objects.equals(train.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "培训计划不存在");
|
|
|
+ }
|
|
|
+ if (!Objects.equals(train.getExamFlag(), 1)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "当前培训无需考试");
|
|
|
+ }
|
|
|
+
|
|
|
+ TTrainRecord trainRecord = getDriverTrainRecord(trainId, driverId);
|
|
|
+ if (!Objects.equals(trainRecord.getFinishCourse(), trainRecord.getSumCourse())) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "请先完成全部课程");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long examId = resolveTrainExamId(trainId);
|
|
|
+ if (examId == null) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "培训考试未配置");
|
|
|
+ }
|
|
|
+
|
|
|
+ TExam exam = tExamService.getById(examId);
|
|
|
+ if (exam == null || !Objects.equals(exam.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "考试不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ TExamLog existing = tExamLogService.lambdaQuery()
|
|
|
+ .eq(TExamLog::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TExamLog::getTrainId, trainId)
|
|
|
+ .eq(TExamLog::getExamId, examId)
|
|
|
+ .eq(TExamLog::getDriverId, driverId)
|
|
|
+ .eq(TExamLog::getExamFlag, 1)
|
|
|
+ .orderByDesc(TExamLog::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+
|
|
|
+ TExamLog log = existing;
|
|
|
+ if (log == null) {
|
|
|
+ log = new TExamLog();
|
|
|
+ log.setTrainId(trainId);
|
|
|
+ log.setExamId(examId);
|
|
|
+ log.setDriverId(driverId);
|
|
|
+ log.setExamFlag(1);
|
|
|
+ log.setStartTime(now);
|
|
|
+ log.setEndTime(null);
|
|
|
+ log.setRightAmount(0);
|
|
|
+ log.setAmount(exam.getAmount());
|
|
|
+ log.setRightRate(0);
|
|
|
+ log.setCreateBy(userId);
|
|
|
+ log.setCreateTime(now);
|
|
|
+ log.setUpdateBy(userId);
|
|
|
+ log.setUpdateTime(now);
|
|
|
+ log.setDelFlag(Global.UN_DELETED);
|
|
|
+ if (!tExamLogService.save(log)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "开始考试失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ TrainAppExamStartResVo res = new TrainAppExamStartResVo();
|
|
|
+ res.setLogId(log.getId());
|
|
|
+ res.setExamId(examId);
|
|
|
+ res.setExamName(exam.getName());
|
|
|
+ res.setExamDuration(exam.getExamDuration());
|
|
|
+ res.setPassRate(exam.getPassRate());
|
|
|
+ res.setAmount(exam.getAmount());
|
|
|
+ res.setStartTime(log.getStartTime());
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ public TrainAppExamPaperResVo examPaper(Long trainId, Long logId) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+// ensureDriverInTrain(trainId, driverId);
|
|
|
+
|
|
|
+ Long examId = resolveTrainExamId(trainId);
|
|
|
+ if (examId == null) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "培训考试未配置");
|
|
|
+ }
|
|
|
+ TExam exam = tExamService.getById(examId);
|
|
|
+ if (exam == null || !Objects.equals(exam.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "考试不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long resolvedLogId = logId;
|
|
|
+ if (resolvedLogId == null) {
|
|
|
+ TExamLog newest = tExamLogService.lambdaQuery()
|
|
|
+ .eq(TExamLog::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TExamLog::getTrainId, trainId)
|
|
|
+ .eq(TExamLog::getExamId, examId)
|
|
|
+ .eq(TExamLog::getDriverId, driverId)
|
|
|
+ .orderByDesc(TExamLog::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+ resolvedLogId = newest == null ? null : newest.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<TQuestion> questions = tQuestionService.lambdaQuery()
|
|
|
+ .eq(TQuestion::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TQuestion::getExamId, examId)
|
|
|
+ .orderByAsc(TQuestion::getId)
|
|
|
+ .list();
|
|
|
+
|
|
|
+ Map<Long, List<TQuestionItem>> optionMap;
|
|
|
+ if (CollUtil.isNotEmpty(questions)) {
|
|
|
+ List<Long> qIds = questions.stream().map(TQuestion::getId).filter(Objects::nonNull).toList();
|
|
|
+ List<TQuestionItem> items = tQuestionItemService.lambdaQuery()
|
|
|
+ .eq(TQuestionItem::getDelFlag, Global.UN_DELETED)
|
|
|
+ .in(TQuestionItem::getQuestionId, qIds)
|
|
|
+ .orderByAsc(TQuestionItem::getSort)
|
|
|
+ .orderByAsc(TQuestionItem::getId)
|
|
|
+ .list();
|
|
|
+ optionMap = items.stream().collect(Collectors.groupingBy(TQuestionItem::getQuestionId));
|
|
|
+ } else {
|
|
|
+ optionMap = Collections.emptyMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<TrainAppExamPaperResVo.Question> qVos = questions.stream().map(q -> {
|
|
|
+ TrainAppExamPaperResVo.Question qVo = new TrainAppExamPaperResVo.Question();
|
|
|
+ qVo.setQuestionId(q.getId());
|
|
|
+ qVo.setName(q.getName());
|
|
|
+ qVo.setType(q.getType());
|
|
|
+ qVo.setTypeName(QuestionTypeEnum.getNameByCode(q.getType()));
|
|
|
+ List<TQuestionItem> items = optionMap.getOrDefault(q.getId(), Collections.emptyList());
|
|
|
+ List<TrainAppExamPaperResVo.Option> opts = items.stream().map(item -> {
|
|
|
+ TrainAppExamPaperResVo.Option opt = new TrainAppExamPaperResVo.Option();
|
|
|
+ opt.setName(item.getName());
|
|
|
+ opt.setContent(item.getContent());
|
|
|
+ return opt;
|
|
|
+ }).toList();
|
|
|
+ qVo.setOptions(opts);
|
|
|
+ return qVo;
|
|
|
+ }).toList();
|
|
|
+
|
|
|
+ TrainAppExamPaperResVo res = new TrainAppExamPaperResVo();
|
|
|
+ res.setLogId(resolvedLogId);
|
|
|
+ res.setExamId(examId);
|
|
|
+ res.setExamName(exam.getName());
|
|
|
+ res.setExamDuration(exam.getExamDuration());
|
|
|
+ res.setPassRate(exam.getPassRate());
|
|
|
+ res.setAmount(exam.getAmount());
|
|
|
+ res.setQuestions(qVos);
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public TrainAppExamSubmitResVo submitExam(TrainAppExamSubmitReqVo reqVo) {
|
|
|
+ Long driverId = getDriverId();
|
|
|
+
|
|
|
+ TExamLog log = tExamLogService.getById(reqVo.getLogId());
|
|
|
+ if (log == null || !Objects.equals(log.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "考试日志不存在");
|
|
|
+ }
|
|
|
+ if (!Objects.equals(log.getDriverId(), driverId)) {
|
|
|
+ throw new SystemException(HttpStatus.AUTHORITY_NO_CODE, HttpStatus.ACCESS_FIAL);
|
|
|
+ }
|
|
|
+ if (!Objects.equals(log.getExamFlag(), 1)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "考试已结束");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long trainId = log.getTrainId();
|
|
|
+ Long examId = log.getExamId();
|
|
|
+// ensureDriverInTrain(trainId, driverId);
|
|
|
+
|
|
|
+ TExam exam = tExamService.getById(examId);
|
|
|
+ if (exam == null || !Objects.equals(exam.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "考试不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<TQuestion> questions = tQuestionService.lambdaQuery()
|
|
|
+ .eq(TQuestion::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TQuestion::getExamId, examId)
|
|
|
+ .list();
|
|
|
+ Map<Long, Integer> questionTypeMap = questions.stream().filter(q -> q.getId() != null).collect(Collectors.toMap(TQuestion::getId, TQuestion::getType, (a, b) -> a));
|
|
|
+
|
|
|
+ List<Long> qIds = questions.stream().map(TQuestion::getId).filter(Objects::nonNull).toList();
|
|
|
+ Map<Long, Set<String>> correctAnswerMap = buildCorrectAnswerMap(qIds);
|
|
|
+
|
|
|
+ Map<Long, String> userAnswerMap = reqVo.getAnswers().stream()
|
|
|
+ .filter(a -> a != null && a.getQuestionId() != null)
|
|
|
+ .collect(Collectors.toMap(TrainAppExamSubmitReqVo.AnswerItem::getQuestionId, a -> normalizeAnswer(a.getAnswer()), (a, b) -> a));
|
|
|
+
|
|
|
+ tExamRecordService.lambdaUpdate()
|
|
|
+ .eq(TExamRecord::getLogId, log.getId())
|
|
|
+ .eq(TExamRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .set(TExamRecord::getDelFlag, Global.DELETED)
|
|
|
+ .update();
|
|
|
+
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+
|
|
|
+ List<TExamRecord> recordEntities = new ArrayList<>();
|
|
|
+ int rightAmount = 0;
|
|
|
+ for (Long questionId : qIds) {
|
|
|
+ Integer type = questionTypeMap.get(questionId);
|
|
|
+ Set<String> correct = correctAnswerMap.getOrDefault(questionId, Collections.emptySet());
|
|
|
+ String userAns = userAnswerMap.getOrDefault(questionId, "");
|
|
|
+ int correctFlag = judgeCorrectFlag(type, userAns, correct);
|
|
|
+ if (correctFlag == 1) {
|
|
|
+ rightAmount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ TExamRecord record = new TExamRecord();
|
|
|
+ record.setLogId(log.getId());
|
|
|
+ record.setQuestionId(questionId);
|
|
|
+ record.setAnswer(userAns);
|
|
|
+ record.setCorrectFlag(correctFlag);
|
|
|
+ record.setCreateBy(userId);
|
|
|
+ record.setCreateTime(now);
|
|
|
+ record.setUpdateBy(userId);
|
|
|
+ record.setUpdateTime(now);
|
|
|
+ record.setDelFlag(Global.UN_DELETED);
|
|
|
+ recordEntities.add(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!recordEntities.isEmpty() && !tExamRecordService.saveBatch(recordEntities)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "保存考试记录失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ int amount = exam.getAmount() == null ? qIds.size() : exam.getAmount();
|
|
|
+ int rightRate = amount == 0 ? 0 : (int) Math.round(rightAmount * 100.0 / amount);
|
|
|
+ boolean passed = rightRate >= (exam.getPassRate() == null ? 0 : exam.getPassRate());
|
|
|
+
|
|
|
+ boolean updated = tExamLogService.lambdaUpdate()
|
|
|
+ .eq(TExamLog::getId, log.getId())
|
|
|
+ .set(TExamLog::getExamFlag, 2)
|
|
|
+ .set(TExamLog::getEndTime, now)
|
|
|
+ .set(TExamLog::getRightAmount, rightAmount)
|
|
|
+ .set(TExamLog::getAmount, amount)
|
|
|
+ .set(TExamLog::getRightRate, rightRate)
|
|
|
+ .set(TExamLog::getUpdateBy, userId)
|
|
|
+ .set(TExamLog::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ if (!updated) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "更新考试日志失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ syncTrainExamResult(trainId, driverId, rightRate, passed, now, userId);
|
|
|
+
|
|
|
+ TrainAppExamSubmitResVo resVo = new TrainAppExamSubmitResVo();
|
|
|
+ resVo.setRightAmount(rightAmount);
|
|
|
+ resVo.setAmount(amount);
|
|
|
+ resVo.setRightRate(rightRate);
|
|
|
+ resVo.setPassed(passed);
|
|
|
+ return resVo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<Long, TTrain> loadTrainMap(List<Long> trainIds) {
|
|
|
+ if (CollUtil.isEmpty(trainIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<TTrain> trains = tTrainService.listByIds(trainIds.stream().filter(Objects::nonNull).distinct().toList());
|
|
|
+ return trains.stream()
|
|
|
+ .filter(t -> t != null && Objects.equals(t.getDelFlag(), Global.UN_DELETED))
|
|
|
+ .collect(Collectors.toMap(TTrain::getId, t -> t, (a, b) -> a));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<Long, List<Long>> loadTrainCourseIdsMap(Set<Long> trainIds) {
|
|
|
+ if (CollUtil.isEmpty(trainIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<TTrainCourse> relations = tTrainCourseService.lambdaQuery()
|
|
|
+ .in(TTrainCourse::getTrainId, trainIds)
|
|
|
+ .list();
|
|
|
+ Map<Long, List<Long>> result = new LinkedHashMap<>();
|
|
|
+ for (TTrainCourse r : relations) {
|
|
|
+ if (r.getTrainId() == null || r.getCourseId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ result.computeIfAbsent(r.getTrainId(), k -> new ArrayList<>()).add(r.getCourseId());
|
|
|
+ }
|
|
|
+ result.values().forEach(list -> {
|
|
|
+ List<Long> distinct = list.stream().distinct().toList();
|
|
|
+ list.clear();
|
|
|
+ list.addAll(distinct);
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<Long, TCourse> loadCourseMap(List<Long> courseIds) {
|
|
|
+ if (CollUtil.isEmpty(courseIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<TCourse> courses = tCourseService.listByIds(courseIds.stream().filter(Objects::nonNull).distinct().toList());
|
|
|
+ return courses.stream()
|
|
|
+ .filter(c -> c != null && Objects.equals(c.getDelFlag(), Global.UN_DELETED))
|
|
|
+ .collect(Collectors.toMap(TCourse::getId, c -> c, (a, b) -> a));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Integer> loadLearnStatusMap(Long driverId, Set<Long> trainIds) {
|
|
|
+ if (CollUtil.isEmpty(trainIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<TCourseRecord> records = tCourseRecordService.lambdaQuery()
|
|
|
+ .select(TCourseRecord::getTrainId, TCourseRecord::getCourseId, TCourseRecord::getStatus)
|
|
|
+ .eq(TCourseRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TCourseRecord::getDriverId, driverId)
|
|
|
+ .in(TCourseRecord::getTrainId, trainIds)
|
|
|
+ .list();
|
|
|
+ Map<String, Integer> map = new HashMap<>();
|
|
|
+ for (TCourseRecord r : records) {
|
|
|
+ if (r.getTrainId() == null || r.getCourseId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String key = r.getTrainId() + "_" + r.getCourseId();
|
|
|
+ map.put(key, r.getStatus());
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<TrainAppTaskCourseResVo> buildCourseList(List<Long> courseIds, Map<Long, TCourse> courseMap, Map<String, Integer> learnStatusMap, Long trainId) {
|
|
|
+ if (CollUtil.isEmpty(courseIds)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<TrainAppTaskCourseResVo> list = new ArrayList<>();
|
|
|
+ for (Long courseId : courseIds) {
|
|
|
+ TCourse course = courseMap.get(courseId);
|
|
|
+ if (course == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ TrainAppTaskCourseResVo vo = new TrainAppTaskCourseResVo();
|
|
|
+ vo.setCourseId(courseId);
|
|
|
+ vo.setCourseName(course.getCourseName());
|
|
|
+ vo.setCourseType(course.getCourseType());
|
|
|
+ vo.setCourseTypeName(CourseTypeEnum.getNameByCode(course.getCourseType()));
|
|
|
+ Integer status = learnStatusMap.get(trainId + "_" + courseId);
|
|
|
+ vo.setLearnStatus(Objects.equals(status, 1) ? 1 : 0);
|
|
|
+ list.add(vo);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ private TrainAppTaskResVo toTask(TTrainRecord record, TTrain train, List<Long> courseIds, Map<Long, TCourse> courseMap, Map<String, Integer> learnStatusMap) {
|
|
|
+ TrainAppTaskResVo vo = new TrainAppTaskResVo();
|
|
|
+ vo.setTrainId(record.getTrainId());
|
|
|
+ vo.setStatus(record.getStatus());
|
|
|
+ vo.setStatusName(TrainStatusEnum.getNameByCode(record.getStatus()));
|
|
|
+ vo.setLearnedCourse(defaultInt(record.getFinishCourse()));
|
|
|
+ vo.setSumCourse(defaultInt(record.getSumCourse()));
|
|
|
+
|
|
|
+ if (train != null) {
|
|
|
+ vo.setTrainName(train.getName());
|
|
|
+ vo.setStartDate(train.getStartDate());
|
|
|
+ vo.setEndDate(train.getEndDate());
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setCourses(buildCourseList(courseIds, courseMap, learnStatusMap, record.getTrainId()));
|
|
|
+ boolean allCourseDone = Objects.equals(vo.getLearnedCourse(), vo.getSumCourse()) && vo.getSumCourse() > 0;
|
|
|
+ vo.setShowExam(Boolean.TRUE.equals(train != null ? train.getExamFlag() : null) && allCourseDone);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Integer calcOverallProgressRate(Long driverId) {
|
|
|
+ List<TTrainRecord> records = tTrainRecordService.lambdaQuery()
|
|
|
+ .select(TTrainRecord::getFinishCourse, TTrainRecord::getSumCourse)
|
|
|
+ .eq(TTrainRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TTrainRecord::getDriverId, driverId)
|
|
|
+ .list();
|
|
|
+ int learned = records.stream().mapToInt(r -> defaultInt(r.getFinishCourse())).sum();
|
|
|
+ int sum = records.stream().mapToInt(r -> defaultInt(r.getSumCourse())).sum();
|
|
|
+ if (sum == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return (int) Math.round(learned * 100.0 / sum);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int defaultInt(Integer value) {
|
|
|
+ return value == null ? 0 : value;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void ensureCourseInTrain(Long trainId, Long courseId) {
|
|
|
+ boolean exists = tTrainCourseService.lambdaQuery()
|
|
|
+ .eq(TTrainCourse::getTrainId, trainId)
|
|
|
+ .eq(TTrainCourse::getCourseId, courseId)
|
|
|
+ .exists();
|
|
|
+ if (!exists) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "课程不属于该培训");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private TTrainRecord getDriverTrainRecord(Long trainId, Long driverId) {
|
|
|
+ TTrainRecord record = tTrainRecordService.lambdaQuery()
|
|
|
+ .eq(TTrainRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TTrainRecord::getTrainId, trainId)
|
|
|
+ .eq(TTrainRecord::getDriverId, driverId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+ if (record == null) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "培训任务不存在");
|
|
|
+ }
|
|
|
+ return record;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateTrainRecordStatusOnLearning(Long trainId, Long driverId) {
|
|
|
+ TTrainRecord record = getDriverTrainRecord(trainId, driverId);
|
|
|
+ if (Objects.equals(record.getStatus(), 1)) {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+ tTrainRecordService.lambdaUpdate()
|
|
|
+ .eq(TTrainRecord::getId, record.getId())
|
|
|
+ .set(TTrainRecord::getStatus, 2)
|
|
|
+ .set(TTrainRecord::getUpdateBy, userId)
|
|
|
+ .set(TTrainRecord::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void syncTrainProgress(Long trainId, Long driverId, LocalDateTime now, Long userId) {
|
|
|
+ TTrainRecord trainRecord = getDriverTrainRecord(trainId, driverId);
|
|
|
+ int sumCourse = defaultInt(trainRecord.getSumCourse());
|
|
|
+ int learned = countLearnedCourse(trainId, driverId);
|
|
|
+ Integer newStatus = trainRecord.getStatus();
|
|
|
+ if (learned > 0 && Objects.equals(newStatus, 1)) {
|
|
|
+ newStatus = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ TTrain train = tTrainService.getById(trainId);
|
|
|
+ boolean hasExam = train != null && Objects.equals(train.getExamFlag(), 1);
|
|
|
+ if (sumCourse > 0 && learned >= sumCourse) {
|
|
|
+ if (!hasExam) {
|
|
|
+ newStatus = 3;
|
|
|
+ } else {
|
|
|
+ newStatus = 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean updated = tTrainRecordService.lambdaUpdate()
|
|
|
+ .eq(TTrainRecord::getId, trainRecord.getId())
|
|
|
+ .set(TTrainRecord::getFinishCourse, learned)
|
|
|
+ .set(TTrainRecord::getStatus, newStatus)
|
|
|
+ .set(TTrainRecord::getUpdateBy, userId)
|
|
|
+ .set(TTrainRecord::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ if (!updated) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "更新培训记录失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int countLearnedCourse(Long trainId, Long driverId) {
|
|
|
+ Long count = tCourseRecordService.lambdaQuery()
|
|
|
+ .eq(TCourseRecord::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TCourseRecord::getTrainId, trainId)
|
|
|
+ .eq(TCourseRecord::getDriverId, driverId)
|
|
|
+ .eq(TCourseRecord::getStatus, 1)
|
|
|
+ .count();
|
|
|
+ return count == null ? 0 : count.intValue();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Long resolveTrainExamId(Long trainId) {
|
|
|
+ return tTrainExamService.lambdaQuery()
|
|
|
+ .select(TTrainExam::getExamId)
|
|
|
+ .eq(TTrainExam::getTrainId, trainId)
|
|
|
+ .orderByDesc(TTrainExam::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ .oneOpt()
|
|
|
+ .map(TTrainExam::getExamId)
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private TrainAppDetailResVo.Exam resolveTrainExam(Long trainId) {
|
|
|
+ Long examId = resolveTrainExamId(trainId);
|
|
|
+ if (examId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ TExam exam = tExamService.getById(examId);
|
|
|
+ if (exam == null || !Objects.equals(exam.getDelFlag(), Global.UN_DELETED)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ TrainAppDetailResVo.Exam vo = new TrainAppDetailResVo.Exam();
|
|
|
+ vo.setExamId(examId);
|
|
|
+ vo.setName(exam.getName());
|
|
|
+ vo.setExamDuration(exam.getExamDuration());
|
|
|
+ vo.setPassRate(exam.getPassRate());
|
|
|
+ vo.setAmount(exam.getAmount());
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean checkExamPassed(Long trainId, Long examId, Long driverId, Integer passRate) {
|
|
|
+ TExamLog newest = tExamLogService.lambdaQuery()
|
|
|
+ .eq(TExamLog::getDelFlag, Global.UN_DELETED)
|
|
|
+ .eq(TExamLog::getTrainId, trainId)
|
|
|
+ .eq(TExamLog::getExamId, examId)
|
|
|
+ .eq(TExamLog::getDriverId, driverId)
|
|
|
+ .eq(TExamLog::getExamFlag, 2)
|
|
|
+ .orderByDesc(TExamLog::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ .one();
|
|
|
+ if (newest == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Integer rate = newest.getRightRate();
|
|
|
+ if (rate == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int pass = passRate == null ? 0 : passRate;
|
|
|
+ return rate >= pass;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<Long, Set<String>> buildCorrectAnswerMap(List<Long> questionIds) {
|
|
|
+ if (CollUtil.isEmpty(questionIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<TQuestionItem> items = tQuestionItemService.lambdaQuery()
|
|
|
+ .select(TQuestionItem::getQuestionId, TQuestionItem::getName, TQuestionItem::getAnswerFlag)
|
|
|
+ .eq(TQuestionItem::getDelFlag, Global.UN_DELETED)
|
|
|
+ .in(TQuestionItem::getQuestionId, questionIds)
|
|
|
+ .list();
|
|
|
+ Map<Long, Set<String>> map = new HashMap<>();
|
|
|
+ for (TQuestionItem item : items) {
|
|
|
+ if (item.getQuestionId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!Objects.equals(item.getAnswerFlag(), 1)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (StrUtil.isBlank(item.getName())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String opt = item.getName().trim().toUpperCase();
|
|
|
+ map.computeIfAbsent(item.getQuestionId(), k -> new LinkedHashSet<>()).add(opt);
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String normalizeAnswer(String answer) {
|
|
|
+ if (answer == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ return Arrays.stream(answer.split(","))
|
|
|
+ .map(String::trim)
|
|
|
+ .filter(StrUtil::isNotBlank)
|
|
|
+ .map(s -> s.toUpperCase())
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.joining(","));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int judgeCorrectFlag(Integer questionType, String userAnswer, Set<String> correct) {
|
|
|
+ if (questionType == null) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ if (questionType == 3) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ Set<String> userSet = Arrays.stream(userAnswer.split(","))
|
|
|
+ .map(String::trim)
|
|
|
+ .filter(StrUtil::isNotBlank)
|
|
|
+ .map(String::toUpperCase)
|
|
|
+ .collect(Collectors.toCollection(LinkedHashSet::new));
|
|
|
+
|
|
|
+ if (questionType == 1) {
|
|
|
+ if (userSet.size() != 1 || correct.size() != 1) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ return Objects.equals(userSet.iterator().next(), correct.iterator().next()) ? 1 : 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (userSet.isEmpty()) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ if (userSet.equals(correct)) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ boolean subset = correct.containsAll(userSet);
|
|
|
+ return subset ? 3 : 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void syncTrainExamResult(Long trainId, Long driverId, Integer rightRate, boolean passed, LocalDateTime now, Long userId) {
|
|
|
+ TTrainRecord record = getDriverTrainRecord(trainId, driverId);
|
|
|
+ Integer newStatus = record.getStatus();
|
|
|
+ if (passed && Objects.equals(record.getFinishCourse(), record.getSumCourse())) {
|
|
|
+ newStatus = 3;
|
|
|
+ } else if (Objects.equals(newStatus, 1)) {
|
|
|
+ newStatus = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean updated = tTrainRecordService.lambdaUpdate()
|
|
|
+ .eq(TTrainRecord::getId, record.getId())
|
|
|
+ .set(TTrainRecord::getRightRate, rightRate)
|
|
|
+ .set(TTrainRecord::getStatus, newStatus)
|
|
|
+ .set(TTrainRecord::getUpdateBy, userId)
|
|
|
+ .set(TTrainRecord::getUpdateTime, now)
|
|
|
+ .update();
|
|
|
+ if (!updated) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "更新培训记录失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public CourseDetailResVo courseDetail(Long courseId) {
|
|
|
+ return kwsCourseService.manageCourseDetail(courseId);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|