feat(tms): 实现培训管理业务模块
Co-authored-by: Cursor <cursoragent@cursor.com>
| | |
| | | <version>${project.version}</version> |
| | | <scope>compile</scope> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>com.jnpf</groupId> |
| | | <artifactId>jnpf-permission-api</artifactId> |
| | | <version>${project.version}</version> |
| | | </dependency> |
| | | </dependencies> |
| | | </project> |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsAnnualPlanItemEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsAnnualPlanItemMapper extends BaseMapper<TmsAnnualPlanItemEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsAnnualPlanEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsAnnualPlanMapper extends BaseMapper<TmsAnnualPlanEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsCourseEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | @Mapper |
| | | public interface TmsCourseMapper extends BaseMapper<TmsCourseEntity> { |
| | | |
| | | @Select("SELECT MAX(course_no) FROM tms_course WHERE course_no LIKE CONCAT(#{prefix}, '%') AND (f_delete_mark = 0 OR f_delete_mark IS NULL)") |
| | | String selectMaxCourseNo(@Param("prefix") String prefix); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsEvalModeEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsEvalModeMapper extends BaseMapper<TmsEvalModeEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsExamItemEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsExamItemMapper extends BaseMapper<TmsExamItemEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsExamEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsExamMapper extends BaseMapper<TmsExamEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsMaterialEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsMaterialMapper extends BaseMapper<TmsMaterialEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | @Mapper |
| | | public interface TmsPaperMapper extends BaseMapper<TmsPaperEntity> { |
| | | |
| | | /** |
| | | * 当年最大试卷编号(含已逻辑删除,避免流水号回退复用)。 |
| | | * 格式:yy + 4 位流水,如 260001。 |
| | | */ |
| | | @Select("SELECT MAX(paper_no) FROM tms_paper WHERE paper_no LIKE CONCAT(#{prefix}, '%') AND LENGTH(paper_no) = 6") |
| | | String selectMaxPaperNo(@Param("prefix") String prefix); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPaperQuestionEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPaperQuestionMapper extends BaseMapper<TmsPaperQuestionEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPaperSectionEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPaperSectionMapper extends BaseMapper<TmsPaperSectionEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPersonFileEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPersonFileMapper extends BaseMapper<TmsPersonFileEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPersonTaskMapper extends BaseMapper<TmsPersonTaskEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPersonTrainEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPersonTrainMapper extends BaseMapper<TmsPersonTrainEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPersonWorkEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPersonWorkMapper extends BaseMapper<TmsPersonWorkEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPlaceEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPlaceMapper extends BaseMapper<TmsPlaceEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPostPlanItemEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPostPlanItemMapper extends BaseMapper<TmsPostPlanItemEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsPostPlanEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsPostPlanMapper extends BaseMapper<TmsPostPlanEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsQuestionBankEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsQuestionBankMapper extends BaseMapper<TmsQuestionBankEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsQuestionEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | @Mapper |
| | | public interface TmsQuestionMapper extends BaseMapper<TmsQuestionEntity> { |
| | | |
| | | /** |
| | | * 当年最大试题编号(含已逻辑删除,避免流水号回退复用)。 |
| | | * 格式:yy + 4 位流水,如 260001。 |
| | | */ |
| | | @Select("SELECT MAX(question_no) FROM tms_question WHERE question_no LIKE CONCAT(#{prefix}, '%') AND LENGTH(question_no) = 6") |
| | | String selectMaxQuestionNo(@Param("prefix") String prefix); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsQuestionOptionEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsQuestionOptionMapper extends BaseMapper<TmsQuestionOptionEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsRecordEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsRecordMapper extends BaseMapper<TmsRecordEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsSelfTestItemEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsSelfTestItemMapper extends BaseMapper<TmsSelfTestItemEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsSelfTestEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsSelfTestMapper extends BaseMapper<TmsSelfTestEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsSignModeEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsSignModeMapper extends BaseMapper<TmsSignModeEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsTaskFileEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsTaskFileMapper extends BaseMapper<TmsTaskFileEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | @Mapper |
| | | public interface TmsTaskMapper extends BaseMapper<TmsTaskEntity> { |
| | | |
| | | /** |
| | | * 当月最大任务编号(含已逻辑删除,避免流水回退)。 |
| | | * 格式:前缀(2) + yyyyMM(6) + 4位 = 12位,如 TT2026090001 / TD2026090001。 |
| | | */ |
| | | @Select("SELECT MAX(task_no) FROM tms_task WHERE task_no LIKE CONCAT(#{prefix}, '%') AND LENGTH(task_no) = 12") |
| | | String selectMaxTaskNo(@Param("prefix") String prefix); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsTrainModeEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsTrainModeMapper extends BaseMapper<TmsTrainModeEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsMapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import jnpf.tmsEntity.TmsTrainerEntity; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface TmsTrainerMapper extends BaseMapper<TmsTrainerEntity> { |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.course.TmsCourseForm; |
| | | import jnpf.tmsEntity.course.TmsCourseQuery; |
| | | import jnpf.tmsEntity.course.TmsPaperOption; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsCourseService { |
| | | |
| | | List<TmsCourseForm> getList(TmsCourseQuery query); |
| | | |
| | | TmsCourseForm getInfo(String id); |
| | | |
| | | String create(TmsCourseForm form); |
| | | |
| | | void update(String id, TmsCourseForm form); |
| | | |
| | | void setEnabled(String id, String enabled); |
| | | |
| | | void delete(String id); |
| | | |
| | | List<TmsPaperOption> paperOptions(); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeForm; |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsEvalModeService { |
| | | |
| | | List<TmsEvalModeForm> getList(TmsEvalModeQuery query); |
| | | |
| | | TmsEvalModeForm getInfo(String id); |
| | | |
| | | String create(TmsEvalModeForm form); |
| | | |
| | | void update(String id, TmsEvalModeForm form); |
| | | |
| | | void setEnabled(String id, String enabled); |
| | | |
| | | void delete(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeDetailVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeExamVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeQuery; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSessionVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitForm; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitResultVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsExamGradeService { |
| | | |
| | | List<TmsExamGradeSessionVO> getSessionList(TmsExamGradeQuery query); |
| | | |
| | | TmsExamGradeSessionVO getSession(String sessionId); |
| | | |
| | | List<TmsExamGradeExamVO> getExamList(String sessionId); |
| | | |
| | | TmsExamGradeDetailVO getExamDetail(String examId); |
| | | |
| | | TmsExamGradeSubmitResultVO submitGrade(TmsExamGradeSubmitForm form); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreDetailVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScorePaperVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreQuery; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreRowVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreSummaryVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsExamScoreService { |
| | | |
| | | List<TmsExamScoreSummaryVO> getList(TmsExamScoreQuery query); |
| | | |
| | | TmsExamScoreDetailVO getDetail(String summaryId); |
| | | |
| | | TmsExamScorePaperVO getPaperView(String examId); |
| | | |
| | | /** 按考核对象导出已勾选试卷的成绩 */ |
| | | List<TmsExamScoreRowVO> exportRows(List<String> summaryIds); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamDetailVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamListVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamPaperVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamQuery; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitForm; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitResultVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsOnlineExamService { |
| | | |
| | | List<TmsOnlineExamListVO> getMyPapers(TmsOnlineExamQuery query); |
| | | |
| | | TmsOnlineExamDetailVO getDetail(String paperId); |
| | | |
| | | /** 按答卷ID查看已交卷回顾 */ |
| | | TmsOnlineExamDetailVO getExamReview(String examId); |
| | | |
| | | TmsOnlineExamPaperVO start(String paperId); |
| | | |
| | | void saveAnswers(String examId, TmsOnlineExamSubmitForm form); |
| | | |
| | | TmsOnlineExamSubmitResultVO submit(String examId, TmsOnlineExamSubmitForm form); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.paper.TmsPaperForm; |
| | | import jnpf.tmsEntity.paper.TmsPaperQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsPaperService { |
| | | |
| | | List<TmsPaperForm> getList(TmsPaperQuery query); |
| | | |
| | | TmsPaperForm getInfo(String id); |
| | | |
| | | String create(TmsPaperForm form); |
| | | |
| | | void update(String id, TmsPaperForm form); |
| | | |
| | | /** 废弃:biz_status=invalid */ |
| | | void invalidate(String id); |
| | | |
| | | /** 启用/停用:open ↔ closed(废弃卷不可) */ |
| | | void setBizStatus(String id, String bizStatus); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.person.TmsPersonTaskLearnResult; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskMineForm; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskQuery; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskSignResult; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsPersonTaskService { |
| | | |
| | | List<TmsPersonTaskMineForm> getMine(TmsPersonTaskQuery query); |
| | | |
| | | TmsPersonTaskMineForm getInfo(String id); |
| | | |
| | | TmsPersonTaskLearnResult markLearned(String id, Integer seconds); |
| | | |
| | | /** 在线签到:校验培训方式是否允许、是否在培训时间窗内,写入签到时间 */ |
| | | TmsPersonTaskSignResult sign(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.place.TmsPlaceOption; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 培训地点 |
| | | */ |
| | | public interface TmsPlaceService { |
| | | |
| | | /** 启用中的地点下拉(供任务发布、培训方式「地点必填」联动选用) */ |
| | | List<TmsPlaceOption> listOptions(); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.question.TmsQuestionBankOption; |
| | | import jnpf.tmsEntity.question.TmsQuestionForm; |
| | | import jnpf.tmsEntity.question.TmsQuestionQuery; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | public interface TmsQuestionService { |
| | | |
| | | List<TmsQuestionBankOption> listBanks(); |
| | | |
| | | List<Map<String, String>> listAdmins(); |
| | | |
| | | List<TmsQuestionForm> getList(TmsQuestionQuery query); |
| | | |
| | | TmsQuestionForm getInfo(String id); |
| | | |
| | | String create(TmsQuestionForm form); |
| | | |
| | | void update(String id, TmsQuestionForm form); |
| | | |
| | | void invalidate(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.record.TmsRecordForm; |
| | | import jnpf.tmsEntity.record.TmsRecordQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsRecordService { |
| | | |
| | | List<TmsRecordForm> getList(TmsRecordQuery query); |
| | | |
| | | TmsRecordForm getInfo(String id); |
| | | |
| | | void update(String id, TmsRecordForm form); |
| | | |
| | | int archive(List<String> ids); |
| | | |
| | | void delete(List<String> ids); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestPaperVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestQuery; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestRecordVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestStartForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitResultVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsSelfTestService { |
| | | |
| | | TmsSelfTestPaperVO start(TmsSelfTestStartForm form); |
| | | |
| | | /** 暂存作答(不交卷、不计分) */ |
| | | void saveAnswers(String id, TmsSelfTestSubmitForm form); |
| | | |
| | | TmsSelfTestSubmitResultVO submit(String id, TmsSelfTestSubmitForm form); |
| | | |
| | | List<TmsSelfTestRecordVO> getList(TmsSelfTestQuery query); |
| | | |
| | | TmsSelfTestPaperVO getInfo(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.signmode.TmsSignModeForm; |
| | | import jnpf.tmsEntity.signmode.TmsSignModeQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsSignModeService { |
| | | |
| | | List<TmsSignModeForm> getList(TmsSignModeQuery query); |
| | | |
| | | TmsSignModeForm getInfo(String id); |
| | | |
| | | String create(TmsSignModeForm form); |
| | | |
| | | void update(String id, TmsSignModeForm form); |
| | | |
| | | void setEnabled(String id, String enabled); |
| | | |
| | | void delete(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.task.TmsFromPlanResult; |
| | | import jnpf.tmsEntity.task.TmsTaskForm; |
| | | import jnpf.tmsEntity.task.TmsTaskQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsTaskService { |
| | | |
| | | List<TmsTaskForm> getList(TmsTaskQuery query); |
| | | |
| | | TmsTaskForm getInfo(String id); |
| | | |
| | | String create(TmsTaskForm form); |
| | | |
| | | void update(String id, TmsTaskForm form); |
| | | |
| | | void delete(String id); |
| | | |
| | | /** 发布:生成 task_no、个人任务、培训记录 */ |
| | | void publish(String id); |
| | | |
| | | /** 取消/终止(有人已签到则不可) */ |
| | | void cancel(String id); |
| | | |
| | | /** 批量取消 */ |
| | | void cancelBatch(List<String> ids); |
| | | |
| | | /** 恢复为草稿 */ |
| | | void restore(String id); |
| | | |
| | | /** |
| | | * 年计划审批通过:按子表生成未发布培训任务。 |
| | | * 已存在 source_item_id 的行跳过,可重复调用。 |
| | | */ |
| | | TmsFromPlanResult createFromAnnual(String planId); |
| | | |
| | | /** |
| | | * 岗位培训计划审批通过:按子表生成未发布培训任务。 |
| | | * 培训对象取计划主表;已存在 source_item_id 的行跳过。 |
| | | */ |
| | | TmsFromPlanResult createFromPost(String planId); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveDetailVO; |
| | | |
| | | /** |
| | | * 个人培训档案 |
| | | */ |
| | | public interface TmsTrainArchiveService { |
| | | |
| | | /** 当前登录用户的个人培训档案(无档则自动建档) */ |
| | | TmsTrainArchiveDetailVO getMine(); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeForm; |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 培训方式配置 |
| | | */ |
| | | public interface TmsTrainModeService { |
| | | |
| | | List<TmsTrainModeForm> getList(TmsTrainModeQuery query); |
| | | |
| | | TmsTrainModeForm getInfo(String id); |
| | | |
| | | String create(TmsTrainModeForm form); |
| | | |
| | | void update(String id, TmsTrainModeForm form); |
| | | |
| | | void setEnabled(String id, String enabled); |
| | | |
| | | void delete(String id); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService; |
| | | |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordCatalogVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordListVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordQuery; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TmsTrainRecordService { |
| | | |
| | | List<TmsTrainRecordListVO> getList(TmsTrainRecordQuery query); |
| | | |
| | | TmsTrainRecordCatalogVO getCatalog(String archiveId, String startDate, String endDate); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsCourseEntity; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.course.TmsCourseForm; |
| | | import jnpf.tmsEntity.course.TmsCourseQuery; |
| | | import jnpf.tmsEntity.course.TmsPaperOption; |
| | | import jnpf.tmsMapper.TmsCourseMapper; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsService.TmsCourseService; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsCourseServiceImpl implements TmsCourseService { |
| | | |
| | | private final TmsCourseMapper courseMapper; |
| | | private final TmsPaperMapper paperMapper; |
| | | |
| | | @Override |
| | | public List<TmsCourseForm> getList(TmsCourseQuery query) { |
| | | LambdaQueryWrapper<TmsCourseEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getEnabled())) { |
| | | qw.eq(TmsCourseEntity::getEnabled, query.getEnabled()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getCategory())) { |
| | | qw.eq(TmsCourseEntity::getCategory, query.getCategory()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getTrainMode())) { |
| | | qw.eq(TmsCourseEntity::getTrainMode, query.getTrainMode()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getEvalMode())) { |
| | | qw.eq(TmsCourseEntity::getEvalMode, query.getEvalMode()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsCourseEntity::getCourseNo, kw) |
| | | .or() |
| | | .like(TmsCourseEntity::getCourseName, kw)); |
| | | } |
| | | qw.orderByDesc(TmsCourseEntity::getCourseNo); |
| | | qw.orderByDesc(TmsCourseEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsCourseEntity> page = courseMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | Map<String, String> paperNames = loadPaperNames(page.getRecords()); |
| | | List<TmsCourseForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsCourseEntity row : page.getRecords()) { |
| | | list.add(toForm(row, paperNames)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsCourseForm getInfo(String id) { |
| | | TmsCourseEntity entity = requireOne(id); |
| | | Map<String, String> paperNames = loadPaperNames(Collections.singletonList(entity)); |
| | | return toForm(entity, paperNames); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsCourseForm form) { |
| | | validateForm(form); |
| | | TmsCourseEntity entity = new TmsCourseEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | entity.setCourseNo(nextCourseNo()); |
| | | applyForm(entity, form); |
| | | entity.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | entity.setCreatorTime(new Date()); |
| | | courseMapper.insert(entity); |
| | | return entity.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsCourseForm form) { |
| | | TmsCourseEntity entity = requireOne(id); |
| | | validateForm(form); |
| | | applyForm(entity, form); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | courseMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void setEnabled(String id, String enabled) { |
| | | if (!"0".equals(enabled) && !"1".equals(enabled)) { |
| | | throw new DataException("启用状态无效"); |
| | | } |
| | | TmsCourseEntity entity = requireOne(id); |
| | | entity.setEnabled(enabled); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | courseMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(String id) { |
| | | TmsCourseEntity entity = requireOne(id); |
| | | if (!"0".equals(entity.getEnabled())) { |
| | | throw new DataException("请先停用后再删除"); |
| | | } |
| | | courseMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<TmsPaperOption> paperOptions() { |
| | | LambdaQueryWrapper<TmsPaperEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPaperEntity::getBizStatus, "open"); |
| | | qw.orderByDesc(TmsPaperEntity::getCreatorTime); |
| | | List<TmsPaperEntity> rows = paperMapper.selectList(qw); |
| | | List<TmsPaperOption> list = new ArrayList<>(rows.size()); |
| | | for (TmsPaperEntity row : rows) { |
| | | TmsPaperOption opt = new TmsPaperOption(); |
| | | opt.setId(row.getId()); |
| | | opt.setFullName(row.getPaperName()); |
| | | list.add(opt); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private String nextCourseNo() { |
| | | String prefix = "KC" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM")); |
| | | String maxNo = courseMapper.selectMaxCourseNo(prefix); |
| | | int next = 1; |
| | | if (StringUtil.isNotEmpty(maxNo) && maxNo.length() >= prefix.length() + 4 && maxNo.startsWith(prefix)) { |
| | | try { |
| | | next = Integer.parseInt(maxNo.substring(prefix.length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | next = 1; |
| | | } |
| | | } |
| | | if (next > 9999) { |
| | | throw new DataException("当月课程编号已用尽(" + prefix + "9999)"); |
| | | } |
| | | return prefix + String.format("%04d", next); |
| | | } |
| | | |
| | | private Map<String, String> loadPaperNames(List<TmsCourseEntity> rows) { |
| | | Set<String> ids = new HashSet<>(); |
| | | for (TmsCourseEntity row : rows) { |
| | | if (StringUtil.isNotEmpty(row.getPaperId())) { |
| | | ids.add(row.getPaperId()); |
| | | } |
| | | } |
| | | if (ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<TmsPaperEntity> papers = paperMapper.selectBatchIds(ids); |
| | | Map<String, String> map = new HashMap<>(papers.size()); |
| | | for (TmsPaperEntity paper : papers) { |
| | | map.put(paper.getId(), paper.getPaperName()); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private TmsCourseEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("培训课程不存在"); |
| | | } |
| | | TmsCourseEntity entity = courseMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("培训课程不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private void validateForm(TmsCourseForm form) { |
| | | if (form == null || StringUtil.isEmpty(form.getCourseName())) { |
| | | throw new DataException("请填写课程名称"); |
| | | } |
| | | if ("exam".equals(form.getEvalMode()) && StringUtil.isEmpty(form.getPaperId())) { |
| | | throw new DataException("在线考试须关联考核试卷"); |
| | | } |
| | | if (StringUtil.isNotEmpty(form.getPaperId())) { |
| | | TmsPaperEntity paper = paperMapper.selectById(form.getPaperId()); |
| | | if (paper == null) { |
| | | throw new DataException("关联试卷不存在"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void applyForm(TmsCourseEntity entity, TmsCourseForm form) { |
| | | entity.setCourseName(form.getCourseName().trim()); |
| | | entity.setCategory(StringUtil.isEmpty(form.getCategory()) ? null : form.getCategory().trim()); |
| | | entity.setTrainMode(StringUtil.isEmpty(form.getTrainMode()) ? null : form.getTrainMode().trim()); |
| | | entity.setEvalMode(StringUtil.isEmpty(form.getEvalMode()) ? null : form.getEvalMode().trim()); |
| | | if ("exam".equals(entity.getEvalMode())) { |
| | | entity.setPaperId(form.getPaperId()); |
| | | } else { |
| | | entity.setPaperId(StringUtil.isEmpty(form.getPaperId()) ? null : form.getPaperId()); |
| | | } |
| | | entity.setHours(form.getHours()); |
| | | entity.setEnabled(yn(form.getEnabled(), "1")); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private static String yn(String v, String def) { |
| | | if ("0".equals(v) || "1".equals(v)) { |
| | | return v; |
| | | } |
| | | return def; |
| | | } |
| | | |
| | | private TmsCourseForm toForm(TmsCourseEntity entity, Map<String, String> paperNames) { |
| | | TmsCourseForm form = new TmsCourseForm(); |
| | | form.setId(entity.getId()); |
| | | form.setCourseNo(entity.getCourseNo()); |
| | | form.setCourseName(entity.getCourseName()); |
| | | form.setCategory(entity.getCategory()); |
| | | form.setTrainMode(entity.getTrainMode()); |
| | | form.setEvalMode(entity.getEvalMode()); |
| | | form.setPaperId(entity.getPaperId()); |
| | | form.setPaperName(StringUtil.isEmpty(entity.getPaperId()) ? null : paperNames.get(entity.getPaperId())); |
| | | form.setHours(entity.getHours()); |
| | | form.setEnabled(entity.getEnabled()); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsEvalModeEntity; |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeForm; |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeQuery; |
| | | import jnpf.tmsMapper.TmsEvalModeMapper; |
| | | import jnpf.tmsService.TmsEvalModeService; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsEvalModeServiceImpl implements TmsEvalModeService { |
| | | |
| | | private final TmsEvalModeMapper evalModeMapper; |
| | | |
| | | @Override |
| | | public List<TmsEvalModeForm> getList(TmsEvalModeQuery query) { |
| | | LambdaQueryWrapper<TmsEvalModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getEnabled())) { |
| | | qw.eq(TmsEvalModeEntity::getEnabled, query.getEnabled()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsEvalModeEntity::getModeCode, kw) |
| | | .or() |
| | | .like(TmsEvalModeEntity::getModeName, kw)); |
| | | } |
| | | qw.orderByDesc(TmsEvalModeEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsEvalModeEntity> page = evalModeMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | List<TmsEvalModeForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsEvalModeEntity row : page.getRecords()) { |
| | | list.add(toForm(row)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsEvalModeForm getInfo(String id) { |
| | | return toForm(requireOne(id)); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsEvalModeForm form) { |
| | | validateForm(form, null); |
| | | TmsEvalModeEntity entity = new TmsEvalModeEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | applyForm(entity, form); |
| | | entity.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | entity.setCreatorTime(new Date()); |
| | | evalModeMapper.insert(entity); |
| | | return entity.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsEvalModeForm form) { |
| | | TmsEvalModeEntity entity = requireOne(id); |
| | | validateForm(form, id); |
| | | form.setModeCode(entity.getModeCode()); |
| | | applyForm(entity, form); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | evalModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void setEnabled(String id, String enabled) { |
| | | if (!"0".equals(enabled) && !"1".equals(enabled)) { |
| | | throw new DataException("启用状态无效"); |
| | | } |
| | | TmsEvalModeEntity entity = requireOne(id); |
| | | entity.setEnabled(enabled); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | evalModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(String id) { |
| | | TmsEvalModeEntity entity = requireOne(id); |
| | | if (!"0".equals(entity.getEnabled())) { |
| | | throw new DataException("请先停用后再删除"); |
| | | } |
| | | evalModeMapper.deleteById(id); |
| | | } |
| | | |
| | | private TmsEvalModeEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("考核方式不存在"); |
| | | } |
| | | TmsEvalModeEntity entity = evalModeMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("考核方式不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private void validateForm(TmsEvalModeForm form, String excludeId) { |
| | | if (form == null || StringUtil.isEmpty(form.getModeCode())) { |
| | | throw new DataException("请填写方式编码"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getModeName())) { |
| | | throw new DataException("请填写方式名称"); |
| | | } |
| | | String code = form.getModeCode().trim(); |
| | | form.setModeCode(code); |
| | | LambdaQueryWrapper<TmsEvalModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsEvalModeEntity::getModeCode, code); |
| | | if (StringUtil.isNotEmpty(excludeId)) { |
| | | qw.ne(TmsEvalModeEntity::getId, excludeId); |
| | | } |
| | | if (evalModeMapper.selectCount(qw) > 0) { |
| | | throw new DataException("方式编码已存在"); |
| | | } |
| | | } |
| | | |
| | | private void applyForm(TmsEvalModeEntity entity, TmsEvalModeForm form) { |
| | | entity.setModeCode(form.getModeCode().trim()); |
| | | entity.setModeName(form.getModeName().trim()); |
| | | entity.setNeedPaper(yn(form.getNeedPaper(), "0")); |
| | | entity.setNeedQuiz(yn(form.getNeedQuiz(), "0")); |
| | | entity.setNeedPractice(yn(form.getNeedPractice(), "0")); |
| | | entity.setEnabled(yn(form.getEnabled(), "1")); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private static String yn(String v, String def) { |
| | | if ("0".equals(v) || "1".equals(v)) { |
| | | return v; |
| | | } |
| | | return def; |
| | | } |
| | | |
| | | private TmsEvalModeForm toForm(TmsEvalModeEntity entity) { |
| | | TmsEvalModeForm form = new TmsEvalModeForm(); |
| | | form.setId(entity.getId()); |
| | | form.setModeCode(entity.getModeCode()); |
| | | form.setModeName(entity.getModeName()); |
| | | form.setNeedPaper(entity.getNeedPaper()); |
| | | form.setNeedQuiz(entity.getNeedQuiz()); |
| | | form.setNeedPractice(entity.getNeedPractice()); |
| | | form.setEnabled(entity.getEnabled()); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.UserApi; |
| | | import jnpf.permission.entity.UserEntity; |
| | | import jnpf.tmsEntity.TmsExamEntity; |
| | | import jnpf.tmsEntity.TmsExamItemEntity; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeDetailVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeExamVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeItemVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeOptionVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeQuery; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSessionVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitForm; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitResultVO; |
| | | import jnpf.tmsMapper.TmsExamItemMapper; |
| | | import jnpf.tmsMapper.TmsExamMapper; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsService.TmsExamGradeService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.Data; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsExamGradeServiceImpl implements TmsExamGradeService { |
| | | |
| | | private static final String EXAM_SUBMITTED = "submitted"; |
| | | private static final String GRADE_PENDING = "pending"; |
| | | private static final String GRADE_GRADED = "graded"; |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | |
| | | private final TmsExamMapper examMapper; |
| | | private final TmsExamItemMapper examItemMapper; |
| | | private final TmsPaperMapper paperMapper; |
| | | private final UserApi userApi; |
| | | |
| | | @Override |
| | | public List<TmsExamGradeSessionVO> getSessionList(TmsExamGradeQuery query) { |
| | | List<TmsExamEntity> exams = loadGradeExams(null); |
| | | Map<String, List<TmsExamEntity>> byPaper = groupByPaper(exams); |
| | | Map<String, TmsPaperEntity> papers = loadPapers(new ArrayList<>(byPaper.keySet())); |
| | | |
| | | List<TmsExamGradeSessionVO> all = new ArrayList<>(); |
| | | for (Map.Entry<String, List<TmsExamEntity>> entry : byPaper.entrySet()) { |
| | | all.add(toSession(entry.getKey(), papers.get(entry.getKey()), entry.getValue())); |
| | | } |
| | | all.sort(Comparator |
| | | .comparing(TmsExamGradeSessionVO::getPendingCount, Comparator.nullsLast(Integer::compareTo)).reversed() |
| | | .thenComparing(TmsExamGradeSessionVO::getExamEnd, Comparator.nullsLast(String::compareTo)).reversed()); |
| | | |
| | | String keyword = trim(query.getKeyword()); |
| | | String taskNo = trim(query.getTaskNo()); |
| | | if (StringUtil.isNotEmpty(keyword)) { |
| | | String kw = keyword.toLowerCase(); |
| | | all = all.stream() |
| | | .filter(x -> contains(x.getTaskSubject(), kw) |
| | | || contains(x.getPaperName(), kw) |
| | | || contains(x.getTaskNo(), kw)) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | if (StringUtil.isNotEmpty(taskNo)) { |
| | | String tn = taskNo.toLowerCase(); |
| | | all = all.stream().filter(x -> contains(x.getTaskNo(), tn)).collect(Collectors.toList()); |
| | | } |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | int from = (int) ((current - 1) * size); |
| | | int to = Math.min(from + (int) size, all.size()); |
| | | List<TmsExamGradeSessionVO> page = from >= all.size() ? new ArrayList<>() : new ArrayList<>(all.subList(from, to)); |
| | | query.setData(page, all.size()); |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public TmsExamGradeSessionVO getSession(String sessionId) { |
| | | if (StringUtil.isEmpty(sessionId)) { |
| | | throw new DataException("阅卷场次不存在"); |
| | | } |
| | | List<TmsExamEntity> exams = loadGradeExams(sessionId); |
| | | if (exams.isEmpty()) { |
| | | throw new DataException("阅卷场次不存在"); |
| | | } |
| | | return toSession(sessionId, paperMapper.selectById(sessionId), exams); |
| | | } |
| | | |
| | | @Override |
| | | public List<TmsExamGradeExamVO> getExamList(String sessionId) { |
| | | if (StringUtil.isEmpty(sessionId)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<TmsExamEntity> exams = loadGradeExams(sessionId); |
| | | exams.sort(Comparator.comparing(TmsExamEntity::getSubmitTime, Comparator.nullsLast(Date::compareTo)).reversed()); |
| | | Map<String, String> names = loadUserNames(exams.stream().map(TmsExamEntity::getUserId).collect(Collectors.toList())); |
| | | List<TmsExamGradeExamVO> rows = new ArrayList<>(); |
| | | for (TmsExamEntity exam : exams) { |
| | | TmsExamGradeExamVO vo = new TmsExamGradeExamVO(); |
| | | vo.setId(exam.getId()); |
| | | vo.setSessionId(sessionId); |
| | | vo.setUserId(exam.getUserId()); |
| | | vo.setUserName(names.getOrDefault(exam.getUserId(), exam.getUserId())); |
| | | vo.setSubmitTime(fmt(exam.getSubmitTime())); |
| | | vo.setObjectiveScore(exam.getObjectiveScore()); |
| | | vo.setSubjectiveScore(exam.getSubjectiveScore()); |
| | | vo.setTotalScore(GRADE_PENDING.equals(exam.getGradeStatus()) ? null : exam.getTotalScore()); |
| | | vo.setPassScore(exam.getPassScore()); |
| | | vo.setGradeStatus(exam.getGradeStatus()); |
| | | vo.setPassFlag(exam.getPassFlag()); |
| | | rows.add(vo); |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | @Override |
| | | public TmsExamGradeDetailVO getExamDetail(String examId) { |
| | | TmsExamEntity exam = requireGradeExam(examId); |
| | | List<TmsExamItemEntity> items = loadItems(exam.getId()); |
| | | Map<String, String> names = loadUserNames(Collections.singletonList(exam.getUserId())); |
| | | |
| | | TmsExamGradeDetailVO vo = new TmsExamGradeDetailVO(); |
| | | vo.setExamId(exam.getId()); |
| | | vo.setSessionId(exam.getPaperId()); |
| | | vo.setPaperName(exam.getPaperName()); |
| | | vo.setUserName(names.getOrDefault(exam.getUserId(), exam.getUserId())); |
| | | vo.setSubmitTime(fmt(exam.getSubmitTime())); |
| | | vo.setTotalScore(resolvePaperTotal(exam)); |
| | | vo.setPassScore(exam.getPassScore()); |
| | | vo.setObjectiveScore(nvl(exam.getObjectiveScore())); |
| | | vo.setSubjectiveScore(nvl(exam.getSubjectiveScore())); |
| | | vo.setGradeStatus(exam.getGradeStatus()); |
| | | vo.setItems(items.stream().map(this::toItemVo).collect(Collectors.toList())); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsExamGradeSubmitResultVO submitGrade(TmsExamGradeSubmitForm form) { |
| | | if (form == null || StringUtil.isEmpty(form.getExamId())) { |
| | | throw new DataException("答卷不存在"); |
| | | } |
| | | TmsExamEntity exam = requireGradeExam(form.getExamId()); |
| | | if (!GRADE_PENDING.equals(exam.getGradeStatus())) { |
| | | throw new DataException("该答卷已批改,无需重复提交"); |
| | | } |
| | | List<TmsExamItemEntity> items = loadItems(exam.getId()); |
| | | Map<String, BigDecimal> scoreMap = new HashMap<>(); |
| | | if (form.getItems() != null) { |
| | | for (TmsExamGradeSubmitForm.ItemScore row : form.getItems()) { |
| | | if (row == null || StringUtil.isEmpty(row.getId())) { |
| | | continue; |
| | | } |
| | | scoreMap.put(row.getId(), row.getGotScore()); |
| | | } |
| | | } |
| | | |
| | | BigDecimal subjective = BigDecimal.ZERO; |
| | | Date now = new Date(); |
| | | String graderId = UserProvider.getLoginUserId(); |
| | | for (TmsExamItemEntity item : items) { |
| | | if (!"1".equals(item.getIsSubjective())) { |
| | | continue; |
| | | } |
| | | if (!scoreMap.containsKey(item.getId()) || scoreMap.get(item.getId()) == null) { |
| | | throw new DataException("请为所有主观题打分"); |
| | | } |
| | | BigDecimal got = scoreMap.get(item.getId()); |
| | | BigDecimal max = nvl(item.getScore()); |
| | | if (got.compareTo(BigDecimal.ZERO) < 0 || got.compareTo(max) > 0) { |
| | | throw new DataException("主观题得分需在 0 ~ " + max.stripTrailingZeros().toPlainString() + " 之间"); |
| | | } |
| | | item.setGotScore(got); |
| | | item.setLastModifyUserId(graderId); |
| | | item.setLastModifyTime(now); |
| | | examItemMapper.updateById(item); |
| | | subjective = subjective.add(got); |
| | | } |
| | | |
| | | BigDecimal objective = nvl(exam.getObjectiveScore()); |
| | | BigDecimal total = objective.add(subjective); |
| | | exam.setSubjectiveScore(subjective); |
| | | exam.setTotalScore(total); |
| | | exam.setPassFlag(passFlag(total, exam.getPassScore())); |
| | | exam.setGradeStatus(GRADE_GRADED); |
| | | exam.setGraderId(graderId); |
| | | exam.setGradeTime(now); |
| | | exam.setLastModifyUserId(graderId); |
| | | exam.setLastModifyTime(now); |
| | | examMapper.updateById(exam); |
| | | |
| | | TmsExamGradeSubmitResultVO result = new TmsExamGradeSubmitResultVO(); |
| | | result.setExamId(exam.getId()); |
| | | result.setSubjectiveScore(subjective); |
| | | result.setTotalScore(total); |
| | | result.setPassFlag(exam.getPassFlag()); |
| | | result.setGradeStatus(GRADE_GRADED); |
| | | result.setNextExamId(findNextPending(exam.getPaperId(), exam.getId())); |
| | | return result; |
| | | } |
| | | |
| | | private String findNextPending(String paperId, String excludeExamId) { |
| | | List<TmsExamEntity> exams = loadGradeExams(paperId); |
| | | return exams.stream() |
| | | .filter(x -> GRADE_PENDING.equals(x.getGradeStatus())) |
| | | .filter(x -> !Objects.equals(x.getId(), excludeExamId)) |
| | | .sorted(Comparator.comparing(TmsExamEntity::getSubmitTime, Comparator.nullsLast(Date::compareTo))) |
| | | .map(TmsExamEntity::getId) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | |
| | | private TmsExamEntity requireGradeExam(String examId) { |
| | | if (StringUtil.isEmpty(examId)) { |
| | | throw new DataException("答卷不存在"); |
| | | } |
| | | TmsExamEntity exam = examMapper.selectById(examId); |
| | | if (exam == null || !EXAM_SUBMITTED.equals(exam.getExamStatus())) { |
| | | throw new DataException("答卷不存在"); |
| | | } |
| | | if (!GRADE_PENDING.equals(exam.getGradeStatus()) && !GRADE_GRADED.equals(exam.getGradeStatus())) { |
| | | throw new DataException("该答卷无需阅卷"); |
| | | } |
| | | return exam; |
| | | } |
| | | |
| | | private List<TmsExamEntity> loadGradeExams(String paperId) { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_SUBMITTED) |
| | | .in(TmsExamEntity::getGradeStatus, GRADE_PENDING, GRADE_GRADED) |
| | | .eq(StringUtil.isNotEmpty(paperId), TmsExamEntity::getPaperId, paperId) |
| | | .orderByDesc(TmsExamEntity::getSubmitTime); |
| | | return examMapper.selectList(qw); |
| | | } |
| | | |
| | | private Map<String, List<TmsExamEntity>> groupByPaper(List<TmsExamEntity> exams) { |
| | | Map<String, List<TmsExamEntity>> map = new LinkedHashMap<>(); |
| | | for (TmsExamEntity exam : exams) { |
| | | if (StringUtil.isEmpty(exam.getPaperId())) { |
| | | continue; |
| | | } |
| | | map.computeIfAbsent(exam.getPaperId(), k -> new ArrayList<>()).add(exam); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, TmsPaperEntity> loadPapers(List<String> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<TmsPaperEntity> list = paperMapper.selectBatchIds(ids); |
| | | Map<String, TmsPaperEntity> map = new HashMap<>(); |
| | | for (TmsPaperEntity paper : list) { |
| | | map.put(paper.getId(), paper); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private TmsExamGradeSessionVO toSession(String paperId, TmsPaperEntity paper, List<TmsExamEntity> exams) { |
| | | TmsExamGradeSessionVO vo = new TmsExamGradeSessionVO(); |
| | | vo.setId(paperId); |
| | | vo.setPaperId(paperId); |
| | | String paperName = paper != null && StringUtil.isNotEmpty(paper.getPaperName()) |
| | | ? paper.getPaperName() |
| | | : (exams.isEmpty() ? paperId : exams.get(0).getPaperName()); |
| | | vo.setPaperName(paperName); |
| | | vo.setTaskSubject(paperName); |
| | | String paperNo = paper != null && StringUtil.isNotEmpty(paper.getPaperNo()) ? paper.getPaperNo() : paperId; |
| | | vo.setTaskNo(paperNo); |
| | | vo.setDurationMin(paper != null && paper.getDurationMin() != null |
| | | ? paper.getDurationMin() |
| | | : (exams.isEmpty() ? null : exams.get(0).getDurationMin())); |
| | | vo.setTotalScore(paper != null && paper.getTotalScore() != null |
| | | ? paper.getTotalScore() |
| | | : resolvePaperTotal(exams.isEmpty() ? null : exams.get(0))); |
| | | vo.setPassScore(paper != null && paper.getPassScore() != null |
| | | ? paper.getPassScore() |
| | | : (exams.isEmpty() ? null : exams.get(0).getPassScore())); |
| | | |
| | | Date start = paper != null ? paper.getExamStart() : null; |
| | | Date end = paper != null ? paper.getExamEnd() : null; |
| | | if (start == null) { |
| | | start = exams.stream().map(TmsExamEntity::getStartTime).filter(Objects::nonNull).min(Date::compareTo).orElse(null); |
| | | } |
| | | if (end == null) { |
| | | end = exams.stream().map(TmsExamEntity::getSubmitTime).filter(Objects::nonNull).max(Date::compareTo).orElse(null); |
| | | } |
| | | vo.setExamStart(fmt(start)); |
| | | vo.setExamEnd(fmt(end)); |
| | | vo.setSubmittedCount(exams.size()); |
| | | vo.setPendingCount((int) exams.stream().filter(x -> GRADE_PENDING.equals(x.getGradeStatus())).count()); |
| | | return vo; |
| | | } |
| | | |
| | | private BigDecimal resolvePaperTotal(TmsExamEntity exam) { |
| | | if (exam == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | if (exam.getPaperId() != null) { |
| | | TmsPaperEntity paper = paperMapper.selectById(exam.getPaperId()); |
| | | if (paper != null && paper.getTotalScore() != null) { |
| | | return paper.getTotalScore(); |
| | | } |
| | | } |
| | | return nvl(exam.getTotalScore()); |
| | | } |
| | | |
| | | private List<TmsExamItemEntity> loadItems(String examId) { |
| | | LambdaQueryWrapper<TmsExamItemEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamItemEntity::getForeignId, examId).orderByAsc(TmsExamItemEntity::getSortNo); |
| | | return examItemMapper.selectList(qw); |
| | | } |
| | | |
| | | private TmsExamGradeItemVO toItemVo(TmsExamItemEntity item) { |
| | | TmsExamGradeItemVO vo = new TmsExamGradeItemVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setQuestionType(item.getQuestionType()); |
| | | vo.setStem(item.getStem()); |
| | | vo.setOptions(parseOptions(item.getOptionsJson())); |
| | | vo.setCorrectAnswer(item.getCorrectAnswer()); |
| | | vo.setUserAnswer(item.getUserAnswer()); |
| | | vo.setScore(item.getScore()); |
| | | vo.setGotScore(item.getGotScore()); |
| | | vo.setIsSubjective(item.getIsSubjective()); |
| | | vo.setAnalysis(item.getAnalysis()); |
| | | return vo; |
| | | } |
| | | |
| | | private List<TmsExamGradeOptionVO> parseOptions(String json) { |
| | | if (StringUtil.isEmpty(json)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | try { |
| | | List<OptSnap> list = OBJECT_MAPPER.readValue(json, new TypeReference<List<OptSnap>>() {}); |
| | | List<TmsExamGradeOptionVO> options = new ArrayList<>(); |
| | | for (OptSnap snap : list) { |
| | | TmsExamGradeOptionVO opt = new TmsExamGradeOptionVO(); |
| | | opt.setOptionLabel(snap.getOptionLabel()); |
| | | opt.setOptionContent(snap.getOptionContent()); |
| | | options.add(opt); |
| | | } |
| | | return options; |
| | | } catch (Exception e) { |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | | |
| | | private Map<String, String> loadUserNames(List<String> userIds) { |
| | | Map<String, String> map = new HashMap<>(); |
| | | List<String> ids = userIds == null ? Collections.emptyList() |
| | | : userIds.stream().filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList()); |
| | | if (ids.isEmpty()) { |
| | | return map; |
| | | } |
| | | try { |
| | | List<UserEntity> users = userApi.getUserName(ids); |
| | | if (users != null) { |
| | | for (UserEntity user : users) { |
| | | if (user == null || StringUtil.isEmpty(user.getId())) { |
| | | continue; |
| | | } |
| | | map.put(user.getId(), StringUtil.isNotEmpty(user.getRealName()) ? user.getRealName() : user.getId()); |
| | | } |
| | | } |
| | | } catch (Exception ignored) { |
| | | // 用户服务不可用时回退 userId |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private String passFlag(BigDecimal score, BigDecimal passScore) { |
| | | if (passScore == null) { |
| | | return null; |
| | | } |
| | | return nvl(score).compareTo(passScore) >= 0 ? "1" : "0"; |
| | | } |
| | | |
| | | private String fmt(Date date) { |
| | | return date == null ? null : DateUtil.dateToString(date, FMT); |
| | | } |
| | | |
| | | private BigDecimal nvl(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private String trim(String value) { |
| | | return value == null || "null".equalsIgnoreCase(value) ? "" : value.trim(); |
| | | } |
| | | |
| | | private boolean contains(String text, String kw) { |
| | | return text != null && text.toLowerCase().contains(kw); |
| | | } |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | private static class OptSnap { |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.UserApi; |
| | | import jnpf.permission.entity.UserEntity; |
| | | import jnpf.tmsEntity.TmsExamEntity; |
| | | import jnpf.tmsEntity.TmsExamItemEntity; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreDetailVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreOptionVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScorePaperItemVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScorePaperVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreQuery; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreRowVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreSummaryVO; |
| | | import jnpf.tmsMapper.TmsExamItemMapper; |
| | | import jnpf.tmsMapper.TmsExamMapper; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsService.TmsExamScoreService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.StringUtil; |
| | | import lombok.Data; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsExamScoreServiceImpl implements TmsExamScoreService { |
| | | |
| | | private static final String EXAM_SUBMITTED = "submitted"; |
| | | private static final String STATUS_INVALID = "invalid"; |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | |
| | | private final TmsExamMapper examMapper; |
| | | private final TmsExamItemMapper examItemMapper; |
| | | private final TmsPaperMapper paperMapper; |
| | | private final UserApi userApi; |
| | | |
| | | @Override |
| | | public List<TmsExamScoreSummaryVO> getList(TmsExamScoreQuery query) { |
| | | List<TmsExamEntity> exams = loadSubmittedExams(); |
| | | Map<String, List<TmsExamEntity>> byPaper = groupByPaper(exams); |
| | | Map<String, TmsPaperEntity> papers = loadPapers(new ArrayList<>(byPaper.keySet())); |
| | | |
| | | List<TmsExamScoreSummaryVO> all = new ArrayList<>(); |
| | | for (Map.Entry<String, List<TmsExamEntity>> entry : byPaper.entrySet()) { |
| | | TmsPaperEntity paper = papers.get(entry.getKey()); |
| | | all.add(toSummary(entry.getKey(), paper, entry.getValue())); |
| | | } |
| | | all.sort(Comparator.comparing(TmsExamScoreSummaryVO::getPaperName, Comparator.nullsLast(String::compareTo)).reversed()); |
| | | |
| | | String keyword = trim(query.getKeyword()); |
| | | String taskNo = trim(query.getTaskNo()); |
| | | if (StringUtil.isNotEmpty(keyword)) { |
| | | String kw = keyword.toLowerCase(); |
| | | all = all.stream() |
| | | .filter(x -> contains(x.getTaskSubject(), kw) |
| | | || contains(x.getPaperName(), kw) |
| | | || contains(x.getTaskNo(), kw)) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | if (StringUtil.isNotEmpty(taskNo)) { |
| | | String tn = taskNo.toLowerCase(); |
| | | all = all.stream().filter(x -> contains(x.getTaskNo(), tn)).collect(Collectors.toList()); |
| | | } |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | int from = (int) ((current - 1) * size); |
| | | int to = Math.min(from + (int) size, all.size()); |
| | | List<TmsExamScoreSummaryVO> page = from >= all.size() ? new ArrayList<>() : new ArrayList<>(all.subList(from, to)); |
| | | query.setData(page, all.size()); |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public TmsExamScoreDetailVO getDetail(String summaryId) { |
| | | if (StringUtil.isEmpty(summaryId)) { |
| | | throw new DataException("成绩汇总不存在"); |
| | | } |
| | | List<TmsExamEntity> exams = loadSubmittedByPaper(summaryId); |
| | | if (exams.isEmpty()) { |
| | | throw new DataException("成绩汇总不存在"); |
| | | } |
| | | TmsPaperEntity paper = paperMapper.selectById(summaryId); |
| | | TmsExamScoreSummaryVO summary = toSummary(summaryId, paper, exams); |
| | | Map<String, String> names = loadUserNames(exams.stream().map(TmsExamEntity::getUserId).collect(Collectors.toList())); |
| | | |
| | | List<TmsExamScoreRowVO> rows = new ArrayList<>(); |
| | | for (TmsExamEntity exam : exams) { |
| | | TmsExamScoreRowVO row = new TmsExamScoreRowVO(); |
| | | row.setId(exam.getId()); |
| | | row.setSummaryId(summaryId); |
| | | row.setTaskSubject(summary.getTaskSubject()); |
| | | row.setTaskNo(summary.getTaskNo()); |
| | | row.setUserId(exam.getUserId()); |
| | | row.setUserName(names.getOrDefault(exam.getUserId(), exam.getUserId())); |
| | | row.setExamStart(fmt(exam.getStartTime())); |
| | | row.setExamEnd(fmt(exam.getSubmitTime())); |
| | | row.setScore(exam.getTotalScore()); |
| | | row.setPassScore(exam.getPassScore() != null ? exam.getPassScore() : summary.getPassScore()); |
| | | row.setPassFlag(exam.getPassFlag()); |
| | | rows.add(row); |
| | | } |
| | | rows.sort(Comparator.comparing(TmsExamScoreRowVO::getExamEnd, Comparator.nullsLast(String::compareTo)).reversed()); |
| | | |
| | | TmsExamScoreDetailVO detail = new TmsExamScoreDetailVO(); |
| | | BeanUtils.copyProperties(summary, detail); |
| | | detail.setRows(rows); |
| | | return detail; |
| | | } |
| | | |
| | | @Override |
| | | public TmsExamScorePaperVO getPaperView(String examId) { |
| | | TmsExamEntity exam = examMapper.selectById(examId); |
| | | if (exam == null || !EXAM_SUBMITTED.equals(exam.getExamStatus())) { |
| | | throw new DataException("答卷不存在或尚未交卷"); |
| | | } |
| | | TmsPaperEntity paper = paperMapper.selectById(exam.getPaperId()); |
| | | Map<String, String> names = loadUserNames(Collections.singletonList(exam.getUserId())); |
| | | |
| | | LambdaQueryWrapper<TmsExamItemEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamItemEntity::getForeignId, examId); |
| | | qw.orderByAsc(TmsExamItemEntity::getSortNo); |
| | | List<TmsExamItemEntity> items = examItemMapper.selectList(qw); |
| | | |
| | | BigDecimal paperTotal = BigDecimal.ZERO; |
| | | List<TmsExamScorePaperItemVO> itemVos = new ArrayList<>(); |
| | | for (TmsExamItemEntity item : items) { |
| | | paperTotal = paperTotal.add(nvl(item.getScore())); |
| | | TmsExamScorePaperItemVO vo = new TmsExamScorePaperItemVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setQuestionType(item.getQuestionType()); |
| | | vo.setStem(item.getStem()); |
| | | vo.setOptions(toOptions(item.getOptionsJson())); |
| | | vo.setCorrectAnswer(item.getCorrectAnswer()); |
| | | vo.setUserAnswer(item.getUserAnswer()); |
| | | vo.setScore(item.getScore()); |
| | | vo.setGotScore(item.getGotScore()); |
| | | vo.setIsSubjective(item.getIsSubjective()); |
| | | if ("1".equals(item.getIsSubjective())) { |
| | | vo.setIsCorrect(null); |
| | | } else if (item.getScore() != null && item.getScore().compareTo(BigDecimal.ZERO) > 0) { |
| | | vo.setIsCorrect(nvl(item.getGotScore()).compareTo(item.getScore()) >= 0); |
| | | } else { |
| | | vo.setIsCorrect(Objects.equals(normalize(item.getCorrectAnswer()), normalize(item.getUserAnswer()))); |
| | | } |
| | | itemVos.add(vo); |
| | | } |
| | | |
| | | TmsExamScorePaperVO vo = new TmsExamScorePaperVO(); |
| | | vo.setExamId(exam.getId()); |
| | | vo.setSummaryId(exam.getPaperId()); |
| | | vo.setTaskSubject(paper != null ? paper.getPaperName() : exam.getPaperName()); |
| | | vo.setTaskNo(paper != null && StringUtil.isNotEmpty(paper.getPaperNo()) ? paper.getPaperNo() : exam.getPaperId()); |
| | | vo.setPaperName(exam.getPaperName()); |
| | | vo.setUserId(exam.getUserId()); |
| | | vo.setUserName(names.getOrDefault(exam.getUserId(), exam.getUserId())); |
| | | vo.setExamStart(fmt(exam.getStartTime())); |
| | | vo.setExamEnd(fmt(exam.getSubmitTime())); |
| | | vo.setScore(exam.getTotalScore()); |
| | | vo.setPassScore(exam.getPassScore()); |
| | | vo.setTotalScore(paper != null && paper.getTotalScore() != null ? paper.getTotalScore() : paperTotal); |
| | | vo.setPassFlag(exam.getPassFlag()); |
| | | vo.setItems(itemVos); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public List<TmsExamScoreRowVO> exportRows(List<String> summaryIds) { |
| | | if (summaryIds == null || summaryIds.isEmpty()) { |
| | | throw new DataException("请选择要导出的考试"); |
| | | } |
| | | List<TmsExamScoreRowVO> rows = new ArrayList<>(); |
| | | for (String id : summaryIds) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | continue; |
| | | } |
| | | List<TmsExamEntity> exams = loadSubmittedByPaper(id); |
| | | if (exams.isEmpty()) { |
| | | continue; |
| | | } |
| | | TmsExamScoreDetailVO detail = getDetail(id); |
| | | if (detail.getRows() != null) { |
| | | rows.addAll(detail.getRows()); |
| | | } |
| | | } |
| | | if (rows.isEmpty()) { |
| | | throw new DataException("所选试卷暂无已交卷成绩"); |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | private TmsExamScoreSummaryVO toSummary(String paperId, TmsPaperEntity paper, List<TmsExamEntity> exams) { |
| | | TmsExamScoreSummaryVO vo = new TmsExamScoreSummaryVO(); |
| | | vo.setId(paperId); |
| | | vo.setPaperId(paperId); |
| | | String paperName = paper != null ? paper.getPaperName() : (exams.isEmpty() ? paperId : exams.get(0).getPaperName()); |
| | | vo.setPaperName(paperName); |
| | | vo.setTaskSubject(paperName); |
| | | String paperNo = paper != null && StringUtil.isNotEmpty(paper.getPaperNo()) ? paper.getPaperNo() : paperId; |
| | | vo.setTaskNo(paperNo); |
| | | vo.setPaperInvalid(paper != null && STATUS_INVALID.equals(paper.getBizStatus())); |
| | | |
| | | BigDecimal passScore = paper != null && paper.getPassScore() != null |
| | | ? paper.getPassScore() |
| | | : exams.stream().map(TmsExamEntity::getPassScore).filter(Objects::nonNull).findFirst().orElse(BigDecimal.ZERO); |
| | | BigDecimal totalScore = paper != null && paper.getTotalScore() != null |
| | | ? paper.getTotalScore() |
| | | : BigDecimal.ZERO; |
| | | vo.setPassScore(passScore); |
| | | vo.setTotalScore(totalScore); |
| | | |
| | | int fail = 0; |
| | | BigDecimal max = null; |
| | | BigDecimal min = null; |
| | | BigDecimal sum = BigDecimal.ZERO; |
| | | int scored = 0; |
| | | for (TmsExamEntity exam : exams) { |
| | | if ("0".equals(exam.getPassFlag())) { |
| | | fail++; |
| | | } |
| | | BigDecimal score = exam.getTotalScore(); |
| | | if (score == null) { |
| | | continue; |
| | | } |
| | | scored++; |
| | | sum = sum.add(score); |
| | | if (max == null || score.compareTo(max) > 0) { |
| | | max = score; |
| | | } |
| | | if (min == null || score.compareTo(min) < 0) { |
| | | min = score; |
| | | } |
| | | } |
| | | vo.setParticipantCount(exams.size()); |
| | | vo.setFailCount(fail); |
| | | vo.setMaxScore(max == null ? BigDecimal.ZERO : max); |
| | | vo.setMinScore(min == null ? BigDecimal.ZERO : min); |
| | | vo.setAvgScore(scored == 0 |
| | | ? BigDecimal.ZERO |
| | | : sum.divide(BigDecimal.valueOf(scored), 1, RoundingMode.HALF_UP)); |
| | | return vo; |
| | | } |
| | | |
| | | private List<TmsExamEntity> loadSubmittedExams() { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_SUBMITTED); |
| | | qw.isNotNull(TmsExamEntity::getPaperId); |
| | | qw.orderByDesc(TmsExamEntity::getSubmitTime); |
| | | return examMapper.selectList(qw); |
| | | } |
| | | |
| | | private List<TmsExamEntity> loadSubmittedByPaper(String paperId) { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_SUBMITTED); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.orderByDesc(TmsExamEntity::getSubmitTime); |
| | | return examMapper.selectList(qw); |
| | | } |
| | | |
| | | private Map<String, List<TmsExamEntity>> groupByPaper(List<TmsExamEntity> exams) { |
| | | Map<String, List<TmsExamEntity>> map = new LinkedHashMap<>(); |
| | | for (TmsExamEntity exam : exams) { |
| | | if (StringUtil.isEmpty(exam.getPaperId())) { |
| | | continue; |
| | | } |
| | | map.computeIfAbsent(exam.getPaperId(), k -> new ArrayList<>()).add(exam); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, TmsPaperEntity> loadPapers(List<String> paperIds) { |
| | | if (paperIds == null || paperIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | Map<String, TmsPaperEntity> map = new HashMap<>(); |
| | | for (TmsPaperEntity paper : paperMapper.selectBatchIds(paperIds)) { |
| | | map.put(paper.getId(), paper); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, String> loadUserNames(List<String> userIds) { |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<String> ids = userIds.stream().filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList()); |
| | | if (ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | try { |
| | | List<UserEntity> users = userApi.getUserName(ids); |
| | | if (users == null || users.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return users.stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(UserEntity::getId, UserEntity::getRealName, (a, b) -> a)); |
| | | } catch (Exception ex) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | } |
| | | |
| | | private List<TmsExamScoreOptionVO> toOptions(String json) { |
| | | if (StringUtil.isEmpty(json)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | try { |
| | | List<StoredOption> stored = OBJECT_MAPPER.readValue(json, new TypeReference<List<StoredOption>>() { |
| | | }); |
| | | List<TmsExamScoreOptionVO> list = new ArrayList<>(); |
| | | for (StoredOption opt : stored) { |
| | | TmsExamScoreOptionVO vo = new TmsExamScoreOptionVO(); |
| | | vo.setOptionLabel(opt.getOptionLabel()); |
| | | vo.setOptionContent(opt.getOptionContent()); |
| | | list.add(vo); |
| | | } |
| | | return list; |
| | | } catch (Exception e) { |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | | |
| | | private String normalize(String text) { |
| | | if (StringUtil.isEmpty(text)) { |
| | | return ""; |
| | | } |
| | | return text.trim(); |
| | | } |
| | | |
| | | private String trim(String text) { |
| | | if (StringUtil.isEmpty(text) || "null".equalsIgnoreCase(text)) { |
| | | return null; |
| | | } |
| | | return text.trim(); |
| | | } |
| | | |
| | | private boolean contains(String text, String kw) { |
| | | return text != null && text.toLowerCase().contains(kw); |
| | | } |
| | | |
| | | private String fmt(java.util.Date date) { |
| | | return date == null ? null : DateUtil.dateToString(date, FMT); |
| | | } |
| | | |
| | | private BigDecimal nvl(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | private static class StoredOption { |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | private String isCorrect; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsExamEntity; |
| | | import jnpf.tmsEntity.TmsExamItemEntity; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.TmsPaperQuestionEntity; |
| | | import jnpf.tmsEntity.TmsPaperSectionEntity; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import jnpf.tmsEntity.TmsQuestionEntity; |
| | | import jnpf.tmsEntity.TmsQuestionOptionEntity; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamAttemptVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamDetailVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamGradeItemVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamListVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamOptionVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamPaperVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamQuery; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamQuestionVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitForm; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitResultVO; |
| | | import jnpf.tmsMapper.TmsExamItemMapper; |
| | | import jnpf.tmsMapper.TmsExamMapper; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsMapper.TmsPaperQuestionMapper; |
| | | import jnpf.tmsMapper.TmsPaperSectionMapper; |
| | | import jnpf.tmsMapper.TmsPersonTaskMapper; |
| | | import jnpf.tmsMapper.TmsQuestionMapper; |
| | | import jnpf.tmsMapper.TmsQuestionOptionMapper; |
| | | import jnpf.tmsMapper.TmsTaskMapper; |
| | | import jnpf.tmsService.TmsOnlineExamService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsOnlineExamServiceImpl implements TmsOnlineExamService { |
| | | private static final String STATUS_OPEN = "open"; |
| | | private static final String EXAM_DOING = "doing"; |
| | | private static final String EXAM_SUBMITTED = "submitted"; |
| | | private static final String NOT_STARTED = "notStarted"; |
| | | private static final String GRADE_AUTO = "auto"; |
| | | private static final String GRADE_PENDING = "pending"; |
| | | private static final String SORT_RANDOM = "random"; |
| | | private static final String TYPE_ESSAY = "essay"; |
| | | private static final String TYPE_BLANK = "blank"; |
| | | private static final String TYPE_MULTI = "multi"; |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | private final TmsPaperMapper paperMapper; |
| | | private final TmsPaperSectionMapper sectionMapper; |
| | | private final TmsPaperQuestionMapper paperQuestionMapper; |
| | | private final TmsQuestionMapper questionMapper; |
| | | private final TmsQuestionOptionMapper optionMapper; |
| | | private final TmsExamMapper examMapper; |
| | | private final TmsExamItemMapper examItemMapper; |
| | | private final TmsTaskMapper taskMapper; |
| | | private final TmsPersonTaskMapper personTaskMapper; |
| | | |
| | | @Override |
| | | public List<TmsOnlineExamListVO> getMyPapers(TmsOnlineExamQuery query) { |
| | | String userId = UserProvider.getLoginUserId(); |
| | | LambdaQueryWrapper<TmsPaperEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPaperEntity::getBizStatus, STATUS_OPEN); |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | qw.like(TmsPaperEntity::getPaperName, query.getKeyword().trim()); |
| | | } |
| | | qw.orderByDesc(TmsPaperEntity::getCreatorTime); |
| | | List<TmsPaperEntity> papers = this.paperMapper.selectList(qw); |
| | | Map<String, TmsExamEntity> examMap = this.loadLatestExams(userId, papers); |
| | | ArrayList<TmsOnlineExamListVO> all = new ArrayList<TmsOnlineExamListVO>(); |
| | | for (TmsPaperEntity paper : papers) { |
| | | TmsExamEntity exam = examMap.get(paper.getId()); |
| | | String status = this.statusOf(exam); |
| | | if (StringUtil.isNotEmpty(query.getStatus()) && !"null".equalsIgnoreCase(query.getStatus()) && !query.getStatus().equals(status)) continue; |
| | | all.add(this.toListVo(paper, exam, status)); |
| | | } |
| | | long current = query.getCurrentPage() > 0L ? query.getCurrentPage() : 1L; |
| | | long size = query.getPageSize() > 0L ? query.getPageSize() : 20L; |
| | | int from = (int)((current - 1L) * size); |
| | | int to = Math.min(from + (int)size, all.size()); |
| | | ArrayList<TmsOnlineExamListVO> page = from >= all.size() ? new ArrayList<TmsOnlineExamListVO>() : new ArrayList(all.subList(from, to)); |
| | | query.setData(page, (long)all.size()); |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public TmsOnlineExamDetailVO getDetail(String paperId) { |
| | | if (StringUtil.isEmpty(paperId)) { |
| | | throw new DataException("请选择试卷"); |
| | | } |
| | | TmsPaperEntity paper = this.paperMapper.selectById(paperId); |
| | | if (paper == null) { |
| | | throw new DataException("试卷不存在"); |
| | | } |
| | | TmsExamEntity exam = this.latestExam(UserProvider.getLoginUserId(), paperId); |
| | | if (exam == null && !STATUS_OPEN.equals(paper.getBizStatus())) { |
| | | throw new DataException("试卷未开放"); |
| | | } |
| | | TmsOnlineExamDetailVO vo = new TmsOnlineExamDetailVO(); |
| | | vo.setId(paper.getId()); |
| | | vo.setPaperId(paper.getId()); |
| | | vo.setPaperName(paper.getPaperName()); |
| | | vo.setStatus(this.statusOf(exam)); |
| | | vo.setExamTimeText(this.timeText(paper.getExamStart(), paper.getExamEnd())); |
| | | vo.setTotalScore(paper.getTotalScore()); |
| | | vo.setPassScore(paper.getPassScore()); |
| | | vo.setDurationMin(paper.getDurationMin()); |
| | | if (exam != null) { |
| | | vo.setExamId(exam.getId()); |
| | | vo.setGotScore(exam.getTotalScore()); |
| | | vo.setStartTime(this.fmt(exam.getStartTime())); |
| | | vo.setSubmitTime(this.fmt(exam.getSubmitTime())); |
| | | vo.setPassFlag(exam.getPassFlag()); |
| | | vo.setGradeStatus(exam.getGradeStatus()); |
| | | if (EXAM_SUBMITTED.equals(exam.getExamStatus())) { |
| | | vo.setQuestions(this.toReviewQuestions(this.loadItems(exam.getId()))); |
| | | if (vo.getTotalScore() == null) { |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (TmsOnlineExamQuestionVO q : vo.getQuestions()) { |
| | | total = total.add(this.nvl(q.getScore())); |
| | | } |
| | | vo.setTotalScore(total); |
| | | } |
| | | } |
| | | } |
| | | this.fillRetakeMeta(vo, paper.getId(), exam, UserProvider.getLoginUserId()); |
| | | vo.setAttempts(this.listAttempts(UserProvider.getLoginUserId(), paper.getId())); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public TmsOnlineExamDetailVO getExamReview(String examId) { |
| | | if (StringUtil.isEmpty(examId)) { |
| | | throw new DataException("请选择答卷"); |
| | | } |
| | | TmsExamEntity exam = this.examMapper.selectById(examId); |
| | | if (exam == null) { |
| | | throw new DataException("答卷不存在"); |
| | | } |
| | | if (!Objects.equals(UserProvider.getLoginUserId(), exam.getUserId())) { |
| | | throw new DataException("无权查看该答卷"); |
| | | } |
| | | if (!EXAM_SUBMITTED.equals(exam.getExamStatus())) { |
| | | throw new DataException("交卷后才能进行考试回顾"); |
| | | } |
| | | TmsPaperEntity paper = this.paperMapper.selectById((exam.getPaperId())); |
| | | if (paper == null) { |
| | | throw new DataException("试卷不存在"); |
| | | } |
| | | TmsOnlineExamDetailVO vo = new TmsOnlineExamDetailVO(); |
| | | vo.setId(paper.getId()); |
| | | vo.setPaperId(paper.getId()); |
| | | vo.setPaperName(paper.getPaperName()); |
| | | vo.setStatus(this.statusOf(exam)); |
| | | vo.setExamTimeText(this.timeText(paper.getExamStart(), paper.getExamEnd())); |
| | | vo.setTotalScore(paper.getTotalScore()); |
| | | vo.setPassScore(paper.getPassScore() != null ? paper.getPassScore() : exam.getPassScore()); |
| | | vo.setDurationMin(paper.getDurationMin()); |
| | | vo.setExamId(exam.getId()); |
| | | vo.setGotScore(exam.getTotalScore()); |
| | | vo.setStartTime(this.fmt(exam.getStartTime())); |
| | | vo.setSubmitTime(this.fmt(exam.getSubmitTime())); |
| | | vo.setPassFlag(exam.getPassFlag()); |
| | | vo.setGradeStatus(exam.getGradeStatus()); |
| | | vo.setAttemptNo(Integer.valueOf(exam.getAttemptNo() == null ? 1 : exam.getAttemptNo())); |
| | | vo.setAttemptLabel(this.attemptLabel(vo.getAttemptNo())); |
| | | vo.setQuestions(this.toReviewQuestions(this.loadItems(exam.getId()))); |
| | | if (vo.getTotalScore() == null) { |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (TmsOnlineExamQuestionVO q : vo.getQuestions()) { |
| | | total = total.add(this.nvl(q.getScore())); |
| | | } |
| | | vo.setTotalScore(total); |
| | | } |
| | | return vo; |
| | | } |
| | | |
| | | private List<TmsOnlineExamAttemptVO> listAttempts(String userId, String paperId) { |
| | | if (StringUtil.isEmpty(userId) || StringUtil.isEmpty(paperId)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.orderByAsc(TmsExamEntity::getAttemptNo); |
| | | qw.orderByAsc(TmsExamEntity::getCreatorTime); |
| | | List<TmsExamEntity> exams = this.examMapper.selectList(qw); |
| | | ArrayList<TmsOnlineExamAttemptVO> list = new ArrayList<TmsOnlineExamAttemptVO>(exams.size()); |
| | | for (TmsExamEntity exam : exams) { |
| | | TmsOnlineExamAttemptVO row = new TmsOnlineExamAttemptVO(); |
| | | row.setExamId(exam.getId()); |
| | | int attemptNo = exam.getAttemptNo() == null ? 1 : exam.getAttemptNo(); |
| | | row.setAttemptNo(Integer.valueOf(attemptNo)); |
| | | row.setAttemptLabel(this.attemptLabel(attemptNo)); |
| | | row.setStatus(this.statusOf(exam)); |
| | | row.setGradeStatus(exam.getGradeStatus()); |
| | | row.setStartTime(this.fmt(exam.getStartTime())); |
| | | row.setSubmitTime(this.fmt(exam.getSubmitTime())); |
| | | row.setGotScore(exam.getTotalScore()); |
| | | row.setPassFlag(exam.getPassFlag()); |
| | | list.add(row); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class, noRollbackFor = ExamTimedOutException.class) |
| | | public TmsOnlineExamPaperVO start(String paperId) { |
| | | if (StringUtil.isEmpty(paperId)) { |
| | | throw new DataException("请选择试卷"); |
| | | } |
| | | String userId = UserProvider.getLoginUserId(); |
| | | TmsExamEntity doing = this.findDoing(userId, paperId); |
| | | if (doing != null) { |
| | | if (this.isTimedOut(doing)) { |
| | | // 超时须先交卷再返回;抛异常会导致事务回滚,答卷一直停在考试中 |
| | | this.submitInternal(doing, null); |
| | | TmsOnlineExamPaperVO vo = this.toPaperVo(doing, this.loadItems(doing.getId())); |
| | | vo.setAutoSubmitted(Boolean.TRUE); |
| | | vo.setRemainSeconds(Integer.valueOf(0)); |
| | | return vo; |
| | | } |
| | | return this.toPaperVo(doing, this.loadItems(doing.getId())); |
| | | } |
| | | TmsPaperEntity paper = this.requireOpenPaper(paperId); |
| | | this.assertExamWindow(paper); |
| | | RetakeContext retake = this.resolveRetake(userId, paperId); |
| | | if (!retake.allowStart) { |
| | | throw new DataException(retake.message); |
| | | } |
| | | TmsExamEntity exam = this.createExam(paper, userId, retake); |
| | | return this.toPaperVo(exam, this.loadItems(exam.getId())); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class, noRollbackFor = ExamTimedOutException.class) |
| | | public void saveAnswers(String examId, TmsOnlineExamSubmitForm form) { |
| | | TmsExamEntity exam = this.requireDoingExam(examId); |
| | | if (this.isTimedOut(exam)) { |
| | | this.submitInternal(exam, form == null ? null : form.getAnswers()); |
| | | throw new ExamTimedOutException(); |
| | | } |
| | | this.writeAnswers(exam.getId(), form == null ? null : form.getAnswers()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor={Exception.class}) |
| | | public TmsOnlineExamSubmitResultVO submit(String examId, TmsOnlineExamSubmitForm form) { |
| | | TmsExamEntity exam = this.requireDoingExam(examId); |
| | | return this.submitInternal(exam, form == null ? null : form.getAnswers()); |
| | | } |
| | | |
| | | private TmsOnlineExamSubmitResultVO submitInternal(TmsExamEntity exam, Map<String, Object> answers) { |
| | | List<TmsExamItemEntity> items = this.loadItems(exam.getId()); |
| | | HashMap<String, String> normalized = new HashMap<String, String>(); |
| | | for (TmsExamItemEntity item : items) { |
| | | if (answers != null && answers.containsKey(item.getId())) { |
| | | normalized.put(item.getId(), this.normalizeAnswer(answers.get(item.getId()), item.getQuestionType())); |
| | | continue; |
| | | } |
| | | normalized.put(item.getId(), item.getUserAnswer() == null ? "" : item.getUserAnswer()); |
| | | } |
| | | BigDecimal objective = BigDecimal.ZERO; |
| | | boolean pending = false; |
| | | ArrayList<TmsOnlineExamGradeItemVO> grades = new ArrayList<TmsOnlineExamGradeItemVO>(); |
| | | Date now = new Date(); |
| | | for (TmsExamItemEntity item : items) { |
| | | String userAnswer = normalized.getOrDefault(item.getId(), ""); |
| | | item.setUserAnswer(userAnswer); |
| | | TmsOnlineExamGradeItemVO grade = new TmsOnlineExamGradeItemVO(); |
| | | grade.setId(item.getId()); |
| | | grade.setCorrectAnswer(item.getCorrectAnswer()); |
| | | boolean subjective = "1".equals(item.getIsSubjective()); |
| | | grade.setSubjective(Boolean.valueOf(subjective)); |
| | | if (subjective) { |
| | | pending = true; |
| | | item.setGotScore(null); |
| | | grade.setGotScore(null); |
| | | grade.setRight(null); |
| | | } else { |
| | | boolean right = this.answerRight(item, userAnswer); |
| | | BigDecimal got = right ? this.nvl(item.getScore()) : BigDecimal.ZERO; |
| | | item.setGotScore(got); |
| | | objective = objective.add(got); |
| | | grade.setGotScore(got); |
| | | grade.setRight(Boolean.valueOf(right)); |
| | | } |
| | | item.setLastModifyTime(now); |
| | | this.examItemMapper.updateById(item); |
| | | grades.add(grade); |
| | | } |
| | | objective = objective.setScale(1, RoundingMode.HALF_UP); |
| | | exam.setObjectiveScore(objective); |
| | | exam.setSubmitTime(now); |
| | | exam.setExamStatus(EXAM_SUBMITTED); |
| | | exam.setLastModifyTime(now); |
| | | if (pending) { |
| | | exam.setGradeStatus(GRADE_PENDING); |
| | | exam.setSubjectiveScore(null); |
| | | exam.setTotalScore(objective); |
| | | exam.setPassFlag(null); |
| | | } else { |
| | | exam.setGradeStatus(GRADE_AUTO); |
| | | exam.setSubjectiveScore(BigDecimal.ZERO); |
| | | exam.setTotalScore(objective); |
| | | exam.setPassFlag(this.passFlag(objective, exam.getPassScore())); |
| | | } |
| | | this.examMapper.updateById(exam); |
| | | TmsOnlineExamSubmitResultVO result = new TmsOnlineExamSubmitResultVO(); |
| | | result.setObjectiveScore(exam.getObjectiveScore()); |
| | | result.setTotalScore(exam.getTotalScore()); |
| | | result.setPassFlag(exam.getPassFlag()); |
| | | result.setGradeStatus(exam.getGradeStatus()); |
| | | result.setPendingGrade(Boolean.valueOf(pending)); |
| | | result.setItems(grades); |
| | | return result; |
| | | } |
| | | |
| | | private TmsExamEntity createExam(TmsPaperEntity paper, String userId, RetakeContext retake) { |
| | | List<QuestionSnap> snaps = this.loadPaperQuestions(paper.getId()); |
| | | if (snaps.isEmpty()) { |
| | | throw new DataException("试卷没有试题,无法开考"); |
| | | } |
| | | if (SORT_RANDOM.equals(paper.getSortMode())) { |
| | | Collections.shuffle(snaps); |
| | | } |
| | | Date now = new Date(); |
| | | TmsExamEntity exam = new TmsExamEntity(); |
| | | exam.setId(RandomUtil.uuId()); |
| | | exam.setTaskId(retake == null ? null : retake.taskId); |
| | | exam.setPersonTaskId(retake == null ? null : retake.personTaskId); |
| | | exam.setPaperId(paper.getId()); |
| | | exam.setPaperName(paper.getPaperName()); |
| | | exam.setUserId(userId); |
| | | exam.setAttemptNo(Integer.valueOf(retake == null || retake.nextAttemptNo == null ? 1 : retake.nextAttemptNo)); |
| | | exam.setStartTime(now); |
| | | exam.setDurationMin(paper.getDurationMin()); |
| | | BigDecimal passScore = paper.getPassScore(); |
| | | if (retake != null && retake.passScore != null) { |
| | | passScore = retake.passScore; |
| | | } |
| | | exam.setPassScore(passScore); |
| | | exam.setGradeStatus(GRADE_AUTO); |
| | | exam.setExamStatus(EXAM_DOING); |
| | | exam.setCreatorUserId(userId); |
| | | exam.setCreatorTime(now); |
| | | this.examMapper.insert(exam); |
| | | int sort = 1; |
| | | for (QuestionSnap snap : snaps) { |
| | | TmsExamItemEntity item = new TmsExamItemEntity(); |
| | | item.setId(RandomUtil.uuId()); |
| | | item.setForeignId(exam.getId()); |
| | | item.setQuestionId(snap.question.getId()); |
| | | item.setQuestionType(snap.question.getQuestionType()); |
| | | item.setStem(snap.question.getStem()); |
| | | item.setOptionsJson(this.toJson(snap.options)); |
| | | item.setCorrectAnswer(this.correctOf(snap)); |
| | | item.setScore(snap.score); |
| | | item.setIsSubjective(TYPE_ESSAY.equals(snap.question.getQuestionType()) ? "1" : "0"); |
| | | item.setAnalysis(snap.question.getAnalysis()); |
| | | item.setSortNo(Integer.valueOf(sort++)); |
| | | item.setCreatorUserId(userId); |
| | | item.setCreatorTime(now); |
| | | this.examItemMapper.insert(item); |
| | | } |
| | | return exam; |
| | | } |
| | | |
| | | private List<QuestionSnap> loadPaperQuestions(String paperId) { |
| | | LambdaQueryWrapper<TmsPaperSectionEntity> secQw = new LambdaQueryWrapper<>(); |
| | | secQw.eq(TmsPaperSectionEntity::getForeignId, paperId); |
| | | secQw.orderByAsc(TmsPaperSectionEntity::getSortNo); |
| | | List<TmsPaperSectionEntity> sections = this.sectionMapper.selectList(secQw); |
| | | LambdaQueryWrapper<TmsPaperQuestionEntity> qQw = new LambdaQueryWrapper<>(); |
| | | qQw.eq(TmsPaperQuestionEntity::getForeignId, paperId); |
| | | qQw.orderByAsc(TmsPaperQuestionEntity::getSortNo); |
| | | List<TmsPaperQuestionEntity> links = this.paperQuestionMapper.selectList(qQw); |
| | | HashMap<String, List<TmsPaperQuestionEntity>> bySection = new HashMap<>(); |
| | | for (TmsPaperQuestionEntity link : links) { |
| | | bySection.computeIfAbsent(link.getSectionId(), k -> new ArrayList<>()).add(link); |
| | | } |
| | | List<String> questionIds = links.stream().map(TmsPaperQuestionEntity::getQuestionId).collect(Collectors.toList()); |
| | | HashMap<String, TmsQuestionEntity> questions = new HashMap<String, TmsQuestionEntity>(); |
| | | if (!questionIds.isEmpty()) { |
| | | for (TmsQuestionEntity q : this.questionMapper.selectBatchIds(questionIds)) { |
| | | questions.put(q.getId(), q); |
| | | } |
| | | } |
| | | Map<String, List<TmsQuestionOptionEntity>> optionMap = this.loadOptions(questionIds); |
| | | ArrayList<QuestionSnap> snaps = new ArrayList<QuestionSnap>(); |
| | | for (TmsPaperSectionEntity section : sections) { |
| | | List<TmsPaperQuestionEntity> rows = bySection.getOrDefault(section.getId(), Collections.emptyList()); |
| | | rows.sort(Comparator.comparing(r -> r.getSortNo() == null ? 0 : r.getSortNo())); |
| | | for (TmsPaperQuestionEntity row : rows) { |
| | | TmsQuestionEntity question = questions.get(row.getQuestionId()); |
| | | if (question == null) { |
| | | throw new DataException("试卷中的试题已不存在,无法开考"); |
| | | } |
| | | QuestionSnap snap = new QuestionSnap(); |
| | | snap.question = question; |
| | | snap.score = row.getScore() == null ? BigDecimal.ZERO : row.getScore(); |
| | | snap.options = this.toStoredOptions(optionMap.getOrDefault(question.getId(), Collections.emptyList())); |
| | | snaps.add(snap); |
| | | } |
| | | } |
| | | return snaps; |
| | | } |
| | | |
| | | private void writeAnswers(String examId, Map<String, Object> answers) { |
| | | List<TmsExamItemEntity> items = this.loadItems(examId); |
| | | Date now = new Date(); |
| | | for (TmsExamItemEntity item : items) { |
| | | Object raw = answers == null ? null : answers.get(item.getId()); |
| | | item.setUserAnswer(this.normalizeAnswer(raw, item.getQuestionType())); |
| | | item.setLastModifyTime(now); |
| | | this.examItemMapper.updateById(item); |
| | | } |
| | | } |
| | | |
| | | private TmsOnlineExamPaperVO toPaperVo(TmsExamEntity exam, List<TmsExamItemEntity> items) { |
| | | TmsOnlineExamPaperVO vo = new TmsOnlineExamPaperVO(); |
| | | vo.setExamId(exam.getId()); |
| | | vo.setPaperId(exam.getPaperId()); |
| | | vo.setPaperName(exam.getPaperName()); |
| | | vo.setPassScore(exam.getPassScore()); |
| | | vo.setDurationMin(exam.getDurationMin()); |
| | | vo.setStartTime(this.fmt(exam.getStartTime())); |
| | | vo.setRemainSeconds(this.remainSeconds(exam)); |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | ArrayList<TmsOnlineExamQuestionVO> questions = new ArrayList<TmsOnlineExamQuestionVO>(); |
| | | for (TmsExamItemEntity item : items) { |
| | | total = total.add(this.nvl(item.getScore())); |
| | | TmsOnlineExamQuestionVO q = new TmsOnlineExamQuestionVO(); |
| | | q.setId(item.getId()); |
| | | q.setQuestionId(item.getQuestionId()); |
| | | q.setQuestionType(item.getQuestionType()); |
| | | q.setStem(item.getStem()); |
| | | q.setScore(item.getScore()); |
| | | q.setUserAnswer(item.getUserAnswer()); |
| | | q.setOptions(this.toClientOptions(item.getOptionsJson())); |
| | | questions.add(q); |
| | | } |
| | | vo.setTotalScore(total); |
| | | vo.setQuestions(questions); |
| | | vo.setAttemptNo(Integer.valueOf(exam.getAttemptNo() == null ? 1 : exam.getAttemptNo())); |
| | | vo.setAttemptLabel(this.attemptLabel(vo.getAttemptNo())); |
| | | return vo; |
| | | } |
| | | |
| | | private List<TmsOnlineExamQuestionVO> toReviewQuestions(List<TmsExamItemEntity> items) { |
| | | ArrayList<TmsOnlineExamQuestionVO> questions = new ArrayList<TmsOnlineExamQuestionVO>(); |
| | | for (TmsExamItemEntity item : items) { |
| | | TmsOnlineExamQuestionVO q = new TmsOnlineExamQuestionVO(); |
| | | q.setId(item.getId()); |
| | | q.setQuestionId(item.getQuestionId()); |
| | | q.setQuestionType(item.getQuestionType()); |
| | | q.setStem(item.getStem()); |
| | | q.setScore(item.getScore()); |
| | | q.setGotScore(item.getGotScore()); |
| | | q.setUserAnswer(item.getUserAnswer()); |
| | | q.setCorrectAnswer(item.getCorrectAnswer()); |
| | | boolean subjective = "1".equals(item.getIsSubjective()); |
| | | q.setSubjective(Boolean.valueOf(subjective)); |
| | | if (subjective) { |
| | | q.setRight(null); |
| | | } else if (item.getScore() != null && item.getScore().compareTo(BigDecimal.ZERO) > 0) { |
| | | q.setRight(Boolean.valueOf(this.nvl(item.getGotScore()).compareTo(item.getScore()) >= 0)); |
| | | } else { |
| | | q.setRight(Boolean.valueOf(Objects.equals(this.normalizeCorrect(item.getCorrectAnswer()), this.normalizeCorrect(item.getUserAnswer())))); |
| | | } |
| | | q.setOptions(this.toReviewOptions(item.getOptionsJson())); |
| | | questions.add(q); |
| | | } |
| | | return questions; |
| | | } |
| | | |
| | | private List<TmsOnlineExamOptionVO> toReviewOptions(String json) { |
| | | List<StoredOption> stored = this.parseOptions(json); |
| | | ArrayList<TmsOnlineExamOptionVO> list = new ArrayList<TmsOnlineExamOptionVO>(stored.size()); |
| | | for (StoredOption opt : stored) { |
| | | TmsOnlineExamOptionVO vo = new TmsOnlineExamOptionVO(); |
| | | vo.setOptionLabel(opt.getOptionLabel()); |
| | | vo.setOptionContent(opt.getOptionContent()); |
| | | vo.setIsCorrect(opt.getIsCorrect()); |
| | | list.add(vo); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private List<TmsOnlineExamOptionVO> toClientOptions(String json) { |
| | | List<StoredOption> stored = this.parseOptions(json); |
| | | ArrayList<TmsOnlineExamOptionVO> list = new ArrayList<TmsOnlineExamOptionVO>(stored.size()); |
| | | for (StoredOption opt : stored) { |
| | | TmsOnlineExamOptionVO vo = new TmsOnlineExamOptionVO(); |
| | | vo.setOptionLabel(opt.getOptionLabel()); |
| | | vo.setOptionContent(opt.getOptionContent()); |
| | | list.add(vo); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private TmsOnlineExamListVO toListVo(TmsPaperEntity paper, TmsExamEntity exam, String status) { |
| | | TmsOnlineExamListVO vo = new TmsOnlineExamListVO(); |
| | | vo.setId(paper.getId()); |
| | | vo.setPaperId(paper.getId()); |
| | | vo.setPaperName(paper.getPaperName()); |
| | | vo.setStatus(status); |
| | | vo.setExamStart(this.fmt(paper.getExamStart())); |
| | | vo.setExamEnd(this.fmt(paper.getExamEnd())); |
| | | vo.setExamTimeText(this.timeText(paper.getExamStart(), paper.getExamEnd())); |
| | | vo.setTotalScore(paper.getTotalScore()); |
| | | vo.setPassScore(paper.getPassScore()); |
| | | vo.setDurationMin(paper.getDurationMin()); |
| | | if (exam != null && EXAM_SUBMITTED.equals(exam.getExamStatus())) { |
| | | vo.setGotScore(exam.getTotalScore()); |
| | | vo.setPassFlag(exam.getPassFlag()); |
| | | vo.setExamId(exam.getId()); |
| | | } else if (exam != null) { |
| | | vo.setExamId(exam.getId()); |
| | | } |
| | | this.fillRetakeMeta(vo, paper.getId(), exam, UserProvider.getLoginUserId()); |
| | | return vo; |
| | | } |
| | | |
| | | private void fillRetakeMeta(TmsOnlineExamListVO vo, String paperId, TmsExamEntity exam, String userId) { |
| | | RetakeMeta meta = this.buildRetakeMeta(paperId, exam, userId); |
| | | vo.setAttemptNo(meta.attemptNo); |
| | | vo.setAttemptLabel(meta.attemptLabel); |
| | | vo.setSubmittedCount(meta.submittedCount); |
| | | vo.setRetakeLimit(meta.retakeLimit); |
| | | vo.setRemainingRetakes(meta.remainingRetakes); |
| | | vo.setCanRetake(meta.canRetake); |
| | | } |
| | | |
| | | private void fillRetakeMeta(TmsOnlineExamDetailVO vo, String paperId, TmsExamEntity exam, String userId) { |
| | | RetakeMeta meta = this.buildRetakeMeta(paperId, exam, userId); |
| | | vo.setAttemptNo(meta.attemptNo); |
| | | vo.setAttemptLabel(meta.attemptLabel); |
| | | vo.setSubmittedCount(meta.submittedCount); |
| | | vo.setRetakeLimit(meta.retakeLimit); |
| | | vo.setRemainingRetakes(meta.remainingRetakes); |
| | | vo.setCanRetake(meta.canRetake); |
| | | } |
| | | |
| | | private RetakeMeta buildRetakeMeta(String paperId, TmsExamEntity exam, String userId) { |
| | | RetakeMeta meta = new RetakeMeta(); |
| | | RetakeContext ctx = this.resolveRetake(userId, paperId); |
| | | meta.retakeLimit = ctx.retakeLimit; |
| | | meta.submittedCount = ctx.submittedCount == null ? 0 : ctx.submittedCount; |
| | | meta.attemptNo = exam != null && exam.getAttemptNo() != null ? exam.getAttemptNo() : (ctx.nextAttemptNo != null ? ctx.nextAttemptNo : Integer.valueOf(1)); |
| | | meta.attemptLabel = this.attemptLabel(meta.attemptNo); |
| | | if (meta.retakeLimit == null) { |
| | | meta.remainingRetakes = 0; |
| | | meta.canRetake = false; |
| | | } else { |
| | | int usedRetakes = Math.max(0, meta.submittedCount - 1); |
| | | meta.remainingRetakes = Math.max(0, meta.retakeLimit - usedRetakes); |
| | | boolean submittedFail = exam != null && EXAM_SUBMITTED.equals(exam.getExamStatus()) && !"1".equals(exam.getPassFlag()); |
| | | meta.canRetake = submittedFail && ctx.allowStart; |
| | | } |
| | | return meta; |
| | | } |
| | | |
| | | private String attemptLabel(Integer attemptNo) { |
| | | int n; |
| | | int n2 = n = attemptNo == null ? 1 : attemptNo; |
| | | if (n <= 1) { |
| | | return "首考"; |
| | | } |
| | | return "补考(第" + (n - 1) + "次)"; |
| | | } |
| | | |
| | | private Map<String, TmsExamEntity> loadLatestExams(String userId, List<TmsPaperEntity> papers) { |
| | | HashMap<String, TmsExamEntity> map = new HashMap<String, TmsExamEntity>(); |
| | | if (papers.isEmpty() || StringUtil.isEmpty(userId)) { |
| | | return map; |
| | | } |
| | | List<String> ids = papers.stream().map(TmsPaperEntity::getId).collect(Collectors.toList()); |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.in(TmsExamEntity::getPaperId, ids); |
| | | qw.orderByDesc(TmsExamEntity::getCreatorTime); |
| | | for (TmsExamEntity exam : this.examMapper.selectList(qw)) { |
| | | TmsExamEntity exists = map.get(exam.getPaperId()); |
| | | if (exists != null && !EXAM_DOING.equals(exam.getExamStatus())) continue; |
| | | map.put(exam.getPaperId(), exam); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private TmsExamEntity latestExam(String userId, String paperId) { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.orderByDesc(TmsExamEntity::getCreatorTime); |
| | | List<TmsExamEntity> list = this.examMapper.selectList(qw); |
| | | TmsExamEntity picked = null; |
| | | for (TmsExamEntity exam : list) { |
| | | if (EXAM_DOING.equals(exam.getExamStatus())) { |
| | | return exam; |
| | | } |
| | | if (picked != null) continue; |
| | | picked = exam; |
| | | } |
| | | return picked; |
| | | } |
| | | |
| | | private TmsExamEntity findDoing(String userId, String paperId) { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_DOING); |
| | | qw.orderByDesc(TmsExamEntity::getCreatorTime); |
| | | qw.last("limit 1"); |
| | | return this.examMapper.selectOne(qw); |
| | | } |
| | | |
| | | private TmsExamEntity latestSubmitted(String userId, String paperId) { |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_SUBMITTED); |
| | | qw.orderByDesc(TmsExamEntity::getCreatorTime); |
| | | qw.last("limit 1"); |
| | | return this.examMapper.selectOne(qw); |
| | | } |
| | | |
| | | private RetakeContext resolveRetake(String userId, String paperId) { |
| | | RetakeContext ctx = new RetakeContext(); |
| | | ctx.allowStart = true; |
| | | ctx.nextAttemptNo = 1; |
| | | TmsExamEntity last = this.latestSubmitted(userId, paperId); |
| | | TmsPersonTaskEntity personTask = this.findActivePersonTask(userId, paperId); |
| | | TmsTaskEntity task = null; |
| | | if (personTask != null) { |
| | | task = this.taskMapper.selectById((personTask.getTaskId())); |
| | | } |
| | | if (task != null) { |
| | | ctx.taskId = task.getId(); |
| | | ctx.personTaskId = personTask == null ? null : personTask.getId(); |
| | | ctx.passScore = task.getPassScore(); |
| | | int retakeLimit = task.getRetakeLimit() == null ? 1 : Math.max(0, task.getRetakeLimit()); |
| | | long submitted = this.countSubmitted(userId, paperId, task.getId()); |
| | | ctx.retakeLimit = retakeLimit; |
| | | ctx.submittedCount = (int)submitted; |
| | | ctx.nextAttemptNo = (int)submitted + 1; |
| | | if (last == null) { |
| | | return ctx; |
| | | } |
| | | if ("1".equals(last.getPassFlag())) { |
| | | ctx.allowStart = false; |
| | | ctx.message = "该试卷已合格,无需重考"; |
| | | return ctx; |
| | | } |
| | | if (submitted > (long)retakeLimit) { |
| | | ctx.allowStart = false; |
| | | ctx.message = "补考次数已用尽(上限 " + retakeLimit + " 次)"; |
| | | return ctx; |
| | | } |
| | | return ctx; |
| | | } |
| | | if (last != null) { |
| | | ctx.submittedCount = 1; |
| | | ctx.retakeLimit = 0; |
| | | ctx.allowStart = false; |
| | | ctx.message = "该试卷已交卷"; |
| | | } |
| | | return ctx; |
| | | } |
| | | |
| | | private long countSubmitted(String userId, String paperId, String taskId) { |
| | | Long cnt; |
| | | LambdaQueryWrapper<TmsExamEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamEntity::getUserId, userId); |
| | | qw.eq(TmsExamEntity::getPaperId, paperId); |
| | | qw.eq(TmsExamEntity::getExamStatus, EXAM_SUBMITTED); |
| | | if (StringUtil.isNotEmpty(taskId)) { |
| | | qw.and(w -> ((w.eq(TmsExamEntity::getTaskId, taskId)).or()).isNull(TmsExamEntity::getTaskId)); |
| | | } |
| | | return (cnt = this.examMapper.selectCount(qw)) == null ? 0L : cnt; |
| | | } |
| | | |
| | | private TmsPersonTaskEntity findActivePersonTask(String userId, String paperId) { |
| | | List<TmsTaskEntity> tasks = this.taskMapper.selectList( |
| | | new LambdaQueryWrapper<TmsTaskEntity>() |
| | | .eq(TmsTaskEntity::getPaperId, paperId) |
| | | .eq(TmsTaskEntity::getPublishStatus, "published") |
| | | .orderByDesc(TmsTaskEntity::getPublishTime)); |
| | | for (TmsTaskEntity t : tasks) { |
| | | TmsPersonTaskEntity pt = this.findPersonTask(t.getId(), userId); |
| | | if (pt == null || "cancelled".equals(pt.getBizStatus())) { |
| | | continue; |
| | | } |
| | | return pt; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private TmsPersonTaskEntity findPersonTask(String taskId, String userId) { |
| | | return this.personTaskMapper.selectOne(new LambdaQueryWrapper<TmsPersonTaskEntity>() |
| | | .eq(TmsPersonTaskEntity::getTaskId, taskId) |
| | | .eq(TmsPersonTaskEntity::getUserId, userId) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | private TmsExamEntity requireDoingExam(String examId) { |
| | | TmsExamEntity exam = this.examMapper.selectById(examId); |
| | | if (exam == null) { |
| | | throw new DataException("答卷不存在"); |
| | | } |
| | | if (!Objects.equals(UserProvider.getLoginUserId(), exam.getUserId())) { |
| | | throw new DataException("无权操作该答卷"); |
| | | } |
| | | if (!EXAM_DOING.equals(exam.getExamStatus())) { |
| | | throw new DataException("试卷已交卷"); |
| | | } |
| | | return exam; |
| | | } |
| | | |
| | | private TmsPaperEntity requireOpenPaper(String paperId) { |
| | | TmsPaperEntity paper = this.paperMapper.selectById(paperId); |
| | | if (paper == null) { |
| | | throw new DataException("试卷不存在"); |
| | | } |
| | | if (!STATUS_OPEN.equals(paper.getBizStatus())) { |
| | | throw new DataException("试卷未开放,不能参加考试"); |
| | | } |
| | | return paper; |
| | | } |
| | | |
| | | private void assertExamWindow(TmsPaperEntity paper) { |
| | | Date now = new Date(); |
| | | if (paper.getExamStart() != null && now.before(paper.getExamStart())) { |
| | | throw new DataException("未到考试时间"); |
| | | } |
| | | if (paper.getExamEnd() != null && now.after(paper.getExamEnd())) { |
| | | throw new DataException("考试时间已结束"); |
| | | } |
| | | } |
| | | |
| | | private List<TmsExamItemEntity> loadItems(String examId) { |
| | | LambdaQueryWrapper<TmsExamItemEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsExamItemEntity::getForeignId, examId); |
| | | qw.orderByAsc(TmsExamItemEntity::getSortNo); |
| | | return this.examItemMapper.selectList(qw); |
| | | } |
| | | |
| | | private Map<String, List<TmsQuestionOptionEntity>> loadOptions(List<String> questionIds) { |
| | | if (questionIds == null || questionIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | LambdaQueryWrapper<TmsQuestionOptionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.in(TmsQuestionOptionEntity::getForeignId, questionIds); |
| | | qw.orderByAsc(TmsQuestionOptionEntity::getSortNo); |
| | | HashMap<String, List<TmsQuestionOptionEntity>> map = new HashMap<String, List<TmsQuestionOptionEntity>>(); |
| | | for (TmsQuestionOptionEntity opt : this.optionMapper.selectList(qw)) { |
| | | map.computeIfAbsent(opt.getForeignId(), k -> new ArrayList()).add(opt); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private List<StoredOption> toStoredOptions(List<TmsQuestionOptionEntity> opts) { |
| | | ArrayList<StoredOption> list = new ArrayList<StoredOption>(); |
| | | for (TmsQuestionOptionEntity opt : opts) { |
| | | StoredOption stored = new StoredOption(); |
| | | stored.setOptionLabel(opt.getOptionLabel()); |
| | | stored.setOptionContent(opt.getOptionContent()); |
| | | stored.setIsCorrect(opt.getIsCorrect()); |
| | | list.add(stored); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private String correctOf(QuestionSnap snap) { |
| | | String type = snap.question.getQuestionType(); |
| | | if (TYPE_ESSAY.equals(type)) { |
| | | return ""; |
| | | } |
| | | if (TYPE_BLANK.equals(type)) { |
| | | return snap.options.stream().map(StoredOption::getOptionContent).filter(StringUtil::isNotEmpty).collect(Collectors.joining("?")); |
| | | } |
| | | return snap.options.stream().filter(o -> "1".equals(o.getIsCorrect())).map(StoredOption::getOptionLabel).filter(StringUtil::isNotEmpty).sorted().collect(Collectors.joining(",")); |
| | | } |
| | | |
| | | private boolean answerRight(TmsExamItemEntity item, String userAnswer) { |
| | | if (StringUtil.isEmpty(item.getCorrectAnswer())) { |
| | | return false; |
| | | } |
| | | if (TYPE_BLANK.equals(item.getQuestionType())) { |
| | | return item.getCorrectAnswer().trim().equals(userAnswer == null ? "" : userAnswer.trim()); |
| | | } |
| | | return Objects.equals(this.normalizeCorrect(item.getCorrectAnswer()), this.normalizeCorrect(userAnswer)); |
| | | } |
| | | |
| | | private String normalizeAnswer(Object raw, String questionType) { |
| | | if (raw == null) { |
| | | return ""; |
| | | } |
| | | if (TYPE_MULTI.equals(questionType)) { |
| | | LinkedHashSet<String> labels = new LinkedHashSet<String>(); |
| | | if (raw instanceof List) { |
| | | for (Object o : (List)raw) { |
| | | if (o == null || !StringUtil.isNotEmpty(String.valueOf(o))) continue; |
| | | labels.add(String.valueOf(o).trim()); |
| | | } |
| | | } else { |
| | | String text = String.valueOf(raw).trim(); |
| | | if (StringUtil.isNotEmpty(text)) { |
| | | for (String part : text.split("[,?]")) { |
| | | if (!StringUtil.isNotEmpty(part.trim())) continue; |
| | | labels.add(part.trim()); |
| | | } |
| | | } |
| | | } |
| | | return labels.stream().sorted().collect(Collectors.joining(",")); |
| | | } |
| | | return String.valueOf(raw).trim(); |
| | | } |
| | | |
| | | private String normalizeCorrect(String correct) { |
| | | if (StringUtil.isEmpty(correct)) { |
| | | return ""; |
| | | } |
| | | return Arrays.stream(correct.split("[,?]")).map(String::trim).filter(StringUtil::isNotEmpty).sorted().collect(Collectors.joining(",")); |
| | | } |
| | | |
| | | private boolean isTimedOut(TmsExamEntity exam) { |
| | | Integer remain = this.remainSeconds(exam); |
| | | return remain != null && remain <= 0; |
| | | } |
| | | |
| | | private Integer remainSeconds(TmsExamEntity exam) { |
| | | if (exam == null || exam.getDurationMin() == null || exam.getDurationMin() <= 0 || exam.getStartTime() == null) { |
| | | return null; |
| | | } |
| | | long endMs = exam.getStartTime().getTime() + (long)exam.getDurationMin().intValue() * 60000L; |
| | | long remain = (endMs - System.currentTimeMillis()) / 1000L; |
| | | return (int)Math.max(0L, remain); |
| | | } |
| | | |
| | | private String passFlag(BigDecimal score, BigDecimal passScore) { |
| | | if (passScore == null) { |
| | | return null; |
| | | } |
| | | return this.nvl(score).compareTo(passScore) >= 0 ? "1" : "0"; |
| | | } |
| | | |
| | | private String statusOf(TmsExamEntity exam) { |
| | | if (exam == null) { |
| | | return NOT_STARTED; |
| | | } |
| | | if (EXAM_DOING.equals(exam.getExamStatus())) { |
| | | return EXAM_DOING; |
| | | } |
| | | return EXAM_SUBMITTED; |
| | | } |
| | | |
| | | private String timeText(Date start, Date end) { |
| | | if (start == null && end == null) { |
| | | return "??"; |
| | | } |
| | | String left = start == null ? "??" : this.fmt(start); |
| | | String right = end == null ? "??" : this.fmt(end); |
| | | return left + " ~ " + right; |
| | | } |
| | | |
| | | private String fmt(Date date) { |
| | | return date == null ? null : DateUtil.dateToString((Date)date, (String)FMT); |
| | | } |
| | | |
| | | private BigDecimal nvl(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private String toJson(Object obj) { |
| | | try { |
| | | return OBJECT_MAPPER.writeValueAsString(obj); |
| | | } |
| | | catch (Exception e) { |
| | | throw new DataException("JSON序列化失败"); |
| | | } |
| | | } |
| | | |
| | | private List<StoredOption> parseOptions(String json) { |
| | | if (StringUtil.isEmpty(json)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | try { |
| | | return (List)OBJECT_MAPPER.readValue(json, (TypeReference)new TypeReference<List<StoredOption>>(){}); |
| | | } |
| | | catch (Exception e) { |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | | |
| | | private static class RetakeContext { |
| | | private boolean allowStart; |
| | | private String message; |
| | | private String taskId; |
| | | private String personTaskId; |
| | | private Integer nextAttemptNo; |
| | | private BigDecimal passScore; |
| | | private Integer retakeLimit; |
| | | private Integer submittedCount; |
| | | |
| | | private RetakeContext() { |
| | | } |
| | | } |
| | | |
| | | private static class QuestionSnap { |
| | | private TmsQuestionEntity question; |
| | | private BigDecimal score; |
| | | private List<StoredOption> options; |
| | | |
| | | private QuestionSnap() { |
| | | } |
| | | } |
| | | |
| | | @JsonIgnoreProperties(ignoreUnknown=true) |
| | | private static class StoredOption { |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | private String isCorrect; |
| | | public StoredOption() { |
| | | } |
| | | public String getOptionLabel() { |
| | | return this.optionLabel; |
| | | } |
| | | public String getOptionContent() { |
| | | return this.optionContent; |
| | | } |
| | | public String getIsCorrect() { |
| | | return this.isCorrect; |
| | | } |
| | | public void setOptionLabel(String optionLabel) { |
| | | this.optionLabel = optionLabel; |
| | | } |
| | | public void setOptionContent(String optionContent) { |
| | | this.optionContent = optionContent; |
| | | } |
| | | public void setIsCorrect(String isCorrect) { |
| | | this.isCorrect = isCorrect; |
| | | } |
| | | public boolean equals(Object o) { |
| | | if (o == this) { |
| | | return true; |
| | | } |
| | | if (!(o instanceof StoredOption)) { |
| | | return false; |
| | | } |
| | | StoredOption other = (StoredOption)o; |
| | | if (!other.canEqual(this)) { |
| | | return false; |
| | | } |
| | | String this$optionLabel = this.getOptionLabel(); |
| | | String other$optionLabel = other.getOptionLabel(); |
| | | if (this$optionLabel == null ? other$optionLabel != null : !this$optionLabel.equals(other$optionLabel)) { |
| | | return false; |
| | | } |
| | | String this$optionContent = this.getOptionContent(); |
| | | String other$optionContent = other.getOptionContent(); |
| | | if (this$optionContent == null ? other$optionContent != null : !this$optionContent.equals(other$optionContent)) { |
| | | return false; |
| | | } |
| | | String this$isCorrect = this.getIsCorrect(); |
| | | String other$isCorrect = other.getIsCorrect(); |
| | | return !(this$isCorrect == null ? other$isCorrect != null : !this$isCorrect.equals(other$isCorrect)); |
| | | } |
| | | protected boolean canEqual(Object other) { |
| | | return other instanceof StoredOption; |
| | | } |
| | | public int hashCode() { |
| | | int PRIME = 59; |
| | | int result = 1; |
| | | String $optionLabel = this.getOptionLabel(); |
| | | result = result * 59 + ($optionLabel == null ? 43 : $optionLabel.hashCode()); |
| | | String $optionContent = this.getOptionContent(); |
| | | result = result * 59 + ($optionContent == null ? 43 : $optionContent.hashCode()); |
| | | String $isCorrect = this.getIsCorrect(); |
| | | result = result * 59 + ($isCorrect == null ? 43 : $isCorrect.hashCode()); |
| | | return result; |
| | | } |
| | | public String toString() { |
| | | return "TmsOnlineExamServiceImpl.StoredOption(optionLabel=" + this.getOptionLabel() + ", optionContent=" + this.getOptionContent() + ", isCorrect=" + this.getIsCorrect() + ")"; |
| | | } |
| | | } |
| | | |
| | | /** 超时已自动交卷:配合 noRollbackFor,保证交卷结果先提交 */ |
| | | public static class ExamTimedOutException extends DataException { |
| | | public ExamTimedOutException() { |
| | | super("考试时间已到,已自动交卷"); |
| | | } |
| | | } |
| | | |
| | | private static class RetakeMeta { |
| | | private Integer attemptNo; |
| | | private String attemptLabel; |
| | | private Integer submittedCount; |
| | | private Integer retakeLimit; |
| | | private Integer remainingRetakes; |
| | | private Boolean canRetake; |
| | | |
| | | private RetakeMeta() { |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.TmsPaperQuestionEntity; |
| | | import jnpf.tmsEntity.TmsPaperSectionEntity; |
| | | import jnpf.tmsEntity.TmsQuestionBankEntity; |
| | | import jnpf.tmsEntity.TmsQuestionEntity; |
| | | import jnpf.tmsEntity.paper.TmsPaperForm; |
| | | import jnpf.tmsEntity.paper.TmsPaperQuestionForm; |
| | | import jnpf.tmsEntity.paper.TmsPaperQuery; |
| | | import jnpf.tmsEntity.paper.TmsPaperSectionForm; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsMapper.TmsPaperQuestionMapper; |
| | | import jnpf.tmsMapper.TmsPaperSectionMapper; |
| | | import jnpf.tmsMapper.TmsQuestionBankMapper; |
| | | import jnpf.tmsMapper.TmsQuestionMapper; |
| | | import jnpf.tmsService.TmsPaperService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsPaperServiceImpl implements TmsPaperService { |
| | | |
| | | private static final String STATUS_OPEN = "open"; |
| | | private static final String STATUS_INVALID = "invalid"; |
| | | private static final String SORT_PAPER = "paper"; |
| | | private static final Set<String> SUBJECTIVE_TYPES = Collections.singleton("essay"); |
| | | |
| | | private final TmsPaperMapper paperMapper; |
| | | private final TmsPaperSectionMapper sectionMapper; |
| | | private final TmsPaperQuestionMapper paperQuestionMapper; |
| | | private final TmsQuestionMapper questionMapper; |
| | | private final TmsQuestionBankMapper bankMapper; |
| | | |
| | | @Override |
| | | public List<TmsPaperForm> getList(TmsPaperQuery query) { |
| | | LambdaQueryWrapper<TmsPaperEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getBizStatus())) { |
| | | qw.eq(TmsPaperEntity::getBizStatus, query.getBizStatus()); |
| | | } else { |
| | | qw.ne(TmsPaperEntity::getBizStatus, STATUS_INVALID); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsPaperEntity::getPaperName, kw).or().like(TmsPaperEntity::getPaperNo, kw)); |
| | | } |
| | | qw.orderByDesc(TmsPaperEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsPaperEntity> page = paperMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | Map<String, Integer> countMap = loadQuestionCounts(page.getRecords()); |
| | | List<TmsPaperForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsPaperEntity row : page.getRecords()) { |
| | | TmsPaperForm form = toHeaderForm(row); |
| | | form.setQuestionCount(countMap.getOrDefault(row.getId(), 0)); |
| | | list.add(form); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsPaperForm getInfo(String id) { |
| | | TmsPaperEntity entity = requireOne(id); |
| | | TmsPaperForm form = toHeaderForm(entity); |
| | | form.setSections(loadSections(id)); |
| | | int count = 0; |
| | | if (form.getSections() != null) { |
| | | for (TmsPaperSectionForm sec : form.getSections()) { |
| | | if (sec.getQuestions() != null) { |
| | | count += sec.getQuestions().size(); |
| | | } |
| | | } |
| | | } |
| | | form.setQuestionCount(count); |
| | | return form; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsPaperForm form) { |
| | | validateForm(form); |
| | | String id = RandomUtil.uuId(); |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | |
| | | TmsPaperEntity entity = new TmsPaperEntity(); |
| | | entity.setId(id); |
| | | entity.setPaperNo(nextPaperNo()); |
| | | applyHeader(entity, form); |
| | | entity.setCreatorUserId(userId); |
| | | entity.setCreatorTime(now); |
| | | paperMapper.insert(entity); |
| | | |
| | | saveComposition(id, form.getSections(), userId, now); |
| | | refreshComputed(id); |
| | | return id; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsPaperForm form) { |
| | | TmsPaperEntity entity = requireOne(id); |
| | | if (STATUS_INVALID.equals(entity.getBizStatus())) { |
| | | throw new DataException("已废弃试卷不可编辑"); |
| | | } |
| | | if (STATUS_OPEN.equals(entity.getBizStatus())) { |
| | | throw new DataException("请先停用后再编辑"); |
| | | } |
| | | validateForm(form); |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | |
| | | applyHeader(entity, form); |
| | | entity.setLastModifyUserId(userId); |
| | | entity.setLastModifyTime(now); |
| | | paperMapper.updateById(entity); |
| | | |
| | | // 章节/试题全量替换(已考快照在 tms_exam_item,改卷不影响历史) |
| | | clearComposition(id); |
| | | saveComposition(id, form.getSections(), userId, now); |
| | | refreshComputed(id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void invalidate(String id) { |
| | | TmsPaperEntity entity = requireOne(id); |
| | | if (STATUS_INVALID.equals(entity.getBizStatus())) { |
| | | return; |
| | | } |
| | | if (STATUS_OPEN.equals(entity.getBizStatus())) { |
| | | throw new DataException("请先停用后再废弃"); |
| | | } |
| | | entity.setBizStatus(STATUS_INVALID); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | paperMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void setBizStatus(String id, String bizStatus) { |
| | | if (!STATUS_OPEN.equals(bizStatus) && !"closed".equals(bizStatus)) { |
| | | throw new DataException("状态无效,仅支持开放/不开放"); |
| | | } |
| | | TmsPaperEntity entity = requireOne(id); |
| | | if (STATUS_INVALID.equals(entity.getBizStatus())) { |
| | | throw new DataException("已废弃试卷不可启用或停用"); |
| | | } |
| | | entity.setBizStatus(bizStatus); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | paperMapper.updateById(entity); |
| | | } |
| | | |
| | | private void validateForm(TmsPaperForm form) { |
| | | if (form == null || StringUtil.isEmpty(form.getPaperName())) { |
| | | throw new DataException("请填写试卷名称"); |
| | | } |
| | | if (form.getDurationMin() != null && form.getDurationMin() < 0) { |
| | | throw new DataException("考试时长不能为负数"); |
| | | } |
| | | Date start = parseDate(form.getExamStart()); |
| | | Date end = parseDate(form.getExamEnd()); |
| | | Date publish = parseDate(form.getScorePublishTime()); |
| | | if ((start == null) != (end == null)) { |
| | | throw new DataException("开考时间与结束时间需同时填写或同时留空"); |
| | | } |
| | | if (start != null && end != null) { |
| | | if (!end.after(start)) { |
| | | throw new DataException("结束时间必须晚于开考时间"); |
| | | } |
| | | if (form.getDurationMin() != null && form.getDurationMin() > 0) { |
| | | long spanMin = (end.getTime() - start.getTime()) / 60_000L; |
| | | if (form.getDurationMin() > spanMin) { |
| | | throw new DataException("考试时长不能超过开考至结束的间隔(" + spanMin + " 分钟)"); |
| | | } |
| | | } |
| | | } |
| | | if (publish != null) { |
| | | if (end != null && publish.before(end)) { |
| | | throw new DataException("成绩公布时间不能早于结束时间"); |
| | | } |
| | | if (end == null && start != null && publish.before(start)) { |
| | | throw new DataException("成绩公布时间不能早于开考时间"); |
| | | } |
| | | } |
| | | BigDecimal totalFromForm = sumSectionScores(form); |
| | | if (form.getPassScore() != null && totalFromForm != null |
| | | && form.getPassScore().compareTo(totalFromForm) > 0) { |
| | | throw new DataException("合格分数不能大于试卷总分(当前总分 " + totalFromForm.stripTrailingZeros().toPlainString() + ")"); |
| | | } |
| | | if (form.getSections() != null) { |
| | | Set<String> usedQuestionIds = new HashSet<>(); |
| | | for (TmsPaperSectionForm sec : form.getSections()) { |
| | | if (sec.getQuestions() == null) { |
| | | continue; |
| | | } |
| | | for (TmsPaperQuestionForm q : sec.getQuestions()) { |
| | | if (StringUtil.isEmpty(q.getQuestionId())) { |
| | | throw new DataException("试卷试题缺少试题ID"); |
| | | } |
| | | if (q.getScore() == null || q.getScore().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new DataException("组卷试题分值必须大于 0"); |
| | | } |
| | | if (!usedQuestionIds.add(q.getQuestionId())) { |
| | | throw new DataException("同一试卷不能重复添加同一试题"); |
| | | } |
| | | TmsQuestionEntity question = questionMapper.selectById(q.getQuestionId()); |
| | | if (question == null) { |
| | | throw new DataException("试题不存在或已废弃,无法组卷"); |
| | | } |
| | | if (STATUS_INVALID.equals(question.getBizStatus())) { |
| | | throw new DataException("废弃试题不可用于组卷:" + question.getQuestionNo()); |
| | | } |
| | | if (StringUtil.isNotEmpty(sec.getQuestionType()) |
| | | && StringUtil.isNotEmpty(question.getQuestionType()) |
| | | && !sec.getQuestionType().equals(question.getQuestionType())) { |
| | | throw new DataException("试题「" + question.getQuestionNo() + "」与章节题型不一致"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private static BigDecimal sumSectionScores(TmsPaperForm form) { |
| | | if (form == null || form.getSections() == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (TmsPaperSectionForm sec : form.getSections()) { |
| | | if (sec.getQuestions() == null) { |
| | | continue; |
| | | } |
| | | for (TmsPaperQuestionForm q : sec.getQuestions()) { |
| | | if (q.getScore() != null) { |
| | | total = total.add(q.getScore()); |
| | | } |
| | | } |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | private void applyHeader(TmsPaperEntity entity, TmsPaperForm form) { |
| | | entity.setPaperName(form.getPaperName().trim()); |
| | | // 保存仅允许开放/不开放;废弃走 invalidate |
| | | String status = StringUtil.isNotEmpty(form.getBizStatus()) ? form.getBizStatus() : STATUS_OPEN; |
| | | if (!STATUS_OPEN.equals(status) && !"closed".equals(status)) { |
| | | status = STATUS_OPEN; |
| | | } |
| | | entity.setBizStatus(status); |
| | | entity.setDurationMin(form.getDurationMin() == null ? 60 : form.getDurationMin()); |
| | | entity.setExamStart(parseDate(form.getExamStart())); |
| | | entity.setExamEnd(parseDate(form.getExamEnd())); |
| | | entity.setScorePublishTime(parseDate(form.getScorePublishTime())); |
| | | entity.setPassScore(form.getPassScore()); |
| | | entity.setTotalScore(form.getTotalScore()); |
| | | entity.setSortMode(StringUtil.isNotEmpty(form.getSortMode()) ? form.getSortMode() : SORT_PAPER); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private void clearComposition(String paperId) { |
| | | LambdaQueryWrapper<TmsPaperQuestionEntity> qQw = new LambdaQueryWrapper<>(); |
| | | qQw.eq(TmsPaperQuestionEntity::getForeignId, paperId); |
| | | paperQuestionMapper.delete(qQw); |
| | | |
| | | LambdaQueryWrapper<TmsPaperSectionEntity> sQw = new LambdaQueryWrapper<>(); |
| | | sQw.eq(TmsPaperSectionEntity::getForeignId, paperId); |
| | | sectionMapper.delete(sQw); |
| | | } |
| | | |
| | | private void saveComposition(String paperId, List<TmsPaperSectionForm> sections, String userId, Date now) { |
| | | if (sections == null || sections.isEmpty()) { |
| | | return; |
| | | } |
| | | int secIdx = 0; |
| | | for (TmsPaperSectionForm secForm : sections) { |
| | | String sectionId = RandomUtil.uuId(); |
| | | TmsPaperSectionEntity sec = new TmsPaperSectionEntity(); |
| | | sec.setId(sectionId); |
| | | sec.setForeignId(paperId); |
| | | sec.setSectionName(StringUtil.isEmpty(secForm.getSectionName()) |
| | | ? defaultSectionName(secForm.getQuestionType(), ++secIdx) |
| | | : secForm.getSectionName().trim()); |
| | | sec.setQuestionType(secForm.getQuestionType()); |
| | | sec.setSortNo(secForm.getSortNo() != null ? secForm.getSortNo() : secIdx); |
| | | sec.setCreatorUserId(userId); |
| | | sec.setCreatorTime(now); |
| | | sectionMapper.insert(sec); |
| | | |
| | | if (secForm.getQuestions() == null || secForm.getQuestions().isEmpty()) { |
| | | continue; |
| | | } |
| | | int qIdx = 0; |
| | | for (TmsPaperQuestionForm qForm : secForm.getQuestions()) { |
| | | TmsPaperQuestionEntity pq = new TmsPaperQuestionEntity(); |
| | | pq.setId(RandomUtil.uuId()); |
| | | pq.setForeignId(paperId); |
| | | pq.setSectionId(sectionId); |
| | | pq.setQuestionId(qForm.getQuestionId()); |
| | | pq.setScore(qForm.getScore() == null ? BigDecimal.ZERO : qForm.getScore()); |
| | | pq.setSortNo(qForm.getSortNo() != null ? qForm.getSortNo() : ++qIdx); |
| | | pq.setCreatorUserId(userId); |
| | | pq.setCreatorTime(now); |
| | | paperQuestionMapper.insert(pq); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 按卷内试题重算总分、是否含主观题 */ |
| | | private void refreshComputed(String paperId) { |
| | | TmsPaperEntity entity = paperMapper.selectById(paperId); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | LambdaQueryWrapper<TmsPaperQuestionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPaperQuestionEntity::getForeignId, paperId); |
| | | List<TmsPaperQuestionEntity> rows = paperQuestionMapper.selectList(qw); |
| | | |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | boolean subjective = false; |
| | | for (TmsPaperQuestionEntity row : rows) { |
| | | if (row.getScore() != null) { |
| | | total = total.add(row.getScore()); |
| | | } |
| | | if (!subjective) { |
| | | TmsQuestionEntity q = questionMapper.selectById(row.getQuestionId()); |
| | | if (q != null && SUBJECTIVE_TYPES.contains(q.getQuestionType())) { |
| | | subjective = true; |
| | | } |
| | | } |
| | | } |
| | | entity.setTotalScore(total); |
| | | entity.setHasSubjective(subjective ? "1" : "0"); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | paperMapper.updateById(entity); |
| | | } |
| | | |
| | | private List<TmsPaperSectionForm> loadSections(String paperId) { |
| | | LambdaQueryWrapper<TmsPaperSectionEntity> sQw = new LambdaQueryWrapper<>(); |
| | | sQw.eq(TmsPaperSectionEntity::getForeignId, paperId); |
| | | sQw.orderByAsc(TmsPaperSectionEntity::getSortNo); |
| | | List<TmsPaperSectionEntity> sections = sectionMapper.selectList(sQw); |
| | | |
| | | LambdaQueryWrapper<TmsPaperQuestionEntity> qQw = new LambdaQueryWrapper<>(); |
| | | qQw.eq(TmsPaperQuestionEntity::getForeignId, paperId); |
| | | qQw.orderByAsc(TmsPaperQuestionEntity::getSortNo); |
| | | List<TmsPaperQuestionEntity> questions = paperQuestionMapper.selectList(qQw); |
| | | |
| | | Map<String, TmsQuestionEntity> questionMap = loadQuestions(questions); |
| | | Map<String, String> bankNames = loadBankNames(questionMap.values()); |
| | | |
| | | Map<String, List<TmsPaperQuestionForm>> bySection = new HashMap<>(); |
| | | for (TmsPaperQuestionEntity pq : questions) { |
| | | TmsPaperQuestionForm qf = new TmsPaperQuestionForm(); |
| | | qf.setId(pq.getId()); |
| | | qf.setSectionId(pq.getSectionId()); |
| | | qf.setQuestionId(pq.getQuestionId()); |
| | | qf.setScore(pq.getScore()); |
| | | qf.setSortNo(pq.getSortNo()); |
| | | TmsQuestionEntity q = questionMap.get(pq.getQuestionId()); |
| | | if (q != null) { |
| | | qf.setQuestionNo(q.getQuestionNo()); |
| | | qf.setStem(q.getStem()); |
| | | qf.setQuestionType(q.getQuestionType()); |
| | | qf.setBankName(bankNames.get(q.getBankId())); |
| | | } |
| | | bySection.computeIfAbsent(pq.getSectionId(), k -> new ArrayList<>()).add(qf); |
| | | } |
| | | |
| | | List<TmsPaperSectionForm> result = new ArrayList<>(sections.size()); |
| | | for (TmsPaperSectionEntity sec : sections) { |
| | | TmsPaperSectionForm sf = new TmsPaperSectionForm(); |
| | | sf.setId(sec.getId()); |
| | | sf.setSectionName(sec.getSectionName()); |
| | | sf.setQuestionType(sec.getQuestionType()); |
| | | sf.setSortNo(sec.getSortNo()); |
| | | sf.setQuestions(bySection.getOrDefault(sec.getId(), Collections.emptyList())); |
| | | result.add(sf); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Map<String, TmsQuestionEntity> loadQuestions(List<TmsPaperQuestionEntity> rows) { |
| | | Set<String> ids = new HashSet<>(); |
| | | for (TmsPaperQuestionEntity row : rows) { |
| | | if (StringUtil.isNotEmpty(row.getQuestionId())) { |
| | | ids.add(row.getQuestionId()); |
| | | } |
| | | } |
| | | if (ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<TmsQuestionEntity> list = questionMapper.selectBatchIds(ids); |
| | | Map<String, TmsQuestionEntity> map = new HashMap<>(list.size()); |
| | | for (TmsQuestionEntity q : list) { |
| | | map.put(q.getId(), q); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, String> loadBankNames(Iterable<TmsQuestionEntity> questions) { |
| | | Set<String> bankIds = new HashSet<>(); |
| | | for (TmsQuestionEntity q : questions) { |
| | | if (q != null && StringUtil.isNotEmpty(q.getBankId())) { |
| | | bankIds.add(q.getBankId()); |
| | | } |
| | | } |
| | | if (bankIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<TmsQuestionBankEntity> banks = bankMapper.selectBatchIds(bankIds); |
| | | Map<String, String> map = new HashMap<>(banks.size()); |
| | | for (TmsQuestionBankEntity bank : banks) { |
| | | map.put(bank.getId(), bank.getBankName()); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Integer> loadQuestionCounts(List<TmsPaperEntity> papers) { |
| | | if (papers == null || papers.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | Set<String> ids = new HashSet<>(); |
| | | for (TmsPaperEntity p : papers) { |
| | | ids.add(p.getId()); |
| | | } |
| | | LambdaQueryWrapper<TmsPaperQuestionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.in(TmsPaperQuestionEntity::getForeignId, ids); |
| | | List<TmsPaperQuestionEntity> rows = paperQuestionMapper.selectList(qw); |
| | | Map<String, Integer> map = new HashMap<>(); |
| | | for (TmsPaperQuestionEntity row : rows) { |
| | | map.merge(row.getForeignId(), 1, Integer::sum); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private TmsPaperEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("试卷不存在"); |
| | | } |
| | | TmsPaperEntity entity = paperMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("试卷不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private TmsPaperForm toHeaderForm(TmsPaperEntity entity) { |
| | | TmsPaperForm form = new TmsPaperForm(); |
| | | form.setId(entity.getId()); |
| | | form.setPaperNo(entity.getPaperNo()); |
| | | form.setPaperName(entity.getPaperName()); |
| | | form.setBizStatus(entity.getBizStatus()); |
| | | form.setDurationMin(entity.getDurationMin()); |
| | | form.setExamStart(formatDate(entity.getExamStart())); |
| | | form.setExamEnd(formatDate(entity.getExamEnd())); |
| | | form.setScorePublishTime(formatDate(entity.getScorePublishTime())); |
| | | form.setPassScore(entity.getPassScore()); |
| | | form.setTotalScore(entity.getTotalScore()); |
| | | form.setSortMode(entity.getSortMode()); |
| | | form.setHasSubjective(entity.getHasSubjective()); |
| | | form.setRemark(entity.getRemark()); |
| | | form.setCreatorTime(formatDate(entity.getCreatorTime())); |
| | | return form; |
| | | } |
| | | |
| | | /** |
| | | * 试卷编号:年份后两位 + 4 位流水,如 260001。 |
| | | * 按当年最大号 +1,含已删记录,避免流水回退。 |
| | | */ |
| | | private synchronized String nextPaperNo() { |
| | | String prefix = String.format("%02d", LocalDate.now().getYear() % 100); |
| | | String maxNo = paperMapper.selectMaxPaperNo(prefix); |
| | | int next = 1; |
| | | if (StringUtil.isNotEmpty(maxNo) && maxNo.length() >= 6 && maxNo.startsWith(prefix)) { |
| | | try { |
| | | next = Integer.parseInt(maxNo.substring(prefix.length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | next = 1; |
| | | } |
| | | } |
| | | if (next > 9999) { |
| | | throw new DataException("当年试卷编号已用尽(" + prefix + "9999)"); |
| | | } |
| | | return prefix + String.format("%04d", next); |
| | | } |
| | | |
| | | private static String defaultSectionName(String questionType, int idx) { |
| | | if (StringUtil.isEmpty(questionType)) { |
| | | return "第" + idx + "章"; |
| | | } |
| | | switch (questionType) { |
| | | case "single": |
| | | return "单选题"; |
| | | case "multi": |
| | | return "多选题"; |
| | | case "judge": |
| | | return "判断题"; |
| | | case "blank": |
| | | return "填空题"; |
| | | case "essay": |
| | | return "问答题"; |
| | | default: |
| | | return "第" + idx + "章"; |
| | | } |
| | | } |
| | | |
| | | private static Date parseDate(String text) { |
| | | if (StringUtil.isEmpty(text)) { |
| | | return null; |
| | | } |
| | | String raw = text.trim(); |
| | | try { |
| | | if (raw.matches("^\\d{10,13}$")) { |
| | | long ts = Long.parseLong(raw); |
| | | if (raw.length() == 10) { |
| | | ts = ts * 1000L; |
| | | } |
| | | return new Date(ts); |
| | | } |
| | | return DateUtil.stringToDate(raw); |
| | | } catch (Exception ex) { |
| | | throw new DataException("时间格式无效:" + text); |
| | | } |
| | | } |
| | | |
| | | private static String formatDate(Date date) { |
| | | if (date == null) { |
| | | return null; |
| | | } |
| | | return DateUtil.dateToString(date, "yyyy-MM-dd HH:mm:ss"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsMaterialEntity; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import jnpf.tmsEntity.TmsTaskFileEntity; |
| | | import jnpf.tmsEntity.TmsTrainModeEntity; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskFileForm; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskLearnResult; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskMineForm; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskQuery; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskSignResult; |
| | | import jnpf.tmsMapper.TmsMaterialMapper; |
| | | import jnpf.tmsMapper.TmsPersonTaskMapper; |
| | | import jnpf.tmsMapper.TmsTaskFileMapper; |
| | | import jnpf.tmsMapper.TmsTaskMapper; |
| | | import jnpf.tmsMapper.TmsTrainModeMapper; |
| | | import jnpf.tmsService.TmsPersonTaskService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsPersonTaskServiceImpl implements TmsPersonTaskService { |
| | | |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final String STATUS_CANCELLED = "cancelled"; |
| | | private static final String STATUS_DONE = "done"; |
| | | private static final String STATUS_SIGNED = "signed"; |
| | | private static final String STATUS_TODO = "todo"; |
| | | private static final String SIGN_MODE_ONLINE = "online"; |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | |
| | | private final TmsPersonTaskMapper personTaskMapper; |
| | | private final TmsTaskMapper taskMapper; |
| | | private final TmsTaskFileMapper taskFileMapper; |
| | | private final TmsMaterialMapper materialMapper; |
| | | private final TmsTrainModeMapper trainModeMapper; |
| | | |
| | | @Override |
| | | public List<TmsPersonTaskMineForm> getMine(TmsPersonTaskQuery query) { |
| | | String userId = UserProvider.getLoginUserId(); |
| | | LambdaQueryWrapper<TmsPersonTaskEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPersonTaskEntity::getUserId, userId); |
| | | qw.ne(TmsPersonTaskEntity::getBizStatus, STATUS_CANCELLED); |
| | | if (StringUtil.isNotEmpty(query.getBizStatus())) { |
| | | qw.eq(TmsPersonTaskEntity::getBizStatus, query.getBizStatus().trim()); |
| | | } |
| | | qw.orderByDesc(TmsPersonTaskEntity::getCreatorTime); |
| | | List<TmsPersonTaskEntity> persons = personTaskMapper.selectList(qw); |
| | | |
| | | Map<String, TmsTaskEntity> taskMap = loadTasks(persons); |
| | | String keyword = StringUtil.isNotEmpty(query.getKeyword()) ? query.getKeyword().trim() : null; |
| | | List<TmsPersonTaskMineForm> all = new ArrayList<>(); |
| | | for (TmsPersonTaskEntity person : persons) { |
| | | TmsTaskEntity task = taskMap.get(person.getTaskId()); |
| | | if (task == null || !"published".equals(task.getPublishStatus())) { |
| | | continue; |
| | | } |
| | | if (keyword != null && !matchKeyword(person, task, keyword)) { |
| | | continue; |
| | | } |
| | | all.add(toForm(person, task, false)); |
| | | } |
| | | all.sort(Comparator.comparing(TmsPersonTaskMineForm::getStartTime, |
| | | Comparator.nullsLast(Comparator.reverseOrder()))); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | int from = (int) Math.min((current - 1) * size, all.size()); |
| | | int to = (int) Math.min(from + size, all.size()); |
| | | List<TmsPersonTaskMineForm> page = all.subList(from, to); |
| | | query.setData(page, all.size()); |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public TmsPersonTaskMineForm getInfo(String id) { |
| | | TmsPersonTaskEntity person = requireMine(id); |
| | | TmsTaskEntity task = taskMapper.selectById(person.getTaskId()); |
| | | if (task == null) { |
| | | throw new DataException("培训任务不存在"); |
| | | } |
| | | TmsPersonTaskMineForm form = toForm(person, task, true); |
| | | form.setFiles(loadFiles(task.getId())); |
| | | return form; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsPersonTaskLearnResult markLearned(String id, Integer seconds) { |
| | | TmsPersonTaskEntity person = requireMine(id); |
| | | TmsTaskEntity task = taskMapper.selectById(person.getTaskId()); |
| | | if (task == null) { |
| | | throw new DataException("培训任务不存在"); |
| | | } |
| | | |
| | | int before = person.getLearnSeconds() == null ? 0 : person.getLearnSeconds(); |
| | | int required = toRequiredSeconds(person.getHours()); |
| | | boolean alreadyFull = required > 0 && before >= required; |
| | | |
| | | int add = seconds == null ? 0 : Math.max(0, seconds); |
| | | if (add > 0) { |
| | | person.setLearnSeconds(before + add); |
| | | person.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | person.setLastModifyTime(new Date()); |
| | | personTaskMapper.updateById(person); |
| | | } |
| | | |
| | | int learned = person.getLearnSeconds() == null ? 0 : person.getLearnSeconds(); |
| | | boolean justFull = required > 0 && !alreadyFull && learned >= required; |
| | | |
| | | TmsPersonTaskLearnResult result = new TmsPersonTaskLearnResult(); |
| | | result.setLearnedSeconds(learned); |
| | | result.setRequiredSeconds(required); |
| | | result.setLearnStatus(resolveLearnStatus(learned, required)); |
| | | result.setBizStatus(person.getBizStatus()); |
| | | result.setAutoCompleted(false); |
| | | result.setMessage(""); |
| | | |
| | | boolean needExam = "exam".equals(task.getEvalMode()) && StringUtil.isNotEmpty(task.getPaperId()); |
| | | if (justFull) { |
| | | if (!needExam && !STATUS_DONE.equals(person.getBizStatus()) |
| | | && !STATUS_CANCELLED.equals(person.getBizStatus())) { |
| | | person.setBizStatus(STATUS_DONE); |
| | | person.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | person.setLastModifyTime(new Date()); |
| | | personTaskMapper.updateById(person); |
| | | result.setAutoCompleted(true); |
| | | result.setBizStatus(STATUS_DONE); |
| | | result.setMessage("已学满规定课时,系统已自动确认完成"); |
| | | } else if (needExam) { |
| | | if (person.getSignTime() == null) { |
| | | result.setMessage("已学满规定课时,请先签到后再进入考试"); |
| | | } else { |
| | | result.setMessage("已学满规定课时,可以进入考试"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | TmsPersonTaskMineForm tmp = new TmsPersonTaskMineForm(); |
| | | fillExamFlag(tmp, task, person); |
| | | result.setCanExam(Boolean.TRUE.equals(tmp.getCanExam())); |
| | | result.setExamTip(tmp.getExamTip()); |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsPersonTaskSignResult sign(String id) { |
| | | TmsPersonTaskEntity person = requireMine(id); |
| | | TmsTaskEntity task = taskMapper.selectById(person.getTaskId()); |
| | | if (task == null) { |
| | | throw new DataException("培训任务不存在"); |
| | | } |
| | | if (!"published".equals(task.getPublishStatus())) { |
| | | throw new DataException("任务未发布,不可签到"); |
| | | } |
| | | if (STATUS_CANCELLED.equals(person.getBizStatus())) { |
| | | throw new DataException("任务已取消,不可签到"); |
| | | } |
| | | if (person.getSignTime() != null) { |
| | | throw new DataException("您已签到,无需重复签到"); |
| | | } |
| | | String deny = resolveSignDenyReason(task, person); |
| | | if (StringUtil.isNotEmpty(deny)) { |
| | | throw new DataException(deny); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | person.setSignTime(now); |
| | | person.setSignMode(SIGN_MODE_ONLINE); |
| | | if (STATUS_TODO.equals(person.getBizStatus()) || StringUtil.isEmpty(person.getBizStatus())) { |
| | | person.setBizStatus(STATUS_SIGNED); |
| | | } |
| | | person.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | person.setLastModifyTime(now); |
| | | personTaskMapper.updateById(person); |
| | | |
| | | TmsPersonTaskSignResult result = new TmsPersonTaskSignResult(); |
| | | result.setSignTime(formatDate(now)); |
| | | result.setBizStatus(person.getBizStatus()); |
| | | result.setSigned(true); |
| | | result.setCanSign(false); |
| | | result.setSignTip("已签到"); |
| | | result.setMessage("签到成功"); |
| | | TmsPersonTaskMineForm tmp = new TmsPersonTaskMineForm(); |
| | | fillExamFlag(tmp, task, person); |
| | | result.setCanExam(Boolean.TRUE.equals(tmp.getCanExam())); |
| | | result.setExamTip(tmp.getExamTip()); |
| | | return result; |
| | | } |
| | | |
| | | private TmsPersonTaskMineForm toForm(TmsPersonTaskEntity person, TmsTaskEntity task, boolean detail) { |
| | | TmsPersonTaskMineForm form = new TmsPersonTaskMineForm(); |
| | | form.setId(person.getId()); |
| | | form.setTaskId(task.getId()); |
| | | form.setTaskNo(StringUtil.isNotEmpty(person.getTaskNo()) ? person.getTaskNo() : task.getTaskNo()); |
| | | form.setSubject(task.getSubject()); |
| | | form.setCategory(task.getCategory()); |
| | | form.setTrainMode(task.getTrainMode()); |
| | | form.setEvalMode(task.getEvalMode()); |
| | | form.setStartTime(formatDate(task.getStartTime())); |
| | | form.setEndTime(formatDate(task.getEndTime())); |
| | | form.setCloseTime(formatDate(task.getCloseTime())); |
| | | form.setPlaceName(task.getPlaceName()); |
| | | form.setBizStatus(person.getBizStatus()); |
| | | form.setSignTime(formatDate(person.getSignTime())); |
| | | form.setSigned(person.getSignTime() != null); |
| | | fillSignFlag(form, task, person); |
| | | form.setLearnSeconds(person.getLearnSeconds()); |
| | | int learned = person.getLearnSeconds() == null ? 0 : person.getLearnSeconds(); |
| | | form.setLearnedSeconds(learned); |
| | | BigDecimal hours = person.getHours(); |
| | | form.setRequiredHours(hours); |
| | | int requiredSeconds = toRequiredSeconds(hours); |
| | | form.setRequiredSeconds(requiredSeconds); |
| | | form.setLearnStatus(resolveLearnStatus(learned, requiredSeconds)); |
| | | form.setExamScore(person.getExamScore()); |
| | | form.setPassFlag(person.getPassFlag()); |
| | | form.setPaperId(task.getPaperId()); |
| | | form.setPaperName(task.getPaperName()); |
| | | fillExamFlag(form, task, person); |
| | | if (detail) { |
| | | form.setKeyPoints(task.getKeyPoints()); |
| | | } |
| | | return form; |
| | | } |
| | | |
| | | private void fillExamFlag(TmsPersonTaskMineForm form, TmsTaskEntity task, TmsPersonTaskEntity person) { |
| | | if (!"exam".equals(task.getEvalMode())) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("本次培训无需在线考试"); |
| | | return; |
| | | } |
| | | if (StringUtil.isEmpty(task.getPaperId())) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("未配置试卷"); |
| | | return; |
| | | } |
| | | if (person.getSignTime() == null) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("请先签到后再考试"); |
| | | return; |
| | | } |
| | | if ("online".equals(task.getTrainMode())) { |
| | | int required = toRequiredSeconds(person.getHours()); |
| | | int learned = person.getLearnSeconds() == null ? 0 : person.getLearnSeconds(); |
| | | if (required <= 0) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("未配置课时,暂不能考试"); |
| | | return; |
| | | } |
| | | if (learned < required) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("请先学满规定课时后再考试(已学 " |
| | | + formatDuration(learned) + " / 需学 " + formatDuration(required) + ")"); |
| | | return; |
| | | } |
| | | } |
| | | Date now = new Date(); |
| | | if (task.getExamStart() != null && now.before(task.getExamStart())) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("未到考试开始时间"); |
| | | return; |
| | | } |
| | | if (task.getExamEnd() != null && now.after(task.getExamEnd())) { |
| | | form.setCanExam(false); |
| | | form.setExamTip("已过考试结束时间"); |
| | | return; |
| | | } |
| | | form.setCanExam(true); |
| | | form.setExamTip(""); |
| | | } |
| | | |
| | | private void fillSignFlag(TmsPersonTaskMineForm form, TmsTaskEntity task, TmsPersonTaskEntity person) { |
| | | if (person.getSignTime() != null) { |
| | | form.setCanSign(false); |
| | | form.setSignTip("已签到"); |
| | | return; |
| | | } |
| | | String deny = resolveSignDenyReason(task, person); |
| | | if (StringUtil.isNotEmpty(deny)) { |
| | | form.setCanSign(false); |
| | | form.setSignTip(deny); |
| | | return; |
| | | } |
| | | form.setCanSign(true); |
| | | form.setSignTip(""); |
| | | } |
| | | |
| | | /** 返回不可签到原因;空字符串表示可签到 */ |
| | | private String resolveSignDenyReason(TmsTaskEntity task, TmsPersonTaskEntity person) { |
| | | if (STATUS_CANCELLED.equals(person.getBizStatus())) { |
| | | return "任务已取消,不可签到"; |
| | | } |
| | | if (task == null || !"published".equals(task.getPublishStatus())) { |
| | | return "任务未发布,不可签到"; |
| | | } |
| | | if (!allowOnlineSign(task.getTrainMode())) { |
| | | return "该培训方式不支持在线签到,请扫码签到"; |
| | | } |
| | | Date now = new Date(); |
| | | if (task.getStartTime() != null && now.before(task.getStartTime())) { |
| | | return "未到培训开始时间,暂不可签到"; |
| | | } |
| | | if (task.getCloseTime() != null && now.after(task.getCloseTime())) { |
| | | return "培训已关闭,不可签到"; |
| | | } |
| | | if (task.getEndTime() != null && now.after(task.getEndTime()) && task.getCloseTime() == null) { |
| | | return "已过培训结束时间,不可签到"; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | private boolean allowOnlineSign(String trainMode) { |
| | | if (StringUtil.isEmpty(trainMode)) { |
| | | return false; |
| | | } |
| | | // 配置表优先:sign_rule=both 才允许在线;无配置时按字典约定 online 允许 |
| | | TmsTrainModeEntity mode = trainModeMapper.selectOne( |
| | | new LambdaQueryWrapper<TmsTrainModeEntity>() |
| | | .eq(TmsTrainModeEntity::getModeCode, trainMode) |
| | | .last("LIMIT 1")); |
| | | if (mode != null && StringUtil.isNotEmpty(mode.getSignRule())) { |
| | | return "both".equalsIgnoreCase(mode.getSignRule()); |
| | | } |
| | | return "online".equalsIgnoreCase(trainMode); |
| | | } |
| | | |
| | | private int toRequiredSeconds(BigDecimal hours) { |
| | | if (hours == null || hours.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return 0; |
| | | } |
| | | return hours.multiply(BigDecimal.valueOf(3600)).intValue(); |
| | | } |
| | | |
| | | private String resolveLearnStatus(int learned, int required) { |
| | | if (required > 0 && learned >= required) { |
| | | return "已学满"; |
| | | } |
| | | if (learned > 0) { |
| | | return "学习中"; |
| | | } |
| | | return "未学习"; |
| | | } |
| | | |
| | | private String formatDuration(int seconds) { |
| | | int h = seconds / 3600; |
| | | int m = (seconds % 3600) / 60; |
| | | int s = seconds % 60; |
| | | if (h > 0) { |
| | | return h + "小时" + m + "分"; |
| | | } |
| | | if (m > 0) { |
| | | return m + "分" + s + "秒"; |
| | | } |
| | | return s + "秒"; |
| | | } |
| | | |
| | | private List<TmsPersonTaskFileForm> loadFiles(String taskId) { |
| | | List<TmsTaskFileEntity> files = taskFileMapper.selectList( |
| | | new LambdaQueryWrapper<TmsTaskFileEntity>() |
| | | .eq(TmsTaskFileEntity::getForeignId, taskId) |
| | | .orderByAsc(TmsTaskFileEntity::getCreatorTime)); |
| | | if (files.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<String> materialIds = files.stream() |
| | | .map(TmsTaskFileEntity::getMaterialId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | Map<String, TmsMaterialEntity> materialMap = new HashMap<>(); |
| | | if (!materialIds.isEmpty()) { |
| | | for (TmsMaterialEntity material : materialMapper.selectBatchIds(materialIds)) { |
| | | materialMap.put(material.getId(), material); |
| | | } |
| | | } |
| | | List<TmsPersonTaskFileForm> list = new ArrayList<>(); |
| | | for (TmsTaskFileEntity file : files) { |
| | | String url = extractUrl(file.getFileJson()); |
| | | String ext = extractExt(file.getFileJson(), file.getFileName()); |
| | | if ((StringUtil.isEmpty(url) || StringUtil.isEmpty(ext)) && StringUtil.isNotEmpty(file.getMaterialId())) { |
| | | TmsMaterialEntity material = materialMap.get(file.getMaterialId()); |
| | | if (material != null) { |
| | | if (StringUtil.isEmpty(url)) { |
| | | url = extractUrl(material.getFileJson()); |
| | | } |
| | | if (StringUtil.isEmpty(ext)) { |
| | | ext = extractExt(material.getFileJson(), material.getMaterialName()); |
| | | } |
| | | } |
| | | } |
| | | TmsPersonTaskFileForm item = new TmsPersonTaskFileForm(); |
| | | item.setId(file.getId()); |
| | | item.setName(file.getFileName()); |
| | | item.setUrl(url); |
| | | item.setFileExt(ext); |
| | | item.setPreviewType(resolvePreviewType(ext)); |
| | | list.add(item); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private String extractExt(String fileJson, String fileName) { |
| | | try { |
| | | if (StringUtil.isNotEmpty(fileJson)) { |
| | | String text = fileJson.trim(); |
| | | if (text.startsWith("[")) { |
| | | List<Map<String, Object>> arr = OBJECT_MAPPER.readValue(text, new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | if (arr != null && !arr.isEmpty() && arr.get(0).get("fileExtension") != null) { |
| | | return String.valueOf(arr.get(0).get("fileExtension")).toLowerCase(); |
| | | } |
| | | if (arr != null && !arr.isEmpty() && arr.get(0).get("name") != null) { |
| | | fileName = String.valueOf(arr.get(0).get("name")); |
| | | } |
| | | } else { |
| | | Map<String, Object> map = OBJECT_MAPPER.readValue(text, new TypeReference<Map<String, Object>>() { |
| | | }); |
| | | if (map.get("fileExtension") != null) { |
| | | return String.valueOf(map.get("fileExtension")).toLowerCase(); |
| | | } |
| | | if (map.get("name") != null) { |
| | | fileName = String.valueOf(map.get("name")); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignored) { |
| | | // ignore |
| | | } |
| | | if (StringUtil.isEmpty(fileName) || !fileName.contains(".")) { |
| | | return ""; |
| | | } |
| | | return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); |
| | | } |
| | | |
| | | private String resolvePreviewType(String ext) { |
| | | if (StringUtil.isEmpty(ext)) { |
| | | return "other"; |
| | | } |
| | | String e = ext.toLowerCase(); |
| | | if (Set.of("jpg", "jpeg", "png", "gif", "webp", "bmp", "svg").contains(e)) { |
| | | return "image"; |
| | | } |
| | | if (Set.of("mp4", "webm", "ogg", "mov", "m4v").contains(e)) { |
| | | return "video"; |
| | | } |
| | | if (Set.of("mp3", "wav", "aac", "m4a", "flac").contains(e)) { |
| | | return "audio"; |
| | | } |
| | | if ("pdf".equals(e)) { |
| | | return "pdf"; |
| | | } |
| | | if (Set.of("doc", "docx", "xls", "xlsx", "ppt", "pptx").contains(e)) { |
| | | return "office"; |
| | | } |
| | | return "other"; |
| | | } |
| | | |
| | | private String extractUrl(String fileJson) { |
| | | if (StringUtil.isEmpty(fileJson)) { |
| | | return null; |
| | | } |
| | | try { |
| | | String text = fileJson.trim(); |
| | | if (text.startsWith("[")) { |
| | | List<Map<String, Object>> arr = OBJECT_MAPPER.readValue(text, new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | if (arr == null || arr.isEmpty() || arr.get(0).get("url") == null) { |
| | | return null; |
| | | } |
| | | return String.valueOf(arr.get(0).get("url")); |
| | | } |
| | | Map<String, Object> map = OBJECT_MAPPER.readValue(text, new TypeReference<Map<String, Object>>() { |
| | | }); |
| | | return map.get("url") == null ? null : String.valueOf(map.get("url")); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private Map<String, TmsTaskEntity> loadTasks(List<TmsPersonTaskEntity> persons) { |
| | | List<String> ids = persons.stream() |
| | | .map(TmsPersonTaskEntity::getTaskId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | Map<String, TmsTaskEntity> map = new HashMap<>(); |
| | | for (TmsTaskEntity task : taskMapper.selectBatchIds(ids)) { |
| | | map.put(task.getId(), task); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private boolean matchKeyword(TmsPersonTaskEntity person, TmsTaskEntity task, String keyword) { |
| | | String no = person.getTaskNo() == null ? "" : person.getTaskNo(); |
| | | String taskNo = task.getTaskNo() == null ? "" : task.getTaskNo(); |
| | | String subject = task.getSubject() == null ? "" : task.getSubject(); |
| | | return no.contains(keyword) || taskNo.contains(keyword) || subject.contains(keyword); |
| | | } |
| | | |
| | | private TmsPersonTaskEntity requireMine(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("个人任务ID不能为空"); |
| | | } |
| | | TmsPersonTaskEntity person = personTaskMapper.selectById(id); |
| | | if (person == null) { |
| | | throw new DataException("个人培训任务不存在"); |
| | | } |
| | | if (!UserProvider.getLoginUserId().equals(person.getUserId())) { |
| | | throw new DataException("只能查看自己的培训任务"); |
| | | } |
| | | return person; |
| | | } |
| | | |
| | | private String formatDate(Date date) { |
| | | return date == null ? null : DateUtil.dateToString(date, FMT); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import jnpf.tmsEntity.TmsPlaceEntity; |
| | | import jnpf.tmsEntity.place.TmsPlaceOption; |
| | | import jnpf.tmsMapper.TmsPlaceMapper; |
| | | import jnpf.tmsService.TmsPlaceService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsPlaceServiceImpl implements TmsPlaceService { |
| | | |
| | | private final TmsPlaceMapper placeMapper; |
| | | |
| | | @Override |
| | | public List<TmsPlaceOption> listOptions() { |
| | | LambdaQueryWrapper<TmsPlaceEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPlaceEntity::getEnabled, "1"); |
| | | qw.orderByAsc(TmsPlaceEntity::getPlaceNo); |
| | | List<TmsPlaceEntity> rows = placeMapper.selectList(qw); |
| | | List<TmsPlaceOption> list = new ArrayList<>(rows.size()); |
| | | for (TmsPlaceEntity row : rows) { |
| | | TmsPlaceOption opt = new TmsPlaceOption(); |
| | | opt.setId(row.getId()); |
| | | opt.setPlaceNo(row.getPlaceNo()); |
| | | opt.setFullName(row.getPlaceName()); |
| | | opt.setPlaceGroup(row.getPlaceGroup()); |
| | | list.add(opt); |
| | | } |
| | | return list; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.UserApi; |
| | | import jnpf.permission.entity.UserEntity; |
| | | import jnpf.tmsEntity.TmsQuestionBankEntity; |
| | | import jnpf.tmsEntity.TmsQuestionEntity; |
| | | import jnpf.tmsEntity.TmsQuestionOptionEntity; |
| | | import jnpf.tmsEntity.question.TmsQuestionBankOption; |
| | | import jnpf.tmsEntity.question.TmsQuestionForm; |
| | | import jnpf.tmsEntity.question.TmsQuestionOptionForm; |
| | | import jnpf.tmsEntity.question.TmsQuestionQuery; |
| | | import jnpf.tmsMapper.TmsQuestionBankMapper; |
| | | import jnpf.tmsMapper.TmsQuestionMapper; |
| | | import jnpf.tmsMapper.TmsQuestionOptionMapper; |
| | | import jnpf.tmsService.TmsQuestionService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsQuestionServiceImpl implements TmsQuestionService { |
| | | |
| | | private final TmsQuestionMapper questionMapper; |
| | | private final TmsQuestionOptionMapper optionMapper; |
| | | private final TmsQuestionBankMapper bankMapper; |
| | | private final UserApi userApi; |
| | | |
| | | @Override |
| | | public List<TmsQuestionBankOption> listBanks() { |
| | | LambdaQueryWrapper<TmsQuestionBankEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.orderByDesc(TmsQuestionBankEntity::getCreatorTime); |
| | | List<TmsQuestionBankEntity> list = bankMapper.selectList(qw); |
| | | List<TmsQuestionBankOption> result = new ArrayList<>(list.size()); |
| | | for (TmsQuestionBankEntity bank : list) { |
| | | TmsQuestionBankOption opt = new TmsQuestionBankOption(); |
| | | opt.setId(bank.getId()); |
| | | opt.setFullName(bank.getBankName()); |
| | | result.add(opt); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, String>> listAdmins() { |
| | | QueryWrapper<TmsQuestionEntity> qw = new QueryWrapper<>(); |
| | | qw.select("DISTINCT admin_user_id"); |
| | | qw.isNotNull("admin_user_id"); |
| | | qw.ne("admin_user_id", ""); |
| | | List<TmsQuestionEntity> rows = questionMapper.selectList(qw); |
| | | List<String> userIds = rows.stream() |
| | | .map(TmsQuestionEntity::getAdminUserId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (userIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<String, String> nameMap = loadUserNames(userIds); |
| | | List<Map<String, String>> result = new ArrayList<>(); |
| | | for (String userId : userIds) { |
| | | Map<String, String> item = new HashMap<>(2); |
| | | item.put("id", userId); |
| | | item.put("fullName", nameMap.getOrDefault(userId, userId)); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public List<TmsQuestionForm> getList(TmsQuestionQuery query) { |
| | | LambdaQueryWrapper<TmsQuestionEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getBankId())) { |
| | | qw.eq(TmsQuestionEntity::getBankId, query.getBankId()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getQuestionType())) { |
| | | qw.eq(TmsQuestionEntity::getQuestionType, query.getQuestionType()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getBizStatus())) { |
| | | qw.eq(TmsQuestionEntity::getBizStatus, query.getBizStatus()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getAdminUserId())) { |
| | | qw.eq(TmsQuestionEntity::getAdminUserId, query.getAdminUserId()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsQuestionEntity::getStem, kw) |
| | | .or() |
| | | .like(TmsQuestionEntity::getQuestionNo, kw)); |
| | | } |
| | | qw.orderByDesc(TmsQuestionEntity::getCreatorTime); |
| | | |
| | | Page<TmsQuestionEntity> page = new Page<>(query.getCurrentPage(), query.getPageSize()); |
| | | Page<TmsQuestionEntity> result = questionMapper.selectPage(page, qw); |
| | | List<TmsQuestionEntity> records = result.getRecords(); |
| | | |
| | | Map<String, String> bankNames = loadBankNames( |
| | | records.stream().map(TmsQuestionEntity::getBankId).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList())); |
| | | Map<String, String> userNames = loadUserNames( |
| | | records.stream().map(TmsQuestionEntity::getAdminUserId).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList())); |
| | | |
| | | List<TmsQuestionForm> list = new ArrayList<>(records.size()); |
| | | for (TmsQuestionEntity entity : records) { |
| | | list.add(toForm(entity, bankNames, userNames, false)); |
| | | } |
| | | query.setData(list, result.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsQuestionForm getInfo(String id) { |
| | | TmsQuestionEntity entity = questionMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("试题不存在"); |
| | | } |
| | | Map<String, String> bankNames = loadBankNames(Collections.singletonList(entity.getBankId())); |
| | | Map<String, String> userNames = loadUserNames( |
| | | StringUtil.isEmpty(entity.getAdminUserId()) |
| | | ? Collections.emptyList() |
| | | : Collections.singletonList(entity.getAdminUserId())); |
| | | return toForm(entity, bankNames, userNames, true); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsQuestionForm form) { |
| | | validateForm(form); |
| | | String id = RandomUtil.uuId(); |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | |
| | | TmsQuestionEntity entity = new TmsQuestionEntity(); |
| | | entity.setId(id); |
| | | entity.setQuestionNo(nextQuestionNo()); |
| | | entity.setBankId(form.getBankId()); |
| | | entity.setQuestionType(form.getQuestionType()); |
| | | entity.setDifficulty(form.getDifficulty()); |
| | | entity.setSourceType(form.getSourceType()); |
| | | entity.setStem(form.getStem()); |
| | | entity.setAnalysis(form.getAnalysis()); |
| | | entity.setAdminUserId(StringUtil.isNotEmpty(form.getAdminUserId()) ? form.getAdminUserId() : userId); |
| | | entity.setBizStatus(StringUtil.isNotEmpty(form.getBizStatus()) ? form.getBizStatus() : "closed"); |
| | | entity.setCreatorUserId(userId); |
| | | entity.setCreatorTime(now); |
| | | questionMapper.insert(entity); |
| | | |
| | | saveOptions(id, form.getOptions()); |
| | | refreshBankCount(form.getBankId()); |
| | | return id; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsQuestionForm form) { |
| | | TmsQuestionEntity entity = questionMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("试题不存在"); |
| | | } |
| | | validateForm(form); |
| | | |
| | | String oldBankId = entity.getBankId(); |
| | | entity.setBankId(form.getBankId()); |
| | | entity.setQuestionType(form.getQuestionType()); |
| | | entity.setDifficulty(form.getDifficulty()); |
| | | entity.setSourceType(form.getSourceType()); |
| | | entity.setStem(form.getStem()); |
| | | entity.setAnalysis(form.getAnalysis()); |
| | | if (StringUtil.isNotEmpty(form.getAdminUserId())) { |
| | | entity.setAdminUserId(form.getAdminUserId()); |
| | | } |
| | | if (StringUtil.isNotEmpty(form.getBizStatus())) { |
| | | entity.setBizStatus(form.getBizStatus()); |
| | | } |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | questionMapper.updateById(entity); |
| | | |
| | | // 选项全量替换 |
| | | LambdaQueryWrapper<TmsQuestionOptionEntity> delQw = new LambdaQueryWrapper<>(); |
| | | delQw.eq(TmsQuestionOptionEntity::getForeignId, id); |
| | | optionMapper.delete(delQw); |
| | | saveOptions(id, form.getOptions()); |
| | | |
| | | refreshBankCount(oldBankId); |
| | | if (!Objects.equals(oldBankId, form.getBankId())) { |
| | | refreshBankCount(form.getBankId()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void invalidate(String id) { |
| | | TmsQuestionEntity entity = questionMapper.selectById(id); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | String bankId = entity.getBankId(); |
| | | // TableLogic:写 f_delete_mark,列表/详情自动过滤 |
| | | questionMapper.deleteById(id); |
| | | |
| | | LambdaQueryWrapper<TmsQuestionOptionEntity> delQw = new LambdaQueryWrapper<>(); |
| | | delQw.eq(TmsQuestionOptionEntity::getForeignId, id); |
| | | optionMapper.delete(delQw); |
| | | |
| | | refreshBankCount(bankId); |
| | | } |
| | | |
| | | private void validateForm(TmsQuestionForm form) { |
| | | if (form == null) { |
| | | throw new DataException("参数不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getBankId())) { |
| | | throw new DataException("请选择所属题库"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getQuestionType())) { |
| | | throw new DataException("请选择题型"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getStem())) { |
| | | throw new DataException("请填写题干"); |
| | | } |
| | | TmsQuestionBankEntity bank = bankMapper.selectById(form.getBankId()); |
| | | if (bank == null) { |
| | | throw new DataException("题库不存在或已删除"); |
| | | } |
| | | } |
| | | |
| | | private void saveOptions(String questionId, List<TmsQuestionOptionForm> options) { |
| | | if (options == null || options.isEmpty()) { |
| | | return; |
| | | } |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | int i = 0; |
| | | for (TmsQuestionOptionForm opt : options) { |
| | | TmsQuestionOptionEntity row = new TmsQuestionOptionEntity(); |
| | | row.setId(RandomUtil.uuId()); |
| | | row.setForeignId(questionId); |
| | | row.setSortNo(opt.getSortNo() != null ? opt.getSortNo() : ++i); |
| | | row.setOptionLabel(opt.getOptionLabel()); |
| | | row.setOptionContent(opt.getOptionContent()); |
| | | row.setIsCorrect(StringUtil.isNotEmpty(opt.getIsCorrect()) ? opt.getIsCorrect() : "0"); |
| | | row.setCreatorUserId(userId); |
| | | row.setCreatorTime(now); |
| | | optionMapper.insert(row); |
| | | } |
| | | } |
| | | |
| | | /** 按题库下未删除试题实计回写 question_count(创建 +1 / 删除 -1 / 换库两边重算) */ |
| | | private void refreshBankCount(String bankId) { |
| | | if (StringUtil.isEmpty(bankId)) { |
| | | return; |
| | | } |
| | | TmsQuestionBankEntity bank = bankMapper.selectById(bankId); |
| | | if (bank == null) { |
| | | return; |
| | | } |
| | | LambdaQueryWrapper<TmsQuestionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsQuestionEntity::getBankId, bankId); |
| | | Long count = questionMapper.selectCount(qw); |
| | | bank.setQuestionCount(count == null ? 0 : count.intValue()); |
| | | bank.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | bank.setLastModifyTime(new Date()); |
| | | bankMapper.updateById(bank); |
| | | } |
| | | |
| | | /** |
| | | * 试题编号:年份后两位 + 4 位流水,如 260001。 |
| | | * 按当年最大号 +1,含已删记录,避免流水回退。 |
| | | */ |
| | | private synchronized String nextQuestionNo() { |
| | | String prefix = String.format("%02d", LocalDate.now().getYear() % 100); |
| | | String maxNo = questionMapper.selectMaxQuestionNo(prefix); |
| | | int next = 1; |
| | | if (StringUtil.isNotEmpty(maxNo) && maxNo.length() >= 6 && maxNo.startsWith(prefix)) { |
| | | try { |
| | | next = Integer.parseInt(maxNo.substring(prefix.length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | next = 1; |
| | | } |
| | | } |
| | | if (next > 9999) { |
| | | throw new DataException("当年试题编号已用尽(" + prefix + "9999)"); |
| | | } |
| | | return prefix + String.format("%04d", next); |
| | | } |
| | | |
| | | private TmsQuestionForm toForm( |
| | | TmsQuestionEntity entity, |
| | | Map<String, String> bankNames, |
| | | Map<String, String> userNames, |
| | | boolean withOptions) { |
| | | TmsQuestionForm form = new TmsQuestionForm(); |
| | | form.setId(entity.getId()); |
| | | form.setQuestionNo(entity.getQuestionNo()); |
| | | form.setBankId(entity.getBankId()); |
| | | form.setBankName(bankNames.get(entity.getBankId())); |
| | | form.setQuestionType(entity.getQuestionType()); |
| | | form.setDifficulty(entity.getDifficulty()); |
| | | form.setSourceType(entity.getSourceType()); |
| | | form.setStem(entity.getStem()); |
| | | form.setAnalysis(entity.getAnalysis()); |
| | | form.setAdminUserId(entity.getAdminUserId()); |
| | | form.setAdminUserName(userNames.get(entity.getAdminUserId())); |
| | | form.setBizStatus(entity.getBizStatus()); |
| | | form.setCreatorTime(entity.getCreatorTime() == null |
| | | ? null |
| | | : DateUtil.dateToString(entity.getCreatorTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | if (withOptions) { |
| | | form.setOptions(loadOptions(entity.getId())); |
| | | } |
| | | return form; |
| | | } |
| | | |
| | | private List<TmsQuestionOptionForm> loadOptions(String questionId) { |
| | | LambdaQueryWrapper<TmsQuestionOptionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsQuestionOptionEntity::getForeignId, questionId); |
| | | qw.orderByAsc(TmsQuestionOptionEntity::getSortNo); |
| | | List<TmsQuestionOptionEntity> rows = optionMapper.selectList(qw); |
| | | List<TmsQuestionOptionForm> list = new ArrayList<>(rows.size()); |
| | | for (TmsQuestionOptionEntity row : rows) { |
| | | TmsQuestionOptionForm opt = new TmsQuestionOptionForm(); |
| | | opt.setId(row.getId()); |
| | | opt.setSortNo(row.getSortNo()); |
| | | opt.setOptionLabel(row.getOptionLabel()); |
| | | opt.setOptionContent(row.getOptionContent()); |
| | | opt.setIsCorrect(row.getIsCorrect()); |
| | | list.add(opt); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private Map<String, String> loadBankNames(List<String> bankIds) { |
| | | if (bankIds == null || bankIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<TmsQuestionBankEntity> banks = bankMapper.selectBatchIds(bankIds); |
| | | Map<String, String> map = new HashMap<>(); |
| | | for (TmsQuestionBankEntity bank : banks) { |
| | | map.put(bank.getId(), bank.getBankName()); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, String> loadUserNames(List<String> userIds) { |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | try { |
| | | List<UserEntity> users = userApi.getUserName(userIds); |
| | | if (users == null || users.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return users.stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(UserEntity::getId, UserEntity::getRealName, (a, b) -> a)); |
| | | } catch (Exception ex) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.UserApi; |
| | | import jnpf.permission.entity.UserEntity; |
| | | import jnpf.tmsEntity.TmsMaterialEntity; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import jnpf.tmsEntity.TmsRecordEntity; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import jnpf.tmsEntity.TmsTaskFileEntity; |
| | | import jnpf.tmsEntity.record.TmsRecordAttachmentForm; |
| | | import jnpf.tmsEntity.record.TmsRecordForm; |
| | | import jnpf.tmsEntity.record.TmsRecordParticipantForm; |
| | | import jnpf.tmsEntity.record.TmsRecordQuery; |
| | | import jnpf.tmsEntity.record.TmsRecordSignForm; |
| | | import jnpf.tmsMapper.TmsMaterialMapper; |
| | | import jnpf.tmsMapper.TmsPersonTaskMapper; |
| | | import jnpf.tmsMapper.TmsRecordMapper; |
| | | import jnpf.tmsMapper.TmsTaskFileMapper; |
| | | import jnpf.tmsMapper.TmsTaskMapper; |
| | | import jnpf.tmsService.TmsRecordService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsRecordServiceImpl implements TmsRecordService { |
| | | |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | |
| | | private final TmsRecordMapper recordMapper; |
| | | private final TmsTaskMapper taskMapper; |
| | | private final TmsTaskFileMapper taskFileMapper; |
| | | private final TmsMaterialMapper materialMapper; |
| | | private final TmsPersonTaskMapper personTaskMapper; |
| | | private final UserApi userApi; |
| | | |
| | | @Override |
| | | public List<TmsRecordForm> getList(TmsRecordQuery query) { |
| | | LambdaQueryWrapper<TmsRecordEntity> qw = new LambdaQueryWrapper<>(); |
| | | applyListMode(qw, query); |
| | | |
| | | if (StringUtil.isNotEmpty(query.getRecordNo())) { |
| | | qw.like(TmsRecordEntity::getRecordNo, query.getRecordNo().trim()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getCategory())) { |
| | | qw.like(TmsRecordEntity::getCategory, query.getCategory().trim()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsRecordEntity::getRecordNo, kw) |
| | | .or().like(TmsRecordEntity::getSubject, kw)); |
| | | } |
| | | qw.orderByDesc(TmsRecordEntity::getStartTime).orderByDesc(TmsRecordEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsRecordEntity> page = recordMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | List<TmsRecordEntity> rows = page.getRecords(); |
| | | Map<String, String> names = loadUserNames(rows.stream() |
| | | .map(TmsRecordEntity::getTeacherId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList())); |
| | | |
| | | List<TmsRecordForm> list = new ArrayList<>(rows.size()); |
| | | String trainerFilter = StringUtil.isNotEmpty(query.getTrainerName()) ? query.getTrainerName().trim() : null; |
| | | for (TmsRecordEntity row : rows) { |
| | | TmsRecordForm form = toListForm(row, names); |
| | | if (trainerFilter != null) { |
| | | String name = form.getTrainerName() == null ? "" : form.getTrainerName(); |
| | | if (!name.contains(trainerFilter)) { |
| | | continue; |
| | | } |
| | | } |
| | | list.add(form); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsRecordForm getInfo(String id) { |
| | | TmsRecordEntity entity = requireOne(id); |
| | | Map<String, String> teacherNames = loadUserNames( |
| | | StringUtil.isEmpty(entity.getTeacherId()) |
| | | ? Collections.emptyList() |
| | | : Collections.singletonList(entity.getTeacherId())); |
| | | TmsRecordForm form = toListForm(entity, teacherNames); |
| | | |
| | | TmsTaskEntity task = StringUtil.isEmpty(entity.getTaskId()) ? null : taskMapper.selectById(entity.getTaskId()); |
| | | if (task != null) { |
| | | form.setKeyPoints(task.getKeyPoints()); |
| | | form.setPlaceName(task.getPlaceName()); |
| | | if (StringUtil.isEmpty(form.getTrainType()) && StringUtil.isNotEmpty(task.getTrainType())) { |
| | | form.setTrainType(task.getTrainType()); |
| | | } |
| | | if (StringUtil.isEmpty(form.getTrainMode()) && StringUtil.isNotEmpty(task.getTrainMode())) { |
| | | form.setTrainMode(task.getTrainMode()); |
| | | } |
| | | } |
| | | |
| | | List<TmsPersonTaskEntity> persons = StringUtil.isEmpty(entity.getTaskId()) |
| | | ? Collections.emptyList() |
| | | : personTaskMapper.selectList(new LambdaQueryWrapper<TmsPersonTaskEntity>() |
| | | .eq(TmsPersonTaskEntity::getTaskId, entity.getTaskId()) |
| | | .orderByAsc(TmsPersonTaskEntity::getCreatorTime)); |
| | | |
| | | Map<String, String> userNames = loadUserNames(persons.stream() |
| | | .map(TmsPersonTaskEntity::getUserId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList())); |
| | | |
| | | List<TmsRecordParticipantForm> participants = new ArrayList<>(persons.size()); |
| | | List<TmsRecordSignForm> signs = new ArrayList<>(); |
| | | List<String> traineeNames = new ArrayList<>(); |
| | | for (TmsPersonTaskEntity p : persons) { |
| | | String userName = userNames.getOrDefault(p.getUserId(), p.getUserId()); |
| | | traineeNames.add(userName); |
| | | |
| | | TmsRecordParticipantForm pf = new TmsRecordParticipantForm(); |
| | | pf.setId(p.getId()); |
| | | pf.setUserId(p.getUserId()); |
| | | pf.setUserName(userName); |
| | | pf.setSignDate(formatDate(p.getSignTime())); |
| | | pf.setExamScore(p.getExamScore()); |
| | | pf.setPassFlag(p.getPassFlag()); |
| | | participants.add(pf); |
| | | |
| | | if (p.getSignTime() != null) { |
| | | TmsRecordSignForm sf = new TmsRecordSignForm(); |
| | | sf.setId(p.getId()); |
| | | sf.setUserName(userName); |
| | | sf.setSignTime(formatDate(p.getSignTime())); |
| | | sf.setSignMode(p.getSignMode()); |
| | | sf.setGuestFlag(p.getGuestFlag()); |
| | | signs.add(sf); |
| | | } |
| | | } |
| | | form.setParticipants(participants); |
| | | form.setSignList(signs); |
| | | form.setTrainees(String.join(",", traineeNames)); |
| | | form.setAttachments(loadAttachments(entity.getTaskId())); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsRecordForm form) { |
| | | TmsRecordEntity entity = requireOne(id); |
| | | if (form == null) { |
| | | throw new DataException("表单不能为空"); |
| | | } |
| | | if (StringUtil.isNotEmpty(form.getSubject())) { |
| | | entity.setSubject(form.getSubject().trim()); |
| | | } |
| | | if (form.getCategory() != null) { |
| | | entity.setCategory(form.getCategory()); |
| | | } |
| | | if (form.getTrainType() != null) { |
| | | entity.setTrainType(form.getTrainType()); |
| | | } |
| | | if (form.getTrainMode() != null) { |
| | | entity.setTrainMode(form.getTrainMode()); |
| | | } |
| | | if (form.getEvalMode() != null) { |
| | | entity.setEvalMode(form.getEvalMode()); |
| | | } |
| | | entity.setRemark(form.getRemark()); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | recordMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int archive(List<String> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | throw new DataException("请选择要归档的记录"); |
| | | } |
| | | int count = 0; |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | for (String id : ids) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | continue; |
| | | } |
| | | TmsRecordEntity entity = recordMapper.selectById(id); |
| | | if (entity == null) { |
| | | continue; |
| | | } |
| | | entity.setArchiveStatus("archived"); |
| | | entity.setLastModifyUserId(userId); |
| | | entity.setLastModifyTime(now); |
| | | recordMapper.updateById(entity); |
| | | count++; |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(List<String> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | throw new DataException("请选择要删除的记录"); |
| | | } |
| | | for (String id : ids) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | continue; |
| | | } |
| | | recordMapper.deleteById(id); |
| | | } |
| | | } |
| | | |
| | | private void applyListMode(LambdaQueryWrapper<TmsRecordEntity> qw, TmsRecordQuery query) { |
| | | String archiveStatus = query.getArchiveStatus(); |
| | | if (StringUtil.isNotEmpty(archiveStatus)) { |
| | | qw.eq(TmsRecordEntity::getArchiveStatus, archiveStatus); |
| | | return; |
| | | } |
| | | String mode = query.getListMode(); |
| | | if ("ready".equals(mode)) { |
| | | qw.eq(TmsRecordEntity::getArchiveStatus, "ready"); |
| | | } else if ("archived".equals(mode)) { |
| | | qw.eq(TmsRecordEntity::getArchiveStatus, "archived"); |
| | | } else { |
| | | // 主列表:待归档 / 进行中 |
| | | qw.and(w -> w.eq(TmsRecordEntity::getArchiveStatus, "pending") |
| | | .or().isNull(TmsRecordEntity::getArchiveStatus)); |
| | | } |
| | | } |
| | | |
| | | private TmsRecordForm toListForm(TmsRecordEntity entity, Map<String, String> names) { |
| | | TmsRecordForm form = new TmsRecordForm(); |
| | | form.setId(entity.getId()); |
| | | form.setRecordNo(entity.getRecordNo()); |
| | | form.setTaskId(entity.getTaskId()); |
| | | form.setTaskNo(entity.getTaskNo()); |
| | | form.setCategory(entity.getCategory()); |
| | | form.setSubject(entity.getSubject()); |
| | | form.setTeacherId(entity.getTeacherId()); |
| | | form.setTrainerName(names.getOrDefault(entity.getTeacherId(), |
| | | StringUtil.isEmpty(entity.getTeacherId()) ? "-" : entity.getTeacherId())); |
| | | form.setTrainType(StringUtil.isNotEmpty(entity.getTrainType()) |
| | | ? entity.getTrainType() |
| | | : entity.getTrainMode()); |
| | | form.setTrainMode(entity.getTrainMode()); |
| | | form.setEvalMode(entity.getEvalMode()); |
| | | form.setStartTime(formatDate(entity.getStartTime())); |
| | | form.setEndTime(formatDate(entity.getEndTime())); |
| | | form.setCloseTime(formatDate(entity.getCloseTime())); |
| | | form.setArchiveStatus(entity.getArchiveStatus()); |
| | | form.setSignRate(entity.getSignRate()); |
| | | form.setPassRate(entity.getPassRate()); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | |
| | | private List<TmsRecordAttachmentForm> loadAttachments(String taskId) { |
| | | if (StringUtil.isEmpty(taskId)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<TmsTaskFileEntity> files = taskFileMapper.selectList( |
| | | new LambdaQueryWrapper<TmsTaskFileEntity>() |
| | | .eq(TmsTaskFileEntity::getForeignId, taskId) |
| | | .orderByAsc(TmsTaskFileEntity::getCreatorTime)); |
| | | if (files.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | List<String> materialIds = files.stream() |
| | | .map(TmsTaskFileEntity::getMaterialId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | Map<String, TmsMaterialEntity> materialMap = new HashMap<>(); |
| | | if (!materialIds.isEmpty()) { |
| | | for (TmsMaterialEntity material : materialMapper.selectBatchIds(materialIds)) { |
| | | materialMap.put(material.getId(), material); |
| | | } |
| | | } |
| | | |
| | | List<TmsRecordAttachmentForm> list = new ArrayList<>(); |
| | | for (TmsTaskFileEntity file : files) { |
| | | String url = extractUrl(file.getFileJson()); |
| | | if (StringUtil.isEmpty(url) && StringUtil.isNotEmpty(file.getMaterialId())) { |
| | | TmsMaterialEntity material = materialMap.get(file.getMaterialId()); |
| | | if (material != null) { |
| | | url = extractUrl(material.getFileJson()); |
| | | } |
| | | } |
| | | TmsRecordAttachmentForm item = new TmsRecordAttachmentForm(); |
| | | item.setId(file.getId()); |
| | | item.setName(file.getFileName()); |
| | | item.setUrl(url); |
| | | list.add(item); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private String extractUrl(String fileJson) { |
| | | if (StringUtil.isEmpty(fileJson)) { |
| | | return null; |
| | | } |
| | | try { |
| | | String text = fileJson.trim(); |
| | | if (text.startsWith("[")) { |
| | | List<Map<String, Object>> arr = OBJECT_MAPPER.readValue(text, |
| | | new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | if (arr == null || arr.isEmpty()) { |
| | | return null; |
| | | } |
| | | Object url = arr.get(0).get("url"); |
| | | return url == null ? null : String.valueOf(url); |
| | | } |
| | | Map<String, Object> map = OBJECT_MAPPER.readValue(text, new TypeReference<Map<String, Object>>() { |
| | | }); |
| | | Object url = map.get("url"); |
| | | return url == null ? null : String.valueOf(url); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private Map<String, String> loadUserNames(List<String> userIds) { |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<String> ids = userIds.stream().filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList()); |
| | | if (ids.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | try { |
| | | List<UserEntity> users = userApi.getUserName(ids); |
| | | if (users == null || users.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | Map<String, String> map = new LinkedHashMap<>(); |
| | | for (UserEntity user : users) { |
| | | if (user == null || StringUtil.isEmpty(user.getId())) { |
| | | continue; |
| | | } |
| | | String name = StringUtil.isNotEmpty(user.getRealName()) ? user.getRealName() : user.getAccount(); |
| | | map.put(user.getId(), name); |
| | | } |
| | | return map; |
| | | } catch (Exception ex) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | } |
| | | |
| | | private TmsRecordEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("记录ID不能为空"); |
| | | } |
| | | TmsRecordEntity entity = recordMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("培训记录不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private String formatDate(Date date) { |
| | | return date == null ? null : DateUtil.dateToString(date, FMT); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsQuestionBankEntity; |
| | | import jnpf.tmsEntity.TmsQuestionEntity; |
| | | import jnpf.tmsEntity.TmsQuestionOptionEntity; |
| | | import jnpf.tmsEntity.TmsSelfTestEntity; |
| | | import jnpf.tmsEntity.TmsSelfTestItemEntity; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestOptionVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestPaperVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestQuery; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestQuestionVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestRecordVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestStartForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitResultVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestTypeSetting; |
| | | import jnpf.tmsMapper.TmsQuestionBankMapper; |
| | | import jnpf.tmsMapper.TmsQuestionMapper; |
| | | import jnpf.tmsMapper.TmsQuestionOptionMapper; |
| | | import jnpf.tmsMapper.TmsSelfTestItemMapper; |
| | | import jnpf.tmsMapper.TmsSelfTestMapper; |
| | | import jnpf.tmsService.TmsSelfTestService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsSelfTestServiceImpl implements TmsSelfTestService { |
| | | |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | |
| | | /** 自我检测 UI 难度 → 试题 difficulty 值 */ |
| | | private static final Map<String, List<String>> DIFFICULTY_MAP = new HashMap<>(); |
| | | |
| | | static { |
| | | DIFFICULTY_MAP.put("veryEasy", Arrays.asList("easy", "veryEasy")); |
| | | DIFFICULTY_MAP.put("easier", Arrays.asList("easy", "easier")); |
| | | DIFFICULTY_MAP.put("normal", Collections.singletonList("normal")); |
| | | DIFFICULTY_MAP.put("harder", Arrays.asList("hard", "harder")); |
| | | DIFFICULTY_MAP.put("veryHard", Arrays.asList("hard", "veryHard")); |
| | | } |
| | | |
| | | private final TmsSelfTestMapper selfTestMapper; |
| | | private final TmsSelfTestItemMapper selfTestItemMapper; |
| | | private final TmsQuestionMapper questionMapper; |
| | | private final TmsQuestionOptionMapper optionMapper; |
| | | private final TmsQuestionBankMapper bankMapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsSelfTestPaperVO start(TmsSelfTestStartForm form) { |
| | | if (form == null || StringUtil.isEmpty(form.getBankId())) { |
| | | throw new DataException("请选择题库"); |
| | | } |
| | | TmsQuestionBankEntity bank = bankMapper.selectById(form.getBankId()); |
| | | if (bank == null) { |
| | | throw new DataException("题库不存在或已删除"); |
| | | } |
| | | |
| | | int singleCnt = countOf(form.getSingle()); |
| | | int multiCnt = countOf(form.getMulti()); |
| | | int judgeCnt = countOf(form.getJudge()); |
| | | if (singleCnt + multiCnt + judgeCnt <= 0) { |
| | | throw new DataException("请至少设置一种题型的抽题数量"); |
| | | } |
| | | |
| | | List<TmsQuestionEntity> drawn = new ArrayList<>(); |
| | | drawn.addAll(drawQuestions(form.getBankId(), "single", form.getSingle())); |
| | | drawn.addAll(drawQuestions(form.getBankId(), "multi", form.getMulti())); |
| | | drawn.addAll(drawQuestions(form.getBankId(), "judge", form.getJudge())); |
| | | if (drawn.isEmpty()) { |
| | | throw new DataException("当前条件下没有可抽题目,请调整数量或难度"); |
| | | } |
| | | |
| | | Map<String, List<TmsQuestionOptionEntity>> optionMap = loadOptions( |
| | | drawn.stream().map(TmsQuestionEntity::getId).collect(Collectors.toList())); |
| | | |
| | | String userId = UserProvider.getLoginUserId(); |
| | | Date now = new Date(); |
| | | String paperId = RandomUtil.uuId(); |
| | | |
| | | TmsSelfTestEntity entity = new TmsSelfTestEntity(); |
| | | entity.setId(paperId); |
| | | entity.setBankId(bank.getId()); |
| | | entity.setBankName(StringUtil.isNotEmpty(form.getBankName()) ? form.getBankName() : bank.getBankName()); |
| | | entity.setUserId(userId); |
| | | entity.setSingleCount(singleCnt); |
| | | entity.setMultiCount(multiCnt); |
| | | entity.setJudgeCount(judgeCnt); |
| | | entity.setSingleDifficulty(diffOf(form.getSingle())); |
| | | entity.setMultiDifficulty(diffOf(form.getMulti())); |
| | | entity.setJudgeDifficulty(diffOf(form.getJudge())); |
| | | entity.setTotalCount(drawn.size()); |
| | | entity.setStartTime(now); |
| | | entity.setTestStatus("doing"); |
| | | entity.setCreatorUserId(userId); |
| | | entity.setCreatorTime(now); |
| | | selfTestMapper.insert(entity); |
| | | |
| | | List<TmsSelfTestQuestionVO> questions = new ArrayList<>(drawn.size()); |
| | | int sort = 0; |
| | | for (TmsQuestionEntity q : drawn) { |
| | | List<TmsQuestionOptionEntity> opts = optionMap.getOrDefault(q.getId(), Collections.emptyList()); |
| | | List<TmsSelfTestOptionVO> optionVos = toOptionVos(opts); |
| | | String correct = opts.stream() |
| | | .filter(o -> "1".equals(o.getIsCorrect())) |
| | | .map(TmsQuestionOptionEntity::getOptionLabel) |
| | | .sorted() |
| | | .collect(Collectors.joining(",")); |
| | | |
| | | TmsSelfTestItemEntity item = new TmsSelfTestItemEntity(); |
| | | item.setId(RandomUtil.uuId()); |
| | | item.setForeignId(paperId); |
| | | item.setQuestionId(q.getId()); |
| | | item.setQuestionNo(q.getQuestionNo()); |
| | | item.setQuestionType(q.getQuestionType()); |
| | | item.setDifficulty(q.getDifficulty()); |
| | | item.setStem(q.getStem()); |
| | | item.setOptionsJson(toJson(optionVos)); |
| | | item.setCorrectAnswer(correct); |
| | | item.setSortNo(++sort); |
| | | item.setCreatorUserId(userId); |
| | | item.setCreatorTime(now); |
| | | selfTestItemMapper.insert(item); |
| | | |
| | | TmsSelfTestQuestionVO vo = new TmsSelfTestQuestionVO(); |
| | | vo.setId(q.getId()); |
| | | vo.setQuestionNo(q.getQuestionNo()); |
| | | vo.setQuestionType(q.getQuestionType()); |
| | | vo.setDifficulty(q.getDifficulty()); |
| | | vo.setStem(q.getStem()); |
| | | vo.setOptions(optionVos); |
| | | questions.add(vo); |
| | | } |
| | | |
| | | TmsSelfTestPaperVO paper = new TmsSelfTestPaperVO(); |
| | | paper.setPaperId(paperId); |
| | | paper.setBankId(entity.getBankId()); |
| | | paper.setBankName(entity.getBankName()); |
| | | paper.setQuestions(questions); |
| | | return paper; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void saveAnswers(String id, TmsSelfTestSubmitForm form) { |
| | | TmsSelfTestEntity entity = requireDoingPaper(id, "无权暂存该检测"); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | List<TmsSelfTestItemEntity> items = listItems(id); |
| | | Map<String, Object> answers = form != null && form.getAnswers() != null |
| | | ? form.getAnswers() |
| | | : Collections.emptyMap(); |
| | | |
| | | Date now = new Date(); |
| | | for (TmsSelfTestItemEntity item : items) { |
| | | // 仅更新前端已作答的题,未传的保持原值,方便增量暂存 |
| | | if (!answers.containsKey(item.getQuestionId())) { |
| | | continue; |
| | | } |
| | | String userAnswer = normalizeAnswer(answers.get(item.getQuestionId()), item.getQuestionType()); |
| | | item.setUserAnswer(userAnswer); |
| | | item.setIsRight(null); |
| | | item.setLastModifyUserId(userId); |
| | | item.setLastModifyTime(now); |
| | | selfTestItemMapper.updateById(item); |
| | | } |
| | | |
| | | entity.setLastModifyUserId(userId); |
| | | entity.setLastModifyTime(now); |
| | | selfTestMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsSelfTestSubmitResultVO submit(String id, TmsSelfTestSubmitForm form) { |
| | | TmsSelfTestEntity entity = requireDoingPaper(id, "无权提交该检测"); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | List<TmsSelfTestItemEntity> items = listItems(id); |
| | | |
| | | Map<String, Object> answers = form != null && form.getAnswers() != null |
| | | ? form.getAnswers() |
| | | : Collections.emptyMap(); |
| | | |
| | | Date now = new Date(); |
| | | int correct = 0; |
| | | for (TmsSelfTestItemEntity item : items) { |
| | | String userAnswer = normalizeAnswer(answers.get(item.getQuestionId()), item.getQuestionType()); |
| | | boolean right = Objects.equals(userAnswer, normalizeCorrect(item.getCorrectAnswer())); |
| | | item.setUserAnswer(userAnswer); |
| | | item.setIsRight(right ? "1" : "0"); |
| | | item.setLastModifyUserId(userId); |
| | | item.setLastModifyTime(now); |
| | | selfTestItemMapper.updateById(item); |
| | | if (right) { |
| | | correct++; |
| | | } |
| | | } |
| | | |
| | | int total = items.size(); |
| | | BigDecimal rate = total <= 0 |
| | | ? BigDecimal.ZERO |
| | | : BigDecimal.valueOf(correct * 100.0 / total).setScale(2, RoundingMode.HALF_UP); |
| | | |
| | | entity.setCorrectCount(correct); |
| | | entity.setTotalCount(total); |
| | | entity.setScoreRate(rate); |
| | | entity.setSubmitTime(now); |
| | | entity.setTestStatus("submitted"); |
| | | entity.setLastModifyUserId(userId); |
| | | entity.setLastModifyTime(now); |
| | | selfTestMapper.updateById(entity); |
| | | |
| | | TmsSelfTestSubmitResultVO result = new TmsSelfTestSubmitResultVO(); |
| | | result.setPaperId(id); |
| | | result.setTotalCount(total); |
| | | result.setCorrectCount(correct); |
| | | result.setScoreRate(rate); |
| | | return result; |
| | | } |
| | | |
| | | private TmsSelfTestEntity requireDoingPaper(String id, String denyMsg) { |
| | | TmsSelfTestEntity entity = selfTestMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("检测记录不存在"); |
| | | } |
| | | String userId = UserProvider.getLoginUserId(); |
| | | if (!Objects.equals(userId, entity.getUserId())) { |
| | | throw new DataException(denyMsg); |
| | | } |
| | | if ("submitted".equals(entity.getTestStatus())) { |
| | | throw new DataException("已交卷,请勿重复操作"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private List<TmsSelfTestItemEntity> listItems(String paperId) { |
| | | LambdaQueryWrapper<TmsSelfTestItemEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsSelfTestItemEntity::getForeignId, paperId); |
| | | qw.orderByAsc(TmsSelfTestItemEntity::getSortNo); |
| | | return selfTestItemMapper.selectList(qw); |
| | | } |
| | | |
| | | @Override |
| | | public List<TmsSelfTestRecordVO> getList(TmsSelfTestQuery query) { |
| | | String userId = UserProvider.getLoginUserId(); |
| | | LambdaQueryWrapper<TmsSelfTestEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsSelfTestEntity::getUserId, userId); |
| | | if (StringUtil.isNotEmpty(query.getBankId())) { |
| | | qw.eq(TmsSelfTestEntity::getBankId, query.getBankId()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getTestStatus())) { |
| | | qw.eq(TmsSelfTestEntity::getTestStatus, query.getTestStatus()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | qw.like(TmsSelfTestEntity::getBankName, query.getKeyword().trim()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getStartTimeBegin())) { |
| | | Date begin = DateUtil.stringToDate(query.getStartTimeBegin()); |
| | | if (begin != null) { |
| | | qw.ge(TmsSelfTestEntity::getStartTime, begin); |
| | | } |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getStartTimeEnd())) { |
| | | Date end = DateUtil.stringToDate(query.getStartTimeEnd()); |
| | | if (end != null) { |
| | | qw.le(TmsSelfTestEntity::getStartTime, end); |
| | | } |
| | | } |
| | | qw.orderByDesc(TmsSelfTestEntity::getCreatorTime); |
| | | |
| | | Page<TmsSelfTestEntity> page = new Page<>(query.getCurrentPage(), query.getPageSize()); |
| | | Page<TmsSelfTestEntity> result = selfTestMapper.selectPage(page, qw); |
| | | |
| | | List<TmsSelfTestRecordVO> list = new ArrayList<>(); |
| | | for (TmsSelfTestEntity row : result.getRecords()) { |
| | | TmsSelfTestRecordVO vo = new TmsSelfTestRecordVO(); |
| | | vo.setId(row.getId()); |
| | | vo.setBankId(row.getBankId()); |
| | | vo.setBankName(row.getBankName()); |
| | | vo.setTotalCount(row.getTotalCount()); |
| | | vo.setCorrectCount(row.getCorrectCount()); |
| | | vo.setScoreRate(row.getScoreRate()); |
| | | vo.setTestStatus(row.getTestStatus()); |
| | | vo.setStartTime(row.getStartTime() == null ? null : DateUtil.dateToString(row.getStartTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | vo.setSubmitTime(row.getSubmitTime() == null ? null : DateUtil.dateToString(row.getSubmitTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | list.add(vo); |
| | | } |
| | | query.setData(list, result.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsSelfTestPaperVO getInfo(String id) { |
| | | TmsSelfTestEntity entity = selfTestMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("检测记录不存在"); |
| | | } |
| | | if (!Objects.equals(UserProvider.getLoginUserId(), entity.getUserId())) { |
| | | throw new DataException("无权查看该检测"); |
| | | } |
| | | |
| | | LambdaQueryWrapper<TmsSelfTestItemEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsSelfTestItemEntity::getForeignId, id); |
| | | qw.orderByAsc(TmsSelfTestItemEntity::getSortNo); |
| | | List<TmsSelfTestItemEntity> items = selfTestItemMapper.selectList(qw); |
| | | |
| | | List<TmsSelfTestQuestionVO> questions = new ArrayList<>(items.size()); |
| | | for (TmsSelfTestItemEntity item : items) { |
| | | TmsSelfTestQuestionVO vo = new TmsSelfTestQuestionVO(); |
| | | vo.setId(item.getQuestionId()); |
| | | vo.setQuestionNo(item.getQuestionNo()); |
| | | vo.setQuestionType(item.getQuestionType()); |
| | | vo.setDifficulty(item.getDifficulty()); |
| | | vo.setStem(item.getStem()); |
| | | vo.setOptions(parseOptions(item.getOptionsJson())); |
| | | vo.setCorrectAnswer(item.getCorrectAnswer()); |
| | | vo.setUserAnswer(item.getUserAnswer()); |
| | | vo.setIsRight(item.getIsRight()); |
| | | vo.setSortNo(item.getSortNo()); |
| | | questions.add(vo); |
| | | } |
| | | |
| | | TmsSelfTestPaperVO paper = new TmsSelfTestPaperVO(); |
| | | paper.setPaperId(entity.getId()); |
| | | paper.setBankId(entity.getBankId()); |
| | | paper.setBankName(entity.getBankName()); |
| | | paper.setTestStatus(entity.getTestStatus()); |
| | | paper.setTotalCount(entity.getTotalCount()); |
| | | paper.setCorrectCount(entity.getCorrectCount()); |
| | | paper.setScoreRate(entity.getScoreRate()); |
| | | paper.setStartTime(entity.getStartTime() == null ? null : DateUtil.dateToString(entity.getStartTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | paper.setSubmitTime(entity.getSubmitTime() == null ? null : DateUtil.dateToString(entity.getSubmitTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | paper.setQuestions(questions); |
| | | return paper; |
| | | } |
| | | |
| | | private List<TmsQuestionEntity> drawQuestions(String bankId, String type, TmsSelfTestTypeSetting setting) { |
| | | int count = countOf(setting); |
| | | if (count <= 0) { |
| | | return Collections.emptyList(); |
| | | } |
| | | LambdaQueryWrapper<TmsQuestionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsQuestionEntity::getBankId, bankId); |
| | | qw.eq(TmsQuestionEntity::getQuestionType, type); |
| | | qw.eq(TmsQuestionEntity::getBizStatus, "open"); |
| | | List<String> diffs = resolveDifficulties(diffOf(setting)); |
| | | if (!diffs.isEmpty()) { |
| | | qw.in(TmsQuestionEntity::getDifficulty, diffs); |
| | | } |
| | | List<TmsQuestionEntity> pool = questionMapper.selectList(qw); |
| | | if (pool.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Collections.shuffle(pool); |
| | | if (pool.size() <= count) { |
| | | return pool; |
| | | } |
| | | return new ArrayList<>(pool.subList(0, count)); |
| | | } |
| | | |
| | | private List<String> resolveDifficulties(String uiDifficulty) { |
| | | if (StringUtil.isEmpty(uiDifficulty)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return DIFFICULTY_MAP.getOrDefault(uiDifficulty, Collections.singletonList(uiDifficulty)); |
| | | } |
| | | |
| | | private Map<String, List<TmsQuestionOptionEntity>> loadOptions(List<String> questionIds) { |
| | | if (questionIds == null || questionIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | LambdaQueryWrapper<TmsQuestionOptionEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.in(TmsQuestionOptionEntity::getForeignId, questionIds); |
| | | qw.orderByAsc(TmsQuestionOptionEntity::getSortNo); |
| | | List<TmsQuestionOptionEntity> all = optionMapper.selectList(qw); |
| | | return all.stream().collect(Collectors.groupingBy(TmsQuestionOptionEntity::getForeignId)); |
| | | } |
| | | |
| | | private List<TmsSelfTestOptionVO> toOptionVos(List<TmsQuestionOptionEntity> opts) { |
| | | List<TmsSelfTestOptionVO> list = new ArrayList<>(opts.size()); |
| | | for (TmsQuestionOptionEntity opt : opts) { |
| | | TmsSelfTestOptionVO vo = new TmsSelfTestOptionVO(); |
| | | vo.setOptionLabel(opt.getOptionLabel()); |
| | | vo.setOptionContent(opt.getOptionContent()); |
| | | vo.setIsCorrect(opt.getIsCorrect()); |
| | | list.add(vo); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private String toJson(Object obj) { |
| | | try { |
| | | return OBJECT_MAPPER.writeValueAsString(obj); |
| | | } catch (Exception e) { |
| | | throw new DataException("选项序列化失败"); |
| | | } |
| | | } |
| | | |
| | | private List<TmsSelfTestOptionVO> parseOptions(String json) { |
| | | if (StringUtil.isEmpty(json)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | try { |
| | | return OBJECT_MAPPER.readValue(json, new TypeReference<List<TmsSelfTestOptionVO>>() { |
| | | }); |
| | | } catch (Exception e) { |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | | |
| | | private int countOf(TmsSelfTestTypeSetting setting) { |
| | | if (setting == null || setting.getCount() == null) { |
| | | return 0; |
| | | } |
| | | return Math.max(0, setting.getCount()); |
| | | } |
| | | |
| | | private String diffOf(TmsSelfTestTypeSetting setting) { |
| | | return setting == null ? null : setting.getDifficulty(); |
| | | } |
| | | |
| | | /** 归一化用户答案:多选排序后逗号拼接 */ |
| | | private String normalizeAnswer(Object raw, String questionType) { |
| | | if (raw == null) { |
| | | return ""; |
| | | } |
| | | if ("multi".equals(questionType)) { |
| | | Set<String> labels = new LinkedHashSet<>(); |
| | | if (raw instanceof List) { |
| | | for (Object o : (List<?>) raw) { |
| | | if (o != null && StringUtil.isNotEmpty(String.valueOf(o))) { |
| | | labels.add(String.valueOf(o).trim()); |
| | | } |
| | | } |
| | | } else { |
| | | String s = String.valueOf(raw).trim(); |
| | | if (StringUtil.isNotEmpty(s)) { |
| | | for (String part : s.split("[,,]")) { |
| | | if (StringUtil.isNotEmpty(part.trim())) { |
| | | labels.add(part.trim()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return labels.stream().sorted().collect(Collectors.joining(",")); |
| | | } |
| | | return String.valueOf(raw).trim(); |
| | | } |
| | | |
| | | private String normalizeCorrect(String correct) { |
| | | if (StringUtil.isEmpty(correct)) { |
| | | return ""; |
| | | } |
| | | return Arrays.stream(correct.split("[,,]")) |
| | | .map(String::trim) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .sorted() |
| | | .collect(Collectors.joining(",")); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsSignModeEntity; |
| | | import jnpf.tmsEntity.signmode.TmsSignModeForm; |
| | | import jnpf.tmsEntity.signmode.TmsSignModeQuery; |
| | | import jnpf.tmsMapper.TmsSignModeMapper; |
| | | import jnpf.tmsService.TmsSignModeService; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsSignModeServiceImpl implements TmsSignModeService { |
| | | |
| | | private final TmsSignModeMapper signModeMapper; |
| | | |
| | | @Override |
| | | public List<TmsSignModeForm> getList(TmsSignModeQuery query) { |
| | | LambdaQueryWrapper<TmsSignModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getEnabled())) { |
| | | qw.eq(TmsSignModeEntity::getEnabled, query.getEnabled()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsSignModeEntity::getModeCode, kw) |
| | | .or() |
| | | .like(TmsSignModeEntity::getModeName, kw)); |
| | | } |
| | | qw.orderByDesc(TmsSignModeEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsSignModeEntity> page = signModeMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | List<TmsSignModeForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsSignModeEntity row : page.getRecords()) { |
| | | list.add(toForm(row)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsSignModeForm getInfo(String id) { |
| | | return toForm(requireOne(id)); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsSignModeForm form) { |
| | | validateForm(form, null); |
| | | TmsSignModeEntity entity = new TmsSignModeEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | applyForm(entity, form); |
| | | entity.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | entity.setCreatorTime(new Date()); |
| | | signModeMapper.insert(entity); |
| | | return entity.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsSignModeForm form) { |
| | | TmsSignModeEntity entity = requireOne(id); |
| | | validateForm(form, id); |
| | | form.setModeCode(entity.getModeCode()); |
| | | applyForm(entity, form); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | signModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void setEnabled(String id, String enabled) { |
| | | if (!"0".equals(enabled) && !"1".equals(enabled)) { |
| | | throw new DataException("启用状态无效"); |
| | | } |
| | | TmsSignModeEntity entity = requireOne(id); |
| | | entity.setEnabled(enabled); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | signModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(String id) { |
| | | TmsSignModeEntity entity = requireOne(id); |
| | | if (!"0".equals(entity.getEnabled())) { |
| | | throw new DataException("请先停用后再删除"); |
| | | } |
| | | signModeMapper.deleteById(id); |
| | | } |
| | | |
| | | private TmsSignModeEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("签到方式不存在"); |
| | | } |
| | | TmsSignModeEntity entity = signModeMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("签到方式不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private void validateForm(TmsSignModeForm form, String excludeId) { |
| | | if (form == null || StringUtil.isEmpty(form.getModeCode())) { |
| | | throw new DataException("请填写方式编码"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getModeName())) { |
| | | throw new DataException("请填写方式名称"); |
| | | } |
| | | String code = form.getModeCode().trim(); |
| | | form.setModeCode(code); |
| | | LambdaQueryWrapper<TmsSignModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsSignModeEntity::getModeCode, code); |
| | | if (StringUtil.isNotEmpty(excludeId)) { |
| | | qw.ne(TmsSignModeEntity::getId, excludeId); |
| | | } |
| | | if (signModeMapper.selectCount(qw) > 0) { |
| | | throw new DataException("方式编码已存在"); |
| | | } |
| | | } |
| | | |
| | | private void applyForm(TmsSignModeEntity entity, TmsSignModeForm form) { |
| | | entity.setModeCode(form.getModeCode().trim()); |
| | | entity.setModeName(form.getModeName().trim()); |
| | | entity.setTipSeconds(form.getTipSeconds() == null ? 30 : Math.max(0, form.getTipSeconds())); |
| | | entity.setMatchRoster(yn(form.getMatchRoster(), "1")); |
| | | entity.setEnabled(yn(form.getEnabled(), "1")); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private static String yn(String v, String def) { |
| | | if ("0".equals(v) || "1".equals(v)) { |
| | | return v; |
| | | } |
| | | return def; |
| | | } |
| | | |
| | | private TmsSignModeForm toForm(TmsSignModeEntity entity) { |
| | | TmsSignModeForm form = new TmsSignModeForm(); |
| | | form.setId(entity.getId()); |
| | | form.setModeCode(entity.getModeCode()); |
| | | form.setModeName(entity.getModeName()); |
| | | form.setTipSeconds(entity.getTipSeconds()); |
| | | form.setMatchRoster(entity.getMatchRoster()); |
| | | form.setEnabled(entity.getEnabled()); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsAnnualPlanEntity; |
| | | import jnpf.tmsEntity.TmsAnnualPlanItemEntity; |
| | | import jnpf.tmsEntity.TmsMaterialEntity; |
| | | import jnpf.tmsEntity.TmsPaperEntity; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import jnpf.tmsEntity.TmsPlaceEntity; |
| | | import jnpf.tmsEntity.TmsPostPlanEntity; |
| | | import jnpf.tmsEntity.TmsPostPlanItemEntity; |
| | | import jnpf.tmsEntity.TmsRecordEntity; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import jnpf.tmsEntity.TmsTaskFileEntity; |
| | | import jnpf.tmsEntity.TmsTrainerEntity; |
| | | import jnpf.tmsEntity.task.TmsFromPlanResult; |
| | | import jnpf.tmsEntity.task.TmsPersonTaskForm; |
| | | import jnpf.tmsEntity.task.TmsTaskFileForm; |
| | | import jnpf.tmsEntity.task.TmsTaskForm; |
| | | import jnpf.tmsEntity.task.TmsTaskQuery; |
| | | import jnpf.tmsMapper.TmsAnnualPlanItemMapper; |
| | | import jnpf.tmsMapper.TmsAnnualPlanMapper; |
| | | import jnpf.tmsMapper.TmsMaterialMapper; |
| | | import jnpf.tmsMapper.TmsPaperMapper; |
| | | import jnpf.tmsMapper.TmsPersonTaskMapper; |
| | | import jnpf.tmsMapper.TmsPlaceMapper; |
| | | import jnpf.tmsMapper.TmsPostPlanItemMapper; |
| | | import jnpf.tmsMapper.TmsPostPlanMapper; |
| | | import jnpf.tmsMapper.TmsRecordMapper; |
| | | import jnpf.tmsMapper.TmsTaskFileMapper; |
| | | import jnpf.tmsMapper.TmsTaskMapper; |
| | | import jnpf.tmsMapper.TmsTrainerMapper; |
| | | import jnpf.tmsService.TmsTaskService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.YearMonth; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsTaskServiceImpl implements TmsTaskService { |
| | | |
| | | private static final String STATUS_DRAFT = "draft"; |
| | | private static final String STATUS_PUBLISHED = "published"; |
| | | private static final String STATUS_CANCELLED = "cancelled"; |
| | | |
| | | private static final String PERSON_TODO = "todo"; |
| | | private static final String PERSON_SIGNED = "signed"; |
| | | private static final String PERSON_DONE = "done"; |
| | | private static final String PERSON_CANCELLED = "cancelled"; |
| | | |
| | | private static final String PREFIX_DRAFT = "TD"; |
| | | private static final String PREFIX_PUBLISH = "TT"; |
| | | private static final String FMT = "yyyy-MM-dd HH:mm:ss"; |
| | | |
| | | private final TmsTaskMapper taskMapper; |
| | | private final TmsTrainerMapper trainerMapper; |
| | | private final TmsPersonTaskMapper personTaskMapper; |
| | | private final TmsRecordMapper recordMapper; |
| | | private final TmsPaperMapper paperMapper; |
| | | private final TmsMaterialMapper materialMapper; |
| | | private final TmsTaskFileMapper taskFileMapper; |
| | | private final TmsPlaceMapper placeMapper; |
| | | private final TmsAnnualPlanMapper annualPlanMapper; |
| | | private final TmsAnnualPlanItemMapper annualPlanItemMapper; |
| | | private final TmsPostPlanMapper postPlanMapper; |
| | | private final TmsPostPlanItemMapper postPlanItemMapper; |
| | | |
| | | @Override |
| | | public List<TmsTaskForm> getList(TmsTaskQuery query) { |
| | | LambdaQueryWrapper<TmsTaskEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getPublishStatus())) { |
| | | qw.eq(TmsTaskEntity::getPublishStatus, query.getPublishStatus()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getTrainMode())) { |
| | | qw.eq(TmsTaskEntity::getTrainMode, query.getTrainMode()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getSourceType())) { |
| | | qw.eq(TmsTaskEntity::getSourceType, query.getSourceType()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsTaskEntity::getTaskNo, kw).or().like(TmsTaskEntity::getSubject, kw)); |
| | | } |
| | | Date from = parseDayStart(query.getStartTimeFrom()); |
| | | Date to = parseDayEnd(query.getStartTimeTo()); |
| | | if (from != null) { |
| | | qw.ge(TmsTaskEntity::getStartTime, from); |
| | | } |
| | | if (to != null) { |
| | | qw.le(TmsTaskEntity::getStartTime, to); |
| | | } |
| | | qw.orderByDesc(TmsTaskEntity::getStartTime).orderByDesc(TmsTaskEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsTaskEntity> page = taskMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | List<TmsTaskForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsTaskEntity row : page.getRecords()) { |
| | | list.add(toForm(row, false)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsTaskForm getInfo(String id) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | TmsTaskForm form = toForm(entity, true); |
| | | List<TmsPersonTaskEntity> persons = personTaskMapper.selectList( |
| | | new LambdaQueryWrapper<TmsPersonTaskEntity>() |
| | | .eq(TmsPersonTaskEntity::getTaskId, id) |
| | | .orderByAsc(TmsPersonTaskEntity::getCreatorTime)); |
| | | List<TmsPersonTaskForm> personForms = new ArrayList<>(persons.size()); |
| | | for (TmsPersonTaskEntity p : persons) { |
| | | TmsPersonTaskForm pf = new TmsPersonTaskForm(); |
| | | pf.setId(p.getId()); |
| | | pf.setUserId(p.getUserId()); |
| | | pf.setDeptId(p.getDeptId()); |
| | | pf.setBizStatus(p.getBizStatus()); |
| | | pf.setSignTime(formatDate(p.getSignTime())); |
| | | pf.setExamScore(p.getExamScore()); |
| | | pf.setPassFlag(p.getPassFlag()); |
| | | pf.setGuestFlag(p.getGuestFlag()); |
| | | personForms.add(pf); |
| | | } |
| | | form.setPersonTasks(personForms); |
| | | form.setPersonCount(personForms.size()); |
| | | form.setFiles(listTaskFiles(id)); |
| | | return form; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsTaskForm form) { |
| | | // 空白新增仅允许临时培训;年/岗计划任务应由计划侧生成并带 source_id/item_id |
| | | boolean hasSourceLink = StringUtil.isNotEmpty(form.getSourceId()) |
| | | || StringUtil.isNotEmpty(form.getSourceItemId()); |
| | | if (!hasSourceLink) { |
| | | form.setSourceType("temp"); |
| | | form.setCategory("temp"); |
| | | } |
| | | validateForm(form, true); |
| | | TmsTaskEntity entity = new TmsTaskEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | entity.setTaskNo(nextTaskNo(PREFIX_DRAFT)); |
| | | entity.setPublishStatus(STATUS_DRAFT); |
| | | entity.setTaskKind(StringUtil.isNotEmpty(form.getTaskKind()) ? form.getTaskKind() : "new"); |
| | | fillEntity(entity, form, false); |
| | | applyDefaults(entity); |
| | | fillRefs(entity); |
| | | taskMapper.insert(entity); |
| | | saveTaskFiles(entity.getId(), form.getFiles()); |
| | | return entity.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsTaskForm form) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | if (!STATUS_DRAFT.equals(entity.getPublishStatus())) { |
| | | throw new DataException("仅未发布任务可编辑"); |
| | | } |
| | | boolean fromPlan = isPlanSourced(entity); |
| | | if (fromPlan) { |
| | | // 计划带出:来源链路只读,业务内容允许维护补全 |
| | | form.setCategory(entity.getCategory()); |
| | | form.setSourceType(entity.getSourceType()); |
| | | form.setSourcePlanNo(entity.getSourcePlanNo()); |
| | | form.setSourceId(entity.getSourceId()); |
| | | form.setSourceItemId(entity.getSourceItemId()); |
| | | } |
| | | validateForm(form, false); |
| | | fillEntity(entity, form, false); |
| | | applyDefaults(entity); |
| | | fillRefs(entity); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | taskMapper.updateById(entity); |
| | | if (form.getFiles() != null) { |
| | | saveTaskFiles(entity.getId(), form.getFiles()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(String id) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | if (!STATUS_DRAFT.equals(entity.getPublishStatus())) { |
| | | throw new DataException("仅未发布任务可删除"); |
| | | } |
| | | Long cnt = personTaskMapper.selectCount( |
| | | new LambdaQueryWrapper<TmsPersonTaskEntity>().eq(TmsPersonTaskEntity::getTaskId, id)); |
| | | if (cnt != null && cnt > 0) { |
| | | throw new DataException("已生成个人任务,不可删除"); |
| | | } |
| | | taskMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void publish(String id) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | if (!STATUS_DRAFT.equals(entity.getPublishStatus())) { |
| | | throw new DataException("仅未发布任务可发布"); |
| | | } |
| | | validateForPublish(entity); |
| | | |
| | | String taskNo = entity.getTaskNo(); |
| | | if (StringUtil.isEmpty(taskNo) || taskNo.startsWith(PREFIX_DRAFT)) { |
| | | taskNo = nextTaskNo(PREFIX_PUBLISH); |
| | | entity.setTaskNo(taskNo); |
| | | } |
| | | |
| | | List<String> userIds = splitTrainees(entity.getTrainees()); |
| | | if (userIds.isEmpty()) { |
| | | throw new DataException("培训对象不能为空"); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | BigDecimal hours = resolveTaskHours(entity); |
| | | for (String userId : userIds) { |
| | | TmsPersonTaskEntity pt = new TmsPersonTaskEntity(); |
| | | pt.setId(RandomUtil.uuId()); |
| | | pt.setTaskId(entity.getId()); |
| | | pt.setTaskNo(taskNo); |
| | | pt.setUserId(userId); |
| | | pt.setHours(hours); |
| | | pt.setBizStatus(PERSON_TODO); |
| | | pt.setGuestFlag("0"); |
| | | pt.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | pt.setCreatorTime(now); |
| | | personTaskMapper.insert(pt); |
| | | } |
| | | |
| | | Long recordCnt = recordMapper.selectCount( |
| | | new LambdaQueryWrapper<TmsRecordEntity>().eq(TmsRecordEntity::getTaskId, entity.getId())); |
| | | if (recordCnt == null || recordCnt == 0) { |
| | | TmsRecordEntity record = new TmsRecordEntity(); |
| | | record.setId(RandomUtil.uuId()); |
| | | record.setRecordNo(taskNo); |
| | | record.setTaskId(entity.getId()); |
| | | record.setTaskNo(taskNo); |
| | | record.setCategory(entity.getCategory()); |
| | | record.setSubject(entity.getSubject()); |
| | | record.setTeacherId(entity.getTeacherId()); |
| | | record.setTrainType(entity.getTrainType()); |
| | | record.setTrainMode(entity.getTrainMode()); |
| | | record.setEvalMode(entity.getEvalMode()); |
| | | record.setStartTime(entity.getStartTime()); |
| | | record.setEndTime(entity.getEndTime()); |
| | | record.setCloseTime(entity.getCloseTime()); |
| | | record.setArchiveStatus("pending"); |
| | | record.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | record.setCreatorTime(now); |
| | | recordMapper.insert(record); |
| | | } |
| | | |
| | | entity.setPublishStatus(STATUS_PUBLISHED); |
| | | entity.setPublishTime(now); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(now); |
| | | taskMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void cancel(String id) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | if (!STATUS_PUBLISHED.equals(entity.getPublishStatus())) { |
| | | throw new DataException("仅已发布任务可取消"); |
| | | } |
| | | Long signed = personTaskMapper.selectCount( |
| | | new LambdaQueryWrapper<TmsPersonTaskEntity>() |
| | | .eq(TmsPersonTaskEntity::getTaskId, id) |
| | | .and(w -> w.isNotNull(TmsPersonTaskEntity::getSignTime) |
| | | .or().in(TmsPersonTaskEntity::getBizStatus, PERSON_SIGNED, PERSON_DONE))); |
| | | if (signed != null && signed > 0) { |
| | | throw new DataException("已有人员签到,不可取消"); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | entity.setPublishStatus(STATUS_CANCELLED); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(now); |
| | | taskMapper.updateById(entity); |
| | | |
| | | personTaskMapper.update(null, new LambdaUpdateWrapper<TmsPersonTaskEntity>() |
| | | .eq(TmsPersonTaskEntity::getTaskId, id) |
| | | .set(TmsPersonTaskEntity::getBizStatus, PERSON_CANCELLED) |
| | | .set(TmsPersonTaskEntity::getLastModifyUserId, UserProvider.getLoginUserId()) |
| | | .set(TmsPersonTaskEntity::getLastModifyTime, now)); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void cancelBatch(List<String> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | throw new DataException("请选择要取消的任务"); |
| | | } |
| | | for (String id : ids) { |
| | | if (StringUtil.isNotEmpty(id)) { |
| | | cancel(id); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void restore(String id) { |
| | | TmsTaskEntity entity = requireOne(id); |
| | | if (!STATUS_CANCELLED.equals(entity.getPublishStatus())) { |
| | | throw new DataException("仅已取消任务可恢复"); |
| | | } |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | |
| | | // 逻辑删除已生成的个人任务与记录,便于再次编辑后发布 |
| | | List<TmsPersonTaskEntity> persons = personTaskMapper.selectList( |
| | | new LambdaQueryWrapper<TmsPersonTaskEntity>().eq(TmsPersonTaskEntity::getTaskId, id)); |
| | | for (TmsPersonTaskEntity p : persons) { |
| | | personTaskMapper.deleteById(p.getId()); |
| | | } |
| | | List<TmsRecordEntity> records = recordMapper.selectList( |
| | | new LambdaQueryWrapper<TmsRecordEntity>().eq(TmsRecordEntity::getTaskId, id)); |
| | | for (TmsRecordEntity r : records) { |
| | | recordMapper.deleteById(r.getId()); |
| | | } |
| | | |
| | | entity.setPublishStatus(STATUS_DRAFT); |
| | | entity.setPublishTime(null); |
| | | entity.setLastModifyUserId(userId); |
| | | entity.setLastModifyTime(now); |
| | | taskMapper.updateById(entity); |
| | | // 清空发布时间字段(updateById 可能跳过 null) |
| | | taskMapper.update(null, new LambdaUpdateWrapper<TmsTaskEntity>() |
| | | .eq(TmsTaskEntity::getId, id) |
| | | .set(TmsTaskEntity::getPublishTime, null)); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsFromPlanResult createFromAnnual(String planId) { |
| | | if (StringUtil.isEmpty(planId)) { |
| | | throw new DataException("年计划ID不能为空"); |
| | | } |
| | | String id = planId.trim(); |
| | | TmsAnnualPlanEntity plan = annualPlanMapper.selectById(id); |
| | | if (plan == null) { |
| | | throw new DataException("年度培训计划不存在: " + id); |
| | | } |
| | | List<TmsAnnualPlanItemEntity> items = annualPlanItemMapper.selectList( |
| | | new LambdaQueryWrapper<TmsAnnualPlanItemEntity>() |
| | | .eq(TmsAnnualPlanItemEntity::getForeignId, plan.getId()) |
| | | .orderByAsc(TmsAnnualPlanItemEntity::getTrainMonth)); |
| | | if (items.isEmpty()) { |
| | | throw new DataException("年度计划没有明细,无法生成任务"); |
| | | } |
| | | |
| | | TmsFromPlanResult result = new TmsFromPlanResult(); |
| | | result.setPlanId(plan.getId()); |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | |
| | | for (TmsAnnualPlanItemEntity item : items) { |
| | | TmsTaskEntity existed = findTaskBySourceItem(item.getId()); |
| | | if (existed != null) { |
| | | result.setSkipped(result.getSkipped() + 1); |
| | | if (StringUtil.isEmpty(item.getTaskId())) { |
| | | item.setTaskId(existed.getId()); |
| | | item.setLineStatus("tasked"); |
| | | annualPlanItemMapper.updateById(item); |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | Date[] window = monthWindow(parseYear(plan.getPlanYear()), item.getTrainMonth()); |
| | | TmsTaskEntity task = new TmsTaskEntity(); |
| | | task.setId(RandomUtil.uuId()); |
| | | task.setTaskNo(nextTaskNo(PREFIX_DRAFT)); |
| | | task.setPublishStatus(STATUS_DRAFT); |
| | | task.setTaskKind("new"); |
| | | task.setCategory("annual_plan"); |
| | | task.setSourceType("annual"); |
| | | task.setSourcePlanNo(plan.getPlanNo()); |
| | | task.setSourceId(plan.getId()); |
| | | task.setSourceItemId(item.getId()); |
| | | String subject = StringUtil.isNotEmpty(item.getTrainSubject()) ? item.getTrainSubject().trim() : "年度培训"; |
| | | task.setSubject(subject); |
| | | task.setTrainMode(item.getTrainMode()); |
| | | task.setEvalMode(item.getEvalMode()); |
| | | task.setTeacherId(resolveTeacherUserId(item.getTeacherId())); |
| | | task.setTrainees(normalizeTrainees(item.getTrainees())); |
| | | if (window != null) { |
| | | task.setStartTime(window[0]); |
| | | task.setEndTime(window[1]); |
| | | task.setCloseTime(window[1]); |
| | | } |
| | | task.setRetakeLimit(1); |
| | | task.setOverdueRemind("0"); |
| | | task.setInviteFlag("0"); |
| | | task.setCreatorUserId(userId); |
| | | task.setCreatorTime(now); |
| | | fillRefs(task); |
| | | taskMapper.insert(task); |
| | | |
| | | item.setTaskId(task.getId()); |
| | | item.setLineStatus("tasked"); |
| | | annualPlanItemMapper.updateById(item); |
| | | |
| | | result.setCreated(result.getCreated() + 1); |
| | | result.getTaskIds().add(task.getId()); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsFromPlanResult createFromPost(String planId) { |
| | | if (StringUtil.isEmpty(planId)) { |
| | | throw new DataException("岗位培训计划ID不能为空"); |
| | | } |
| | | String id = planId.trim(); |
| | | TmsPostPlanEntity plan = postPlanMapper.selectById(id); |
| | | if (plan == null) { |
| | | throw new DataException("岗位培训计划不存在: " + id); |
| | | } |
| | | List<TmsPostPlanItemEntity> items = postPlanItemMapper.selectList( |
| | | new LambdaQueryWrapper<TmsPostPlanItemEntity>() |
| | | .eq(TmsPostPlanItemEntity::getForeignId, plan.getId()) |
| | | .orderByAsc(TmsPostPlanItemEntity::getPlanTime)); |
| | | if (items.isEmpty()) { |
| | | throw new DataException("岗位培训计划没有明细,无法生成任务"); |
| | | } |
| | | |
| | | TmsFromPlanResult result = new TmsFromPlanResult(); |
| | | result.setPlanId(plan.getId()); |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | String trainees = normalizeTrainees(plan.getTrainees()); |
| | | |
| | | for (TmsPostPlanItemEntity item : items) { |
| | | TmsTaskEntity existed = findTaskBySourceItem(item.getId()); |
| | | if (existed != null) { |
| | | result.setSkipped(result.getSkipped() + 1); |
| | | if (StringUtil.isEmpty(item.getTaskId())) { |
| | | item.setTaskId(existed.getId()); |
| | | item.setLineStatus("tasked"); |
| | | postPlanItemMapper.updateById(item); |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | Date[] window = dayWindow(item.getPlanTime() != null ? item.getPlanTime() : plan.getPlanEndTime()); |
| | | TmsTaskEntity task = new TmsTaskEntity(); |
| | | task.setId(RandomUtil.uuId()); |
| | | task.setTaskNo(nextTaskNo(PREFIX_DRAFT)); |
| | | task.setPublishStatus(STATUS_DRAFT); |
| | | task.setTaskKind("new"); |
| | | task.setCategory("post_plan"); |
| | | task.setSourceType("post"); |
| | | task.setSourcePlanNo(plan.getPlanNo()); |
| | | task.setSourceId(plan.getId()); |
| | | task.setSourceItemId(item.getId()); |
| | | String subject = StringUtil.isNotEmpty(item.getSubject()) ? item.getSubject().trim() : null; |
| | | if (StringUtil.isEmpty(subject)) { |
| | | subject = StringUtil.isNotEmpty(plan.getPostName()) ? plan.getPostName() + "岗位培训" : "岗位培训"; |
| | | } |
| | | task.setSubject(subject); |
| | | task.setTrainMode(item.getTrainMode()); |
| | | task.setEvalMode(item.getEvalMode()); |
| | | task.setTeacherId(resolveTeacherUserId(item.getTeacherId())); |
| | | task.setTrainees(trainees); |
| | | task.setMaterialId(item.getMaterialId()); |
| | | if (window != null) { |
| | | task.setStartTime(window[0]); |
| | | task.setEndTime(window[1]); |
| | | task.setCloseTime(window[1]); |
| | | } |
| | | task.setRetakeLimit(1); |
| | | task.setOverdueRemind("0"); |
| | | task.setInviteFlag("0"); |
| | | task.setCreatorUserId(userId); |
| | | task.setCreatorTime(now); |
| | | fillRefs(task); |
| | | taskMapper.insert(task); |
| | | |
| | | item.setTaskId(task.getId()); |
| | | item.setLineStatus("tasked"); |
| | | postPlanItemMapper.updateById(item); |
| | | |
| | | result.setCreated(result.getCreated() + 1); |
| | | result.getTaskIds().add(task.getId()); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private TmsTaskEntity findTaskBySourceItem(String itemId) { |
| | | if (StringUtil.isEmpty(itemId)) { |
| | | return null; |
| | | } |
| | | return taskMapper.selectOne(new LambdaQueryWrapper<TmsTaskEntity>() |
| | | .eq(TmsTaskEntity::getSourceItemId, itemId) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | /** |
| | | * 年份:2026、2026-01-01…、或 13 位毫秒时间戳。 |
| | | * 在线表单日期控件会把年份存成日期字符串。 |
| | | */ |
| | | private static Integer parseYear(String raw) { |
| | | if (StringUtil.isEmpty(raw)) { |
| | | return null; |
| | | } |
| | | String text = raw.trim(); |
| | | try { |
| | | if (text.matches("\\d{4}")) { |
| | | return Integer.parseInt(text); |
| | | } |
| | | if (text.matches("\\d{13}")) { |
| | | return java.time.Instant.ofEpochMilli(Long.parseLong(text)) |
| | | .atZone(ZoneId.systemDefault()).getYear(); |
| | | } |
| | | if (text.length() >= 4 && text.substring(0, 4).matches("\\d{4}")) { |
| | | return Integer.parseInt(text.substring(0, 4)); |
| | | } |
| | | } catch (Exception ignored) { |
| | | return null; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 月份占位:精确时间在任务「执行安排」再改。支持 yyyyMM、1-12、yyyy-MM-dd、13 位毫秒时间戳。 */ |
| | | private static Date[] monthWindow(Integer planYear, String trainMonth) { |
| | | if (StringUtil.isEmpty(trainMonth)) { |
| | | return null; |
| | | } |
| | | String raw = trainMonth.trim(); |
| | | int year; |
| | | int month; |
| | | try { |
| | | if (raw.matches("\\d{13}")) { |
| | | java.time.LocalDate day = java.time.Instant.ofEpochMilli(Long.parseLong(raw)) |
| | | .atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | year = day.getYear(); |
| | | month = day.getMonthValue(); |
| | | } else if (raw.matches("\\d{4}-\\d{2}-\\d{2}.*")) { |
| | | year = Integer.parseInt(raw.substring(0, 4)); |
| | | month = Integer.parseInt(raw.substring(5, 7)); |
| | | } else if (raw.matches("\\d{6}")) { |
| | | year = Integer.parseInt(raw.substring(0, 4)); |
| | | month = Integer.parseInt(raw.substring(4, 6)); |
| | | } else if (raw.matches("\\d{1,2}")) { |
| | | if (planYear == null) { |
| | | return null; |
| | | } |
| | | year = planYear; |
| | | month = Integer.parseInt(raw); |
| | | } else { |
| | | return null; |
| | | } |
| | | if (month < 1 || month > 12) { |
| | | return null; |
| | | } |
| | | YearMonth ym = YearMonth.of(year, month); |
| | | ZoneId zone = ZoneId.systemDefault(); |
| | | Date start = Date.from(ym.atDay(1).atStartOfDay(zone).toInstant()); |
| | | Date end = Date.from(ym.atEndOfMonth().atTime(23, 59, 59).atZone(zone).toInstant()); |
| | | return new Date[]{start, end}; |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** 岗位计划明细是具体日期;没有明细日期时用计划结束日。 */ |
| | | private static Date[] dayWindow(Date day) { |
| | | if (day == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | java.time.LocalDate date = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | ZoneId zone = ZoneId.systemDefault(); |
| | | Date start = Date.from(date.atStartOfDay(zone).toInstant()); |
| | | Date end = Date.from(date.atTime(23, 59, 59).atZone(zone).toInstant()); |
| | | return new Date[]{start, end}; |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private static String normalizeTrainees(String raw) { |
| | | if (StringUtil.isEmpty(raw)) { |
| | | return null; |
| | | } |
| | | String text = raw.trim(); |
| | | if (text.startsWith("[") && text.endsWith("]")) { |
| | | text = text.substring(1, text.length() - 1).replace("\"", ""); |
| | | } |
| | | List<String> ids = splitTrainees(text); |
| | | return ids.isEmpty() ? text : joinTrainees(ids); |
| | | } |
| | | |
| | | private void validateForm(TmsTaskForm form, boolean creating) { |
| | | if (form == null) { |
| | | throw new DataException("表单不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getSubject())) { |
| | | throw new DataException("培训主题不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getCategory())) { |
| | | throw new DataException("培训分类不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getTrainMode())) { |
| | | throw new DataException("培训方式不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getSourceType())) { |
| | | throw new DataException("来源类型不能为空"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getEvalMode())) { |
| | | throw new DataException("考核方式不能为空"); |
| | | } |
| | | Date start = parseDate(form.getStartTime()); |
| | | Date end = parseDate(form.getEndTime()); |
| | | Date close = parseDate(form.getCloseTime()); |
| | | if (start == null || end == null || close == null) { |
| | | throw new DataException("开始/结束/关闭时间不能为空"); |
| | | } |
| | | if (start.after(end)) { |
| | | throw new DataException("开始时间不能晚于结束时间"); |
| | | } |
| | | if (end.after(close)) { |
| | | throw new DataException("结束时间不能晚于关闭时间"); |
| | | } |
| | | if (creating && start.before(new Date())) { |
| | | // 允许当天稍早几分钟;严格:开始不得早于当前日 00:00 |
| | | Calendar cal = Calendar.getInstance(); |
| | | cal.set(Calendar.HOUR_OF_DAY, 0); |
| | | cal.set(Calendar.MINUTE, 0); |
| | | cal.set(Calendar.SECOND, 0); |
| | | cal.set(Calendar.MILLISECOND, 0); |
| | | if (start.before(cal.getTime())) { |
| | | throw new DataException("开始时间不得早于当天"); |
| | | } |
| | | } |
| | | |
| | | String mode = form.getTrainMode(); |
| | | if ("onsite".equals(mode) || "practice".equals(mode)) { |
| | | if (!sameDay(start, end)) { |
| | | throw new DataException("集中/操作授课的开始与结束须为同一天"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getPlaceId()) && StringUtil.isEmpty(form.getPlaceName())) { |
| | | throw new DataException("现场类培训须填写地点"); |
| | | } |
| | | } |
| | | if ("online".equals(mode) && splitTrainees(form.getMaterialId()).isEmpty()) { |
| | | throw new DataException("在线学习须选择教材"); |
| | | } |
| | | if ("exam".equals(form.getEvalMode())) { |
| | | if (StringUtil.isEmpty(form.getPaperId())) { |
| | | throw new DataException("在线考试须选择试卷"); |
| | | } |
| | | Date examStart = parseDate(form.getExamStart()); |
| | | Date examEnd = parseDate(form.getExamEnd()); |
| | | if (examStart != null && examStart.before(start)) { |
| | | throw new DataException("考试开始不得早于培训开始"); |
| | | } |
| | | if (examStart != null && examEnd != null && examStart.after(examEnd)) { |
| | | throw new DataException("考试开始不能晚于考试结束"); |
| | | } |
| | | } |
| | | |
| | | List<String> trainees = resolveTraineeIds(form); |
| | | if (trainees.isEmpty()) { |
| | | throw new DataException("培训对象不能为空"); |
| | | } |
| | | |
| | | Integer retake = form.getRetakeLimit(); |
| | | if (retake != null && retake < 0) { |
| | | throw new DataException("补考次数不能为负数"); |
| | | } |
| | | } |
| | | |
| | | private void validateForPublish(TmsTaskEntity entity) { |
| | | if (StringUtil.isEmpty(entity.getSubject()) |
| | | || StringUtil.isEmpty(entity.getTrainMode()) |
| | | || StringUtil.isEmpty(entity.getEvalMode()) |
| | | || entity.getStartTime() == null |
| | | || entity.getEndTime() == null |
| | | || entity.getCloseTime() == null) { |
| | | throw new DataException("任务信息不完整,请先完善后发布"); |
| | | } |
| | | if (splitTrainees(entity.getTrainees()).isEmpty()) { |
| | | throw new DataException("培训对象不能为空"); |
| | | } |
| | | if ("exam".equals(entity.getEvalMode())) { |
| | | if (StringUtil.isEmpty(entity.getPaperId())) { |
| | | throw new DataException("在线考试须选择试卷"); |
| | | } |
| | | TmsPaperEntity paper = paperMapper.selectById(entity.getPaperId()); |
| | | if (paper == null) { |
| | | throw new DataException("所选试卷不存在"); |
| | | } |
| | | if ("invalid".equals(paper.getBizStatus())) { |
| | | throw new DataException("失效试卷不可用于发布"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void fillEntity(TmsTaskEntity entity, TmsTaskForm form, boolean keepPlanContent) { |
| | | if (!keepPlanContent) { |
| | | entity.setCategory(form.getCategory()); |
| | | entity.setTrainType(form.getTrainType()); |
| | | entity.setSourceType(form.getSourceType()); |
| | | entity.setSourcePlanNo(form.getSourcePlanNo()); |
| | | entity.setSourceId(form.getSourceId()); |
| | | entity.setSourceItemId(form.getSourceItemId()); |
| | | entity.setSubject(form.getSubject() == null ? null : form.getSubject().trim()); |
| | | entity.setKeyPoints(form.getKeyPoints()); |
| | | entity.setMaterialId(firstId(form.getMaterialId())); |
| | | entity.setMaterialNo(firstId(form.getMaterialNo())); |
| | | entity.setTeacherId(form.getTeacherId()); |
| | | entity.setTrainMode(form.getTrainMode()); |
| | | entity.setEvalMode(form.getEvalMode()); |
| | | entity.setTrainees(joinTrainees(resolveTraineeIds(form))); |
| | | if (StringUtil.isNotEmpty(form.getTaskKind())) { |
| | | entity.setTaskKind(form.getTaskKind()); |
| | | } |
| | | entity.setPrevTaskNo(form.getPrevTaskNo()); |
| | | } |
| | | // 执行信息:计划/临时均可维护 |
| | | entity.setPlaceId(form.getPlaceId()); |
| | | entity.setPlaceNo(form.getPlaceNo()); |
| | | entity.setPlaceName(form.getPlaceName()); |
| | | entity.setStartTime(parseDate(form.getStartTime())); |
| | | entity.setEndTime(parseDate(form.getEndTime())); |
| | | entity.setCloseTime(parseDate(form.getCloseTime())); |
| | | entity.setPaperId(form.getPaperId()); |
| | | entity.setPaperName(form.getPaperName()); |
| | | entity.setExamStart(parseDate(form.getExamStart())); |
| | | entity.setExamEnd(parseDate(form.getExamEnd())); |
| | | entity.setRetakeLimit(form.getRetakeLimit() == null ? 1 : form.getRetakeLimit()); |
| | | entity.setPassScore(form.getPassScore()); |
| | | entity.setQuizRatio(form.getQuizRatio()); |
| | | entity.setOverdueRemind(StringUtil.isEmpty(form.getOverdueRemind()) ? "0" : form.getOverdueRemind()); |
| | | entity.setInviteFlag(StringUtil.isEmpty(form.getInviteFlag()) ? "0" : form.getInviteFlag()); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private static boolean isPlanSourced(TmsTaskEntity entity) { |
| | | if (StringUtil.isNotEmpty(entity.getSourceId()) |
| | | || StringUtil.isNotEmpty(entity.getSourceItemId()) |
| | | || StringUtil.isNotEmpty(entity.getSourcePlanNo())) { |
| | | return true; |
| | | } |
| | | String st = entity.getSourceType(); |
| | | return StringUtil.isNotEmpty(st) && !"temp".equals(st); |
| | | } |
| | | |
| | | private void applyDefaults(TmsTaskEntity entity) { |
| | | if (entity.getRetakeLimit() == null) { |
| | | entity.setRetakeLimit(1); |
| | | } |
| | | if (StringUtil.isEmpty(entity.getOverdueRemind())) { |
| | | entity.setOverdueRemind("0"); |
| | | } |
| | | if (StringUtil.isEmpty(entity.getInviteFlag())) { |
| | | entity.setInviteFlag("0"); |
| | | } |
| | | if (StringUtil.isEmpty(entity.getTaskKind())) { |
| | | entity.setTaskKind("new"); |
| | | } |
| | | } |
| | | |
| | | private void fillRefs(TmsTaskEntity entity) { |
| | | if (StringUtil.isNotEmpty(entity.getPaperId())) { |
| | | TmsPaperEntity paper = paperMapper.selectById(entity.getPaperId()); |
| | | if (paper != null) { |
| | | entity.setPaperName(paper.getPaperName()); |
| | | if (entity.getPassScore() == null) { |
| | | entity.setPassScore(paper.getPassScore()); |
| | | } |
| | | } |
| | | } else { |
| | | entity.setPaperName(null); |
| | | } |
| | | if (StringUtil.isNotEmpty(entity.getMaterialId())) { |
| | | TmsMaterialEntity material = materialMapper.selectById(entity.getMaterialId()); |
| | | if (material != null && StringUtil.isNotEmpty(material.getMaterialNo())) { |
| | | entity.setMaterialNo(material.getMaterialNo()); |
| | | } |
| | | } else { |
| | | entity.setMaterialNo(null); |
| | | } |
| | | if (StringUtil.isNotEmpty(entity.getPlaceId())) { |
| | | TmsPlaceEntity place = placeMapper.selectById(entity.getPlaceId()); |
| | | if (place != null) { |
| | | entity.setPlaceNo(place.getPlaceNo()); |
| | | entity.setPlaceName(place.getPlaceName()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void saveTaskFiles(String taskId, List<TmsTaskFileForm> files) { |
| | | taskFileMapper.delete(new LambdaQueryWrapper<TmsTaskFileEntity>() |
| | | .eq(TmsTaskFileEntity::getForeignId, taskId)); |
| | | if (files == null || files.isEmpty()) { |
| | | return; |
| | | } |
| | | Date now = new Date(); |
| | | String userId = UserProvider.getLoginUserId(); |
| | | for (TmsTaskFileForm row : files) { |
| | | if (row == null || StringUtil.isEmpty(row.getFileName())) { |
| | | continue; |
| | | } |
| | | TmsTaskFileEntity entity = new TmsTaskFileEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | entity.setForeignId(taskId); |
| | | entity.setFileNo(row.getFileNo()); |
| | | entity.setFileName(row.getFileName().trim()); |
| | | entity.setMaterialId(row.getMaterialId()); |
| | | entity.setFileJson(row.getFileJson()); |
| | | entity.setCanDownload(StringUtil.isEmpty(row.getCanDownload()) ? "0" : row.getCanDownload()); |
| | | entity.setCreatorUserId(userId); |
| | | entity.setCreatorTime(now); |
| | | taskFileMapper.insert(entity); |
| | | } |
| | | } |
| | | |
| | | private List<TmsTaskFileForm> listTaskFiles(String taskId) { |
| | | List<TmsTaskFileEntity> rows = taskFileMapper.selectList( |
| | | new LambdaQueryWrapper<TmsTaskFileEntity>() |
| | | .eq(TmsTaskFileEntity::getForeignId, taskId) |
| | | .orderByAsc(TmsTaskFileEntity::getCreatorTime)); |
| | | List<TmsTaskFileForm> list = new ArrayList<>(rows.size()); |
| | | for (TmsTaskFileEntity row : rows) { |
| | | TmsTaskFileForm form = new TmsTaskFileForm(); |
| | | form.setId(row.getId()); |
| | | form.setFileNo(row.getFileNo()); |
| | | form.setFileName(row.getFileName()); |
| | | form.setMaterialId(row.getMaterialId()); |
| | | form.setFileJson(row.getFileJson()); |
| | | form.setCanDownload(row.getCanDownload()); |
| | | list.add(form); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private static String firstId(String raw) { |
| | | List<String> ids = splitTrainees(raw); |
| | | return ids.isEmpty() ? null : ids.get(0); |
| | | } |
| | | |
| | | private synchronized String nextTaskNo(String typePrefix) { |
| | | String ym = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM")); |
| | | String prefix = typePrefix + ym; |
| | | String max = taskMapper.selectMaxTaskNo(prefix); |
| | | int seq = 1; |
| | | if (StringUtil.isNotEmpty(max) && max.length() == 12) { |
| | | try { |
| | | seq = Integer.parseInt(max.substring(8)) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | seq = 1; |
| | | } |
| | | } |
| | | if (seq > 9999) { |
| | | throw new DataException("当月任务编号已用尽"); |
| | | } |
| | | return prefix + String.format("%04d", seq); |
| | | } |
| | | |
| | | /** |
| | | * 年计划子表 teacher_id 多为培训师档案 f_id;任务表单用 UserSelect,需落到系统用户。 |
| | | */ |
| | | private String resolveTeacherUserId(String teacherRef) { |
| | | if (StringUtil.isEmpty(teacherRef)) { |
| | | return null; |
| | | } |
| | | String raw = teacherRef.trim(); |
| | | TmsTrainerEntity trainer = trainerMapper.selectById(raw); |
| | | if (trainer != null && StringUtil.isNotEmpty(trainer.getUserId())) { |
| | | return trainer.getUserId(); |
| | | } |
| | | // 已是系统用户,或档案尚未关联用户 |
| | | return raw; |
| | | } |
| | | |
| | | private TmsTaskEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("任务ID不能为空"); |
| | | } |
| | | TmsTaskEntity entity = taskMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("培训任务不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private TmsTaskForm toForm(TmsTaskEntity entity, boolean detail) { |
| | | TmsTaskForm form = new TmsTaskForm(); |
| | | form.setId(entity.getId()); |
| | | form.setTaskNo(entity.getTaskNo()); |
| | | form.setCategory(entity.getCategory()); |
| | | form.setTrainType(entity.getTrainType()); |
| | | form.setSourceType(entity.getSourceType()); |
| | | form.setSourcePlanNo(entity.getSourcePlanNo()); |
| | | form.setSourceId(entity.getSourceId()); |
| | | form.setSourceItemId(entity.getSourceItemId()); |
| | | form.setSubject(entity.getSubject()); |
| | | form.setKeyPoints(entity.getKeyPoints()); |
| | | form.setMaterialId(entity.getMaterialId()); |
| | | form.setMaterialNo(entity.getMaterialNo()); |
| | | form.setPlaceId(entity.getPlaceId()); |
| | | form.setPlaceNo(entity.getPlaceNo()); |
| | | form.setPlaceName(entity.getPlaceName()); |
| | | form.setStartTime(formatDate(entity.getStartTime())); |
| | | form.setEndTime(formatDate(entity.getEndTime())); |
| | | form.setCloseTime(formatDate(entity.getCloseTime())); |
| | | form.setPaperId(entity.getPaperId()); |
| | | form.setPaperName(entity.getPaperName()); |
| | | form.setExamStart(formatDate(entity.getExamStart())); |
| | | form.setExamEnd(formatDate(entity.getExamEnd())); |
| | | form.setTeacherId(resolveTeacherUserId(entity.getTeacherId())); |
| | | form.setTrainMode(entity.getTrainMode()); |
| | | form.setEvalMode(entity.getEvalMode()); |
| | | form.setTrainees(entity.getTrainees()); |
| | | form.setTraineeIds(splitTrainees(entity.getTrainees())); |
| | | form.setTaskKind(entity.getTaskKind()); |
| | | form.setPrevTaskNo(entity.getPrevTaskNo()); |
| | | form.setPublishStatus(entity.getPublishStatus()); |
| | | form.setPublishTime(formatDate(entity.getPublishTime())); |
| | | form.setRetakeLimit(entity.getRetakeLimit()); |
| | | form.setPassScore(entity.getPassScore()); |
| | | form.setQuizRatio(entity.getQuizRatio()); |
| | | form.setOverdueRemind(entity.getOverdueRemind()); |
| | | form.setInviteFlag(entity.getInviteFlag()); |
| | | form.setRemark(entity.getRemark()); |
| | | form.setCreatorTime(formatDate(entity.getCreatorTime())); |
| | | form.setCreatorUserId(entity.getCreatorUserId()); |
| | | if (detail) { |
| | | // person tasks filled by caller |
| | | } |
| | | return form; |
| | | } |
| | | |
| | | private static List<String> resolveTraineeIds(TmsTaskForm form) { |
| | | Set<String> set = new LinkedHashSet<>(); |
| | | if (form.getTraineeIds() != null) { |
| | | for (String id : form.getTraineeIds()) { |
| | | if (StringUtil.isNotEmpty(id)) { |
| | | set.add(id.trim()); |
| | | } |
| | | } |
| | | } |
| | | if (set.isEmpty() && StringUtil.isNotEmpty(form.getTrainees())) { |
| | | set.addAll(splitTrainees(form.getTrainees())); |
| | | } |
| | | return new ArrayList<>(set); |
| | | } |
| | | |
| | | private static List<String> splitTrainees(String raw) { |
| | | if (StringUtil.isEmpty(raw)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return Arrays.stream(raw.split("[,;,;\\s]+")) |
| | | .map(String::trim) |
| | | .filter(s -> !s.isEmpty()) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private static String joinTrainees(List<String> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return null; |
| | | } |
| | | return ids.stream().filter(Objects::nonNull).map(String::trim).filter(s -> !s.isEmpty()) |
| | | .distinct().collect(Collectors.joining(",")); |
| | | } |
| | | |
| | | private static boolean sameDay(Date a, Date b) { |
| | | LocalDate da = a.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | LocalDate db = b.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | return da.equals(db); |
| | | } |
| | | |
| | | private static Date parseDate(String text) { |
| | | if (StringUtil.isEmpty(text)) { |
| | | return null; |
| | | } |
| | | String raw = text.trim(); |
| | | try { |
| | | if (raw.matches("^\\d{10,13}$")) { |
| | | long ts = Long.parseLong(raw); |
| | | if (raw.length() == 10) { |
| | | ts = ts * 1000L; |
| | | } |
| | | return new Date(ts); |
| | | } |
| | | return DateUtil.stringToDate(raw); |
| | | } catch (Exception ex) { |
| | | throw new DataException("时间格式无效:" + text); |
| | | } |
| | | } |
| | | |
| | | private static Date parseDayStart(String day) { |
| | | if (StringUtil.isEmpty(day) || "null".equalsIgnoreCase(day)) { |
| | | return null; |
| | | } |
| | | String raw = day.trim(); |
| | | if (raw.length() == 10) { |
| | | raw = raw + " 00:00:00"; |
| | | } |
| | | return parseDate(raw); |
| | | } |
| | | |
| | | private static Date parseDayEnd(String day) { |
| | | if (StringUtil.isEmpty(day) || "null".equalsIgnoreCase(day)) { |
| | | return null; |
| | | } |
| | | String raw = day.trim(); |
| | | if (raw.length() == 10) { |
| | | raw = raw + " 23:59:59"; |
| | | } |
| | | return parseDate(raw); |
| | | } |
| | | |
| | | private BigDecimal resolveTaskHours(TmsTaskEntity task) { |
| | | if (task == null || StringUtil.isEmpty(task.getSourceItemId())) { |
| | | return null; |
| | | } |
| | | if ("annual".equals(task.getSourceType())) { |
| | | TmsAnnualPlanItemEntity item = annualPlanItemMapper.selectById(task.getSourceItemId()); |
| | | return item == null ? null : item.getHours(); |
| | | } |
| | | if ("post".equals(task.getSourceType())) { |
| | | TmsPostPlanItemEntity item = postPlanItemMapper.selectById(task.getSourceItemId()); |
| | | return item == null ? null : item.getHours(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private static String formatDate(Date date) { |
| | | if (date == null) { |
| | | return null; |
| | | } |
| | | return DateUtil.dateToString(date, FMT); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import jnpf.base.UserInfo; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.OrganizeApi; |
| | | import jnpf.permission.PositionApi; |
| | | import jnpf.permission.entity.OrganizeEntity; |
| | | import jnpf.permission.entity.PositionEntity; |
| | | import jnpf.tmsEntity.TmsPersonFileEntity; |
| | | import jnpf.tmsEntity.TmsPersonTrainEntity; |
| | | import jnpf.tmsEntity.TmsPersonWorkEntity; |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveCatalogVO; |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveDetailVO; |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveProfileVO; |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveWorkVO; |
| | | import jnpf.tmsMapper.TmsPersonFileMapper; |
| | | import jnpf.tmsMapper.TmsPersonTrainMapper; |
| | | import jnpf.tmsMapper.TmsPersonWorkMapper; |
| | | import jnpf.tmsService.TmsTrainArchiveService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Calendar; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainArchiveServiceImpl implements TmsTrainArchiveService { |
| | | |
| | | private final TmsPersonFileMapper personFileMapper; |
| | | private final TmsPersonWorkMapper personWorkMapper; |
| | | private final TmsPersonTrainMapper personTrainMapper; |
| | | private final OrganizeApi organizeApi; |
| | | private final PositionApi positionApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public TmsTrainArchiveDetailVO getMine() { |
| | | UserInfo user = UserProvider.getUser(); |
| | | if (user == null || StringUtil.isEmpty(user.getUserId())) { |
| | | throw new DataException("未获取到登录用户"); |
| | | } |
| | | String userId = user.getUserId(); |
| | | TmsPersonFileEntity file = findByUserId(userId); |
| | | if (file == null) { |
| | | file = createArchive(user); |
| | | } |
| | | |
| | | TmsTrainArchiveDetailVO detail = new TmsTrainArchiveDetailVO(); |
| | | detail.setProfile(toProfile(file, user)); |
| | | detail.setWorkList(loadWorkList(file.getId())); |
| | | detail.setCatalogList(loadCatalogList(file.getId(), userId)); |
| | | return detail; |
| | | } |
| | | |
| | | private TmsPersonFileEntity findByUserId(String userId) { |
| | | LambdaQueryWrapper<TmsPersonFileEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPersonFileEntity::getUserId, userId); |
| | | qw.last("LIMIT 1"); |
| | | return personFileMapper.selectOne(qw); |
| | | } |
| | | |
| | | private TmsPersonFileEntity createArchive(UserInfo user) { |
| | | Date now = new Date(); |
| | | TmsPersonFileEntity entity = new TmsPersonFileEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | entity.setArchiveNo(nextArchiveNo()); |
| | | entity.setArchiveDate(now); |
| | | entity.setUserId(user.getUserId()); |
| | | entity.setUserName(user.getUserName()); |
| | | entity.setDeptId(user.getOrganizeId()); |
| | | List<String> positionIds = normalizeIds(user.getPositionIds()); |
| | | if (!positionIds.isEmpty()) { |
| | | entity.setMainPostId(positionIds.get(0)); |
| | | if (positionIds.size() > 1) { |
| | | entity.setPartPostId(positionIds.get(1)); |
| | | } |
| | | } |
| | | entity.setBizStatus("valid"); |
| | | entity.setCreatorUserId(user.getUserId()); |
| | | entity.setCreatorTime(now); |
| | | personFileMapper.insert(entity); |
| | | return entity; |
| | | } |
| | | |
| | | private List<String> normalizeIds(Object raw) { |
| | | if (raw == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (raw instanceof List) { |
| | | List<?> list = (List<?>) raw; |
| | | List<String> out = new ArrayList<>(); |
| | | for (Object o : list) { |
| | | if (o != null && StringUtil.isNotEmpty(String.valueOf(o))) { |
| | | out.add(String.valueOf(o)); |
| | | } |
| | | } |
| | | return out; |
| | | } |
| | | if (raw instanceof String[]) { |
| | | List<String> out = new ArrayList<>(); |
| | | for (String s : (String[]) raw) { |
| | | if (StringUtil.isNotEmpty(s)) { |
| | | out.add(s); |
| | | } |
| | | } |
| | | return out; |
| | | } |
| | | if (raw instanceof String && StringUtil.isNotEmpty((String) raw)) { |
| | | return Collections.singletonList((String) raw); |
| | | } |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | /** 档案编号:AR + 年份 + 4 位流水 */ |
| | | private String nextArchiveNo() { |
| | | int year = Calendar.getInstance().get(Calendar.YEAR); |
| | | String prefix = "AR" + year; |
| | | LambdaQueryWrapper<TmsPersonFileEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.likeRight(TmsPersonFileEntity::getArchiveNo, prefix); |
| | | qw.orderByDesc(TmsPersonFileEntity::getArchiveNo); |
| | | qw.last("LIMIT 1"); |
| | | TmsPersonFileEntity last = personFileMapper.selectOne(qw); |
| | | int seq = 1; |
| | | if (last != null && StringUtil.isNotEmpty(last.getArchiveNo()) && last.getArchiveNo().length() >= prefix.length() + 4) { |
| | | try { |
| | | seq = Integer.parseInt(last.getArchiveNo().substring(prefix.length())) + 1; |
| | | } catch (NumberFormatException ignored) { |
| | | seq = 1; |
| | | } |
| | | } |
| | | return prefix + String.format("%04d", Math.min(seq, 9999)); |
| | | } |
| | | |
| | | private TmsTrainArchiveProfileVO toProfile(TmsPersonFileEntity file, UserInfo user) { |
| | | TmsTrainArchiveProfileVO vo = new TmsTrainArchiveProfileVO(); |
| | | vo.setId(file.getId()); |
| | | vo.setArchiveNo(file.getArchiveNo()); |
| | | vo.setArchiveDate(file.getArchiveDate() == null ? null : DateUtil.dateToString(file.getArchiveDate(), "yyyy-MM-dd")); |
| | | vo.setUserId(file.getUserId()); |
| | | vo.setUserName(StringUtil.isNotEmpty(file.getUserName()) ? file.getUserName() : user.getUserName()); |
| | | vo.setDeptName(resolveDeptName(file.getDeptId(), user)); |
| | | vo.setMainPostName(resolvePostName(file.getMainPostId(), user, true)); |
| | | vo.setPartPostName(resolvePostName(file.getPartPostId(), user, false)); |
| | | return vo; |
| | | } |
| | | |
| | | private String resolveDeptName(String deptId, UserInfo user) { |
| | | if (StringUtil.isNotEmpty(deptId)) { |
| | | try { |
| | | OrganizeEntity org = organizeApi.getInfoById(deptId); |
| | | if (org != null && StringUtil.isNotEmpty(org.getFullName())) { |
| | | return org.getFullName(); |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve dept name failed: {}", e.getMessage()); |
| | | } |
| | | } |
| | | if (user != null && StringUtil.isNotEmpty(user.getOrganize())) { |
| | | return user.getOrganize(); |
| | | } |
| | | return "无"; |
| | | } |
| | | |
| | | private String resolvePostName(String postId, UserInfo user, boolean main) { |
| | | if (StringUtil.isNotEmpty(postId)) { |
| | | try { |
| | | PositionEntity pos = positionApi.queryInfoById(postId); |
| | | if (pos != null && StringUtil.isNotEmpty(pos.getFullName())) { |
| | | return pos.getFullName(); |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve post name failed: {}", e.getMessage()); |
| | | } |
| | | } |
| | | if (main && user != null) { |
| | | List<String> positionIds = normalizeIds(user.getPositionIds()); |
| | | if (!positionIds.isEmpty()) { |
| | | try { |
| | | List<PositionEntity> list = positionApi.getListByIds(positionIds); |
| | | if (list != null && !list.isEmpty() && StringUtil.isNotEmpty(list.get(0).getFullName())) { |
| | | return list.get(0).getFullName(); |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve main post from user failed: {}", e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | return "无"; |
| | | } |
| | | |
| | | private List<TmsTrainArchiveWorkVO> loadWorkList(String archiveId) { |
| | | LambdaQueryWrapper<TmsPersonWorkEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPersonWorkEntity::getForeignId, archiveId); |
| | | qw.orderByAsc(TmsPersonWorkEntity::getHireDate); |
| | | List<TmsPersonWorkEntity> rows = personWorkMapper.selectList(qw); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<TmsTrainArchiveWorkVO> list = new ArrayList<>(rows.size()); |
| | | for (TmsPersonWorkEntity row : rows) { |
| | | TmsTrainArchiveWorkVO vo = new TmsTrainArchiveWorkVO(); |
| | | vo.setId(row.getId()); |
| | | vo.setCompanyName(row.getCompanyName()); |
| | | vo.setPostName(row.getPostName()); |
| | | vo.setHireDate(row.getHireDate() == null ? null : DateUtil.dateToString(row.getHireDate(), "yyyy-MM-dd")); |
| | | vo.setLeaveDate(row.getLeaveDate() == null ? null : DateUtil.dateToString(row.getLeaveDate(), "yyyy-MM-dd")); |
| | | list.add(vo); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private List<TmsTrainArchiveCatalogVO> loadCatalogList(String archiveId, String userId) { |
| | | LambdaQueryWrapper<TmsPersonTrainEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.and(w -> w.eq(TmsPersonTrainEntity::getArchiveId, archiveId) |
| | | .or() |
| | | .eq(TmsPersonTrainEntity::getUserId, userId)); |
| | | qw.orderByDesc(TmsPersonTrainEntity::getStartTime); |
| | | List<TmsPersonTrainEntity> rows = personTrainMapper.selectList(qw); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | // 去重:同一 id 只保留一条 |
| | | return rows.stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(TmsPersonTrainEntity::getId, r -> r, (a, b) -> a)) |
| | | .values() |
| | | .stream() |
| | | .sorted((a, b) -> { |
| | | Date ta = a.getStartTime(); |
| | | Date tb = b.getStartTime(); |
| | | if (ta == null && tb == null) return 0; |
| | | if (ta == null) return 1; |
| | | if (tb == null) return -1; |
| | | return tb.compareTo(ta); |
| | | }) |
| | | .map(this::toCatalog) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private TmsTrainArchiveCatalogVO toCatalog(TmsPersonTrainEntity row) { |
| | | TmsTrainArchiveCatalogVO vo = new TmsTrainArchiveCatalogVO(); |
| | | vo.setId(row.getId()); |
| | | vo.setRecordNo(row.getRecordNo()); |
| | | vo.setTrainType(row.getCategory()); |
| | | vo.setTrainContent(row.getSubject()); |
| | | vo.setTrainMode(row.getTrainMode()); |
| | | vo.setTrainerName(row.getTeacherId()); |
| | | vo.setEvalMode(row.getEvalMode()); |
| | | vo.setPassFlag(row.getPassFlag() == null ? "" : row.getPassFlag()); |
| | | vo.setTrainStart(row.getStartTime() == null ? null : DateUtil.dateToString(row.getStartTime(), "yyyy-MM-dd HH:mm")); |
| | | vo.setTrainEnd(row.getEndTime() == null ? null : DateUtil.dateToString(row.getEndTime(), "yyyy-MM-dd HH:mm")); |
| | | return vo; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.tmsEntity.TmsTrainModeEntity; |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeForm; |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeQuery; |
| | | import jnpf.tmsMapper.TmsTrainModeMapper; |
| | | import jnpf.tmsService.TmsTrainModeService; |
| | | import jnpf.util.RandomUtil; |
| | | import jnpf.util.StringUtil; |
| | | import jnpf.util.UserProvider; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainModeServiceImpl implements TmsTrainModeService { |
| | | |
| | | private final TmsTrainModeMapper trainModeMapper; |
| | | |
| | | @Override |
| | | public List<TmsTrainModeForm> getList(TmsTrainModeQuery query) { |
| | | LambdaQueryWrapper<TmsTrainModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getEnabled())) { |
| | | qw.eq(TmsTrainModeEntity::getEnabled, query.getEnabled()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsTrainModeEntity::getModeCode, kw) |
| | | .or() |
| | | .like(TmsTrainModeEntity::getModeName, kw)); |
| | | } |
| | | qw.orderByAsc(TmsTrainModeEntity::getSortNo); |
| | | qw.orderByDesc(TmsTrainModeEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsTrainModeEntity> page = trainModeMapper.selectPage(new Page<>(current, size), qw); |
| | | |
| | | List<TmsTrainModeForm> list = new ArrayList<>(page.getRecords().size()); |
| | | for (TmsTrainModeEntity row : page.getRecords()) { |
| | | list.add(toForm(row)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsTrainModeForm getInfo(String id) { |
| | | TmsTrainModeEntity entity = requireOne(id); |
| | | return toForm(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String create(TmsTrainModeForm form) { |
| | | validateForm(form, null); |
| | | Date now = new Date(); |
| | | TmsTrainModeEntity entity = new TmsTrainModeEntity(); |
| | | entity.setId(RandomUtil.uuId()); |
| | | applyForm(entity, form); |
| | | entity.setCreatorUserId(UserProvider.getLoginUserId()); |
| | | entity.setCreatorTime(now); |
| | | trainModeMapper.insert(entity); |
| | | return entity.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(String id, TmsTrainModeForm form) { |
| | | TmsTrainModeEntity entity = requireOne(id); |
| | | validateForm(form, id); |
| | | // 编码不允许改 |
| | | form.setModeCode(entity.getModeCode()); |
| | | applyForm(entity, form); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | trainModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void setEnabled(String id, String enabled) { |
| | | if (!"0".equals(enabled) && !"1".equals(enabled)) { |
| | | throw new DataException("启用状态无效"); |
| | | } |
| | | TmsTrainModeEntity entity = requireOne(id); |
| | | entity.setEnabled(enabled); |
| | | entity.setLastModifyUserId(UserProvider.getLoginUserId()); |
| | | entity.setLastModifyTime(new Date()); |
| | | trainModeMapper.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(String id) { |
| | | TmsTrainModeEntity entity = requireOne(id); |
| | | if (!"0".equals(entity.getEnabled())) { |
| | | throw new DataException("请先停用后再删除"); |
| | | } |
| | | trainModeMapper.deleteById(id); |
| | | } |
| | | |
| | | private TmsTrainModeEntity requireOne(String id) { |
| | | if (StringUtil.isEmpty(id)) { |
| | | throw new DataException("培训方式不存在"); |
| | | } |
| | | TmsTrainModeEntity entity = trainModeMapper.selectById(id); |
| | | if (entity == null) { |
| | | throw new DataException("培训方式不存在"); |
| | | } |
| | | return entity; |
| | | } |
| | | |
| | | private void validateForm(TmsTrainModeForm form, String excludeId) { |
| | | if (form == null || StringUtil.isEmpty(form.getModeCode())) { |
| | | throw new DataException("请填写方式编码"); |
| | | } |
| | | if (StringUtil.isEmpty(form.getModeName())) { |
| | | throw new DataException("请填写方式名称"); |
| | | } |
| | | String code = form.getModeCode().trim(); |
| | | form.setModeCode(code); |
| | | LambdaQueryWrapper<TmsTrainModeEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsTrainModeEntity::getModeCode, code); |
| | | if (StringUtil.isNotEmpty(excludeId)) { |
| | | qw.ne(TmsTrainModeEntity::getId, excludeId); |
| | | } |
| | | if (trainModeMapper.selectCount(qw) > 0) { |
| | | throw new DataException("方式编码已存在"); |
| | | } |
| | | } |
| | | |
| | | private void applyForm(TmsTrainModeEntity entity, TmsTrainModeForm form) { |
| | | entity.setModeCode(form.getModeCode().trim()); |
| | | entity.setModeName(form.getModeName().trim()); |
| | | entity.setSignRule(StringUtil.isNotEmpty(form.getSignRule()) ? form.getSignRule() : "both"); |
| | | entity.setAllowGuestSign(yn(form.getAllowGuestSign(), "0")); |
| | | entity.setSameDayRequired(yn(form.getSameDayRequired(), "0")); |
| | | entity.setPlaceRequired(yn(form.getPlaceRequired(), "1")); |
| | | entity.setSortNo(form.getSortNo() == null ? 0 : form.getSortNo()); |
| | | entity.setEnabled(yn(form.getEnabled(), "1")); |
| | | entity.setRemark(form.getRemark()); |
| | | } |
| | | |
| | | private static String yn(String v, String def) { |
| | | if ("0".equals(v) || "1".equals(v)) { |
| | | return v; |
| | | } |
| | | return def; |
| | | } |
| | | |
| | | private TmsTrainModeForm toForm(TmsTrainModeEntity entity) { |
| | | TmsTrainModeForm form = new TmsTrainModeForm(); |
| | | form.setId(entity.getId()); |
| | | form.setModeCode(entity.getModeCode()); |
| | | form.setModeName(entity.getModeName()); |
| | | form.setSignRule(entity.getSignRule()); |
| | | form.setAllowGuestSign(entity.getAllowGuestSign()); |
| | | form.setSameDayRequired(entity.getSameDayRequired()); |
| | | form.setPlaceRequired(entity.getPlaceRequired()); |
| | | form.setSortNo(entity.getSortNo()); |
| | | form.setEnabled(entity.getEnabled()); |
| | | form.setRemark(entity.getRemark()); |
| | | return form; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsService.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import jnpf.exception.DataException; |
| | | import jnpf.permission.OrganizeApi; |
| | | import jnpf.permission.PositionApi; |
| | | import jnpf.permission.UserApi; |
| | | import jnpf.permission.entity.OrganizeEntity; |
| | | import jnpf.permission.entity.PositionEntity; |
| | | import jnpf.permission.entity.UserEntity; |
| | | import jnpf.tmsEntity.TmsPersonFileEntity; |
| | | import jnpf.tmsEntity.TmsPersonTaskEntity; |
| | | import jnpf.tmsEntity.TmsPersonTrainEntity; |
| | | import jnpf.tmsEntity.TmsTaskEntity; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordCatalogItemVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordCatalogVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordListVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordQuery; |
| | | import jnpf.tmsMapper.TmsPersonFileMapper; |
| | | import jnpf.tmsMapper.TmsPersonTaskMapper; |
| | | import jnpf.tmsMapper.TmsPersonTrainMapper; |
| | | import jnpf.tmsMapper.TmsTaskMapper; |
| | | import jnpf.tmsService.TmsTrainRecordService; |
| | | import jnpf.util.DateUtil; |
| | | import jnpf.util.StringUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainRecordServiceImpl implements TmsTrainRecordService { |
| | | |
| | | private static final String DATE_FMT = "yyyy-MM-dd"; |
| | | |
| | | private final TmsPersonFileMapper personFileMapper; |
| | | private final TmsPersonTrainMapper personTrainMapper; |
| | | private final TmsPersonTaskMapper personTaskMapper; |
| | | private final TmsTaskMapper taskMapper; |
| | | private final OrganizeApi organizeApi; |
| | | private final PositionApi positionApi; |
| | | private final UserApi userApi; |
| | | |
| | | @Override |
| | | public List<TmsTrainRecordListVO> getList(TmsTrainRecordQuery query) { |
| | | LambdaQueryWrapper<TmsPersonFileEntity> qw = new LambdaQueryWrapper<>(); |
| | | if (StringUtil.isNotEmpty(query.getArchiveNo()) && !"null".equalsIgnoreCase(query.getArchiveNo())) { |
| | | qw.like(TmsPersonFileEntity::getArchiveNo, query.getArchiveNo().trim()); |
| | | } |
| | | if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) { |
| | | String kw = query.getKeyword().trim(); |
| | | qw.and(w -> w.like(TmsPersonFileEntity::getUserName, kw) |
| | | .or().like(TmsPersonFileEntity::getArchiveNo, kw) |
| | | .or().like(TmsPersonFileEntity::getDeptId, kw)); |
| | | } |
| | | qw.orderByDesc(TmsPersonFileEntity::getArchiveDate) |
| | | .orderByDesc(TmsPersonFileEntity::getCreatorTime); |
| | | |
| | | long current = query.getCurrentPage() > 0 ? query.getCurrentPage() : 1; |
| | | long size = query.getPageSize() > 0 ? query.getPageSize() : 20; |
| | | Page<TmsPersonFileEntity> page = personFileMapper.selectPage(new Page<>(current, size), qw); |
| | | List<TmsPersonFileEntity> rows = page.getRecords(); |
| | | if (rows == null || rows.isEmpty()) { |
| | | query.setData(Collections.emptyList(), page.getTotal()); |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | Map<String, String> deptNames = loadDeptNames(rows.stream() |
| | | .map(TmsPersonFileEntity::getDeptId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .collect(Collectors.toSet())); |
| | | Map<String, String> postNames = loadPostNames(rows); |
| | | |
| | | List<TmsTrainRecordListVO> list = new ArrayList<>(rows.size()); |
| | | for (TmsPersonFileEntity row : rows) { |
| | | list.add(toListVo(row, deptNames, postNames)); |
| | | } |
| | | query.setData(list, page.getTotal()); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public TmsTrainRecordCatalogVO getCatalog(String archiveId, String startDate, String endDate) { |
| | | if (StringUtil.isEmpty(archiveId)) { |
| | | throw new DataException("档案ID不能为空"); |
| | | } |
| | | TmsPersonFileEntity file = personFileMapper.selectById(archiveId); |
| | | if (file == null) { |
| | | throw new DataException("个人培训记录不存在"); |
| | | } |
| | | |
| | | List<TmsTrainRecordCatalogItemVO> items = new ArrayList<>(); |
| | | Set<String> seenNos = new HashSet<>(); |
| | | |
| | | // 1) 档案目录表(若已同步) |
| | | for (TmsPersonTrainEntity row : loadPersonTrains(file)) { |
| | | TmsTrainRecordCatalogItemVO item = fromPersonTrain(row); |
| | | if (item == null || !inDateRange(item.getTrainDate(), startDate, endDate)) { |
| | | continue; |
| | | } |
| | | if (StringUtil.isNotEmpty(item.getRecordNo())) { |
| | | seenNos.add(item.getRecordNo()); |
| | | } |
| | | items.add(item); |
| | | } |
| | | |
| | | // 2) 个人培训任务(联调/日常主数据源) |
| | | for (TmsTrainRecordCatalogItemVO item : loadFromPersonTasks(file.getUserId())) { |
| | | if (item == null || !inDateRange(item.getTrainDate(), startDate, endDate)) { |
| | | continue; |
| | | } |
| | | if (StringUtil.isNotEmpty(item.getRecordNo()) && seenNos.contains(item.getRecordNo())) { |
| | | continue; |
| | | } |
| | | if (StringUtil.isNotEmpty(item.getRecordNo())) { |
| | | seenNos.add(item.getRecordNo()); |
| | | } |
| | | items.add(item); |
| | | } |
| | | |
| | | items.sort(Comparator.comparing(TmsTrainRecordCatalogItemVO::getTrainDate, |
| | | Comparator.nullsLast(Comparator.reverseOrder()))); |
| | | |
| | | TmsTrainRecordCatalogVO vo = new TmsTrainRecordCatalogVO(); |
| | | vo.setId(file.getId()); |
| | | vo.setUserName(file.getUserName()); |
| | | vo.setArchiveNo(file.getArchiveNo()); |
| | | vo.setList(items); |
| | | return vo; |
| | | } |
| | | |
| | | private List<TmsPersonTrainEntity> loadPersonTrains(TmsPersonFileEntity file) { |
| | | LambdaQueryWrapper<TmsPersonTrainEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.and(w -> w.eq(TmsPersonTrainEntity::getArchiveId, file.getId()) |
| | | .or() |
| | | .eq(TmsPersonTrainEntity::getUserId, file.getUserId())); |
| | | qw.orderByDesc(TmsPersonTrainEntity::getStartTime); |
| | | List<TmsPersonTrainEntity> rows = personTrainMapper.selectList(qw); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return new ArrayList<>(rows.stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toMap(TmsPersonTrainEntity::getId, r -> r, (a, b) -> a, LinkedHashMap::new)) |
| | | .values()); |
| | | } |
| | | |
| | | private List<TmsTrainRecordCatalogItemVO> loadFromPersonTasks(String userId) { |
| | | if (StringUtil.isEmpty(userId)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | LambdaQueryWrapper<TmsPersonTaskEntity> qw = new LambdaQueryWrapper<>(); |
| | | qw.eq(TmsPersonTaskEntity::getUserId, userId); |
| | | qw.ne(TmsPersonTaskEntity::getBizStatus, "cancelled"); |
| | | qw.orderByDesc(TmsPersonTaskEntity::getCreatorTime); |
| | | List<TmsPersonTaskEntity> pts = personTaskMapper.selectList(qw); |
| | | if (pts == null || pts.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | Set<String> taskIds = pts.stream() |
| | | .map(TmsPersonTaskEntity::getTaskId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .collect(Collectors.toSet()); |
| | | Map<String, TmsTaskEntity> taskMap = new HashMap<>(); |
| | | if (!taskIds.isEmpty()) { |
| | | for (TmsTaskEntity t : taskMapper.selectBatchIds(taskIds)) { |
| | | if (t != null) { |
| | | taskMap.put(t.getId(), t); |
| | | } |
| | | } |
| | | } |
| | | |
| | | Set<String> teacherIds = taskMap.values().stream() |
| | | .map(TmsTaskEntity::getTeacherId) |
| | | .filter(StringUtil::isNotEmpty) |
| | | .flatMap(s -> splitIds(s).stream()) |
| | | .collect(Collectors.toSet()); |
| | | Map<String, String> teacherNames = loadUserNames(teacherIds); |
| | | |
| | | List<TmsTrainRecordCatalogItemVO> list = new ArrayList<>(pts.size()); |
| | | for (TmsPersonTaskEntity pt : pts) { |
| | | TmsTaskEntity task = taskMap.get(pt.getTaskId()); |
| | | TmsTrainRecordCatalogItemVO item = new TmsTrainRecordCatalogItemVO(); |
| | | item.setId(pt.getId()); |
| | | item.setRecordNo(StringUtil.isNotEmpty(pt.getTaskNo()) |
| | | ? pt.getTaskNo() |
| | | : (task == null ? "" : task.getTaskNo())); |
| | | if (task != null) { |
| | | item.setTrainType(labelCategory(firstNonEmpty(task.getCategory(), task.getTrainType()))); |
| | | item.setTrainContent(task.getSubject()); |
| | | item.setTrainMode(labelTrainMode(task.getTrainMode())); |
| | | item.setEvalMode(labelEvalMode(task.getEvalMode())); |
| | | item.setTrainerName(resolveTeacherNames(task.getTeacherId(), teacherNames)); |
| | | item.setTrainDate(fmtDate(task.getStartTime())); |
| | | } else { |
| | | item.setTrainType(""); |
| | | item.setTrainContent(pt.getTaskNo()); |
| | | item.setTrainMode(""); |
| | | item.setEvalMode(""); |
| | | item.setTrainerName(""); |
| | | item.setTrainDate(fmtDate(pt.getSignTime() != null ? pt.getSignTime() : pt.getCreatorTime())); |
| | | } |
| | | if (pt.getExamScore() != null) { |
| | | item.setTrainResult(pt.getExamScore().stripTrailingZeros()); |
| | | } else { |
| | | item.setTrainResult(""); |
| | | } |
| | | item.setPassFlag(pt.getPassFlag() == null ? "" : pt.getPassFlag()); |
| | | list.add(item); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private TmsTrainRecordCatalogItemVO fromPersonTrain(TmsPersonTrainEntity row) { |
| | | TmsTrainRecordCatalogItemVO item = new TmsTrainRecordCatalogItemVO(); |
| | | item.setId(row.getId()); |
| | | item.setRecordNo(row.getRecordNo()); |
| | | item.setTrainType(labelCategory(row.getCategory())); |
| | | item.setTrainContent(row.getSubject()); |
| | | item.setTrainMode(labelTrainMode(row.getTrainMode())); |
| | | item.setEvalMode(labelEvalMode(row.getEvalMode())); |
| | | item.setTrainerName(row.getTeacherId()); |
| | | item.setTrainResult(""); |
| | | item.setPassFlag(row.getPassFlag() == null ? "" : row.getPassFlag()); |
| | | item.setTrainDate(fmtDate(row.getStartTime())); |
| | | return item; |
| | | } |
| | | |
| | | private TmsTrainRecordListVO toListVo(TmsPersonFileEntity row, |
| | | Map<String, String> deptNames, |
| | | Map<String, String> postNames) { |
| | | TmsTrainRecordListVO vo = new TmsTrainRecordListVO(); |
| | | vo.setId(row.getId()); |
| | | vo.setArchiveNo(row.getArchiveNo()); |
| | | Date post = row.getHireDate() != null ? row.getHireDate() : row.getArchiveDate(); |
| | | vo.setPostDate(fmtDate(post)); |
| | | vo.setUserName(row.getUserName()); |
| | | vo.setDeptName(deptNames.getOrDefault(row.getDeptId(), "")); |
| | | vo.setPostName(postNames.getOrDefault(row.getMainPostId(), "")); |
| | | vo.setPartPostName(postNames.getOrDefault(row.getPartPostId(), "")); |
| | | vo.setMajor(""); |
| | | vo.setEducation(""); |
| | | vo.setTechTitle(""); |
| | | vo.setHireDate(fmtDate(row.getHireDate())); |
| | | vo.setLeaveDate(fmtDate(row.getLeaveDate())); |
| | | return vo; |
| | | } |
| | | |
| | | private Map<String, String> loadDeptNames(Set<String> deptIds) { |
| | | Map<String, String> map = new HashMap<>(); |
| | | if (deptIds == null || deptIds.isEmpty()) { |
| | | return map; |
| | | } |
| | | for (String id : deptIds) { |
| | | try { |
| | | OrganizeEntity org = organizeApi.getInfoById(id); |
| | | if (org != null && StringUtil.isNotEmpty(org.getFullName())) { |
| | | map.put(id, org.getFullName()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve dept failed: {}", e.getMessage()); |
| | | } |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, String> loadPostNames(List<TmsPersonFileEntity> rows) { |
| | | Set<String> ids = new HashSet<>(); |
| | | for (TmsPersonFileEntity row : rows) { |
| | | if (StringUtil.isNotEmpty(row.getMainPostId())) { |
| | | ids.add(row.getMainPostId()); |
| | | } |
| | | if (StringUtil.isNotEmpty(row.getPartPostId())) { |
| | | ids.add(row.getPartPostId()); |
| | | } |
| | | } |
| | | Map<String, String> map = new HashMap<>(); |
| | | if (ids.isEmpty()) { |
| | | return map; |
| | | } |
| | | try { |
| | | List<PositionEntity> list = positionApi.getListByIds(new ArrayList<>(ids)); |
| | | if (list != null) { |
| | | for (PositionEntity p : list) { |
| | | if (p != null && StringUtil.isNotEmpty(p.getId())) { |
| | | map.put(p.getId(), p.getFullName()); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve posts failed: {}", e.getMessage()); |
| | | for (String id : ids) { |
| | | try { |
| | | PositionEntity p = positionApi.queryInfoById(id); |
| | | if (p != null) { |
| | | map.put(id, p.getFullName()); |
| | | } |
| | | } catch (Exception ignored) { |
| | | // ignore |
| | | } |
| | | } |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, String> loadUserNames(Set<String> userIds) { |
| | | Map<String, String> map = new HashMap<>(); |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return map; |
| | | } |
| | | try { |
| | | List<UserEntity> users = userApi.getUserName(new ArrayList<>(userIds)); |
| | | if (users != null) { |
| | | for (UserEntity u : users) { |
| | | if (u != null && StringUtil.isNotEmpty(u.getId())) { |
| | | map.put(u.getId(), StringUtil.isNotEmpty(u.getRealName()) ? u.getRealName() : u.getId()); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("resolve users failed: {}", e.getMessage()); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private String resolveTeacherNames(String teacherId, Map<String, String> nameMap) { |
| | | if (StringUtil.isEmpty(teacherId)) { |
| | | return ""; |
| | | } |
| | | List<String> ids = splitIds(teacherId); |
| | | if (ids.isEmpty()) { |
| | | return teacherId; |
| | | } |
| | | List<String> names = new ArrayList<>(); |
| | | for (String id : ids) { |
| | | names.add(nameMap.getOrDefault(id, id)); |
| | | } |
| | | return String.join("、", names); |
| | | } |
| | | |
| | | private List<String> splitIds(String raw) { |
| | | if (StringUtil.isEmpty(raw)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | String[] parts = raw.split("[,,;;\\s]+"); |
| | | List<String> out = new ArrayList<>(); |
| | | for (String p : parts) { |
| | | if (StringUtil.isNotEmpty(p)) { |
| | | out.add(p.trim()); |
| | | } |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | private boolean inDateRange(String trainDate, String startDate, String endDate) { |
| | | if (StringUtil.isEmpty(trainDate)) { |
| | | return StringUtil.isEmpty(startDate) && StringUtil.isEmpty(endDate); |
| | | } |
| | | if (StringUtil.isNotEmpty(startDate) && trainDate.compareTo(startDate) < 0) { |
| | | return false; |
| | | } |
| | | if (StringUtil.isNotEmpty(endDate) && trainDate.compareTo(endDate) > 0) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private String fmtDate(Date date) { |
| | | return date == null ? null : DateUtil.dateToString(date, DATE_FMT); |
| | | } |
| | | |
| | | private String firstNonEmpty(String a, String b) { |
| | | if (StringUtil.isNotEmpty(a)) { |
| | | return a; |
| | | } |
| | | return b == null ? "" : b; |
| | | } |
| | | |
| | | private String labelCategory(String code) { |
| | | if (StringUtil.isEmpty(code)) { |
| | | return ""; |
| | | } |
| | | switch (code) { |
| | | case "annual_plan": |
| | | case "annual": |
| | | return "年度培训"; |
| | | case "temp": |
| | | case "temporary": |
| | | return "临时培训"; |
| | | case "post": |
| | | return "岗位培训"; |
| | | default: |
| | | return code; |
| | | } |
| | | } |
| | | |
| | | private String labelTrainMode(String code) { |
| | | if (StringUtil.isEmpty(code)) { |
| | | return ""; |
| | | } |
| | | switch (code.toLowerCase()) { |
| | | case "online": |
| | | return "在线学习"; |
| | | case "onsite": |
| | | return "集中授课"; |
| | | case "practice": |
| | | return "实操培训"; |
| | | default: |
| | | return code; |
| | | } |
| | | } |
| | | |
| | | private String labelEvalMode(String code) { |
| | | if (StringUtil.isEmpty(code)) { |
| | | return ""; |
| | | } |
| | | switch (code.toLowerCase()) { |
| | | case "exam": |
| | | return "在线考试"; |
| | | case "none": |
| | | return "无需考核"; |
| | | case "quiz": |
| | | return "随堂测验"; |
| | | case "practice": |
| | | return "实操考核"; |
| | | default: |
| | | return code; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.course.TmsCourseForm; |
| | | import jnpf.tmsEntity.course.TmsCourseQuery; |
| | | import jnpf.tmsEntity.course.TmsPaperOption; |
| | | import jnpf.tmsService.TmsCourseService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 培训课程。网关 /api/tms/course/** (StripPrefix=2)→ /course/** |
| | | */ |
| | | @Tag(name = "TMS培训课程", description = "Course") |
| | | @RestController |
| | | @RequestMapping("/course") |
| | | @RequiredArgsConstructor |
| | | public class TmsCourseController { |
| | | |
| | | private final TmsCourseService courseService; |
| | | |
| | | @Operation(summary = "培训课程分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsCourseForm>> list(TmsCourseQuery query) { |
| | | List<TmsCourseForm> list = courseService.getList(query); |
| | | PageListVO<TmsCourseForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "关联试卷下拉") |
| | | @GetMapping("/paper-options") |
| | | public ActionResult<List<TmsPaperOption>> paperOptions() { |
| | | return ActionResult.success(courseService.paperOptions()); |
| | | } |
| | | |
| | | @Operation(summary = "培训课程详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsCourseForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(courseService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增培训课程") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsCourseForm form) { |
| | | return ActionResult.success(courseService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新培训课程") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsCourseForm form) { |
| | | courseService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "启用/停用") |
| | | @PutMapping("/{id}/enabled") |
| | | public ActionResult<String> enabled(@PathVariable("id") String id, @RequestBody Map<String, String> body) { |
| | | courseService.setEnabled(id, body == null ? null : body.get("enabled")); |
| | | return ActionResult.success("操作成功"); |
| | | } |
| | | |
| | | @Operation(summary = "删除培训课程") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | courseService.delete(id); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeForm; |
| | | import jnpf.tmsEntity.evalmode.TmsEvalModeQuery; |
| | | import jnpf.tmsService.TmsEvalModeService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 考核方式。网关 /api/tms/eval-mode/** (StripPrefix=2)→ /eval-mode/** |
| | | */ |
| | | @Tag(name = "TMS考核方式", description = "EvalMode") |
| | | @RestController |
| | | @RequestMapping("/eval-mode") |
| | | @RequiredArgsConstructor |
| | | public class TmsEvalModeController { |
| | | |
| | | private final TmsEvalModeService evalModeService; |
| | | |
| | | @Operation(summary = "考核方式分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsEvalModeForm>> list(TmsEvalModeQuery query) { |
| | | List<TmsEvalModeForm> list = evalModeService.getList(query); |
| | | PageListVO<TmsEvalModeForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "考核方式详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsEvalModeForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(evalModeService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增考核方式") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsEvalModeForm form) { |
| | | return ActionResult.success(evalModeService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新考核方式") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsEvalModeForm form) { |
| | | evalModeService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "启用/停用") |
| | | @PutMapping("/{id}/enabled") |
| | | public ActionResult<String> enabled(@PathVariable("id") String id, @RequestBody Map<String, String> body) { |
| | | evalModeService.setEnabled(id, body == null ? null : body.get("enabled")); |
| | | return ActionResult.success("操作成功"); |
| | | } |
| | | |
| | | @Operation(summary = "删除考核方式") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | evalModeService.delete(id); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeDetailVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeExamVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeQuery; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSessionVO; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitForm; |
| | | import jnpf.tmsEntity.examgrade.TmsExamGradeSubmitResultVO; |
| | | import jnpf.tmsService.TmsExamGradeService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 考试阅卷。网关 /api/tms/exam-grade/** (StripPrefix=2)→ /exam-grade/** |
| | | */ |
| | | @Tag(name = "TMS考试阅卷", description = "ExamGrade") |
| | | @RestController |
| | | @RequestMapping("/exam-grade") |
| | | @RequiredArgsConstructor |
| | | public class TmsExamGradeController { |
| | | |
| | | private final TmsExamGradeService examGradeService; |
| | | |
| | | @Operation(summary = "阅卷场次列表") |
| | | @GetMapping("/sessions") |
| | | public ActionResult<PageListVO<TmsExamGradeSessionVO>> sessions(TmsExamGradeQuery query) { |
| | | List<TmsExamGradeSessionVO> list = examGradeService.getSessionList(query); |
| | | PageListVO<TmsExamGradeSessionVO> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "阅卷场次信息") |
| | | @GetMapping("/sessions/{id}") |
| | | public ActionResult<TmsExamGradeSessionVO> session(@PathVariable("id") String id) { |
| | | return ActionResult.success(examGradeService.getSession(id)); |
| | | } |
| | | |
| | | @Operation(summary = "场次下答卷列表") |
| | | @GetMapping("/sessions/{id}/exams") |
| | | public ActionResult<List<TmsExamGradeExamVO>> exams(@PathVariable("id") String id) { |
| | | return ActionResult.success(examGradeService.getExamList(id)); |
| | | } |
| | | |
| | | @Operation(summary = "答卷阅卷详情") |
| | | @GetMapping("/exams/{examId}") |
| | | public ActionResult<TmsExamGradeDetailVO> examDetail(@PathVariable("examId") String examId) { |
| | | return ActionResult.success(examGradeService.getExamDetail(examId)); |
| | | } |
| | | |
| | | @Operation(summary = "提交阅卷") |
| | | @PostMapping("/exams/{examId}/submit") |
| | | public ActionResult<TmsExamGradeSubmitResultVO> submit( |
| | | @PathVariable("examId") String examId, |
| | | @RequestBody TmsExamGradeSubmitForm form) { |
| | | if (form == null) { |
| | | form = new TmsExamGradeSubmitForm(); |
| | | } |
| | | form.setExamId(examId); |
| | | return ActionResult.success(examGradeService.submitGrade(form)); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreDetailVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreExportForm; |
| | | import jnpf.tmsEntity.examscore.TmsExamScorePaperVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreQuery; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreRowVO; |
| | | import jnpf.tmsEntity.examscore.TmsExamScoreSummaryVO; |
| | | import jnpf.tmsService.TmsExamScoreService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 考试成绩。网关 /api/tms/exam-score/** (StripPrefix=2)→ /exam-score/** |
| | | */ |
| | | @Tag(name = "TMS考试成绩", description = "ExamScore") |
| | | @RestController |
| | | @RequestMapping("/exam-score") |
| | | @RequiredArgsConstructor |
| | | public class TmsExamScoreController { |
| | | |
| | | private final TmsExamScoreService examScoreService; |
| | | |
| | | @Operation(summary = "考试成绩汇总列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsExamScoreSummaryVO>> list(TmsExamScoreQuery query) { |
| | | List<TmsExamScoreSummaryVO> list = examScoreService.getList(query); |
| | | PageListVO<TmsExamScoreSummaryVO> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "考试成绩详情(考生列表)") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsExamScoreDetailVO> detail(@PathVariable("id") String id) { |
| | | return ActionResult.success(examScoreService.getDetail(id)); |
| | | } |
| | | |
| | | @Operation(summary = "按考核对象导出已勾选试卷成绩") |
| | | @PostMapping("/export") |
| | | public ActionResult<List<TmsExamScoreRowVO>> export(@RequestBody TmsExamScoreExportForm form) { |
| | | List<String> ids = form == null ? null : form.getIds(); |
| | | return ActionResult.success(examScoreService.exportRows(ids)); |
| | | } |
| | | |
| | | @Operation(summary = "查看答卷") |
| | | @GetMapping("/paper/{examId}") |
| | | public ActionResult<TmsExamScorePaperVO> paper(@PathVariable("examId") String examId) { |
| | | return ActionResult.success(examScoreService.getPaperView(examId)); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamDetailVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamListVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamPaperVO; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamQuery; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamStartForm; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitForm; |
| | | import jnpf.tmsEntity.exam.TmsOnlineExamSubmitResultVO; |
| | | import jnpf.tmsService.TmsOnlineExamService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 在线考试。网关 /api/tms/online-exam/** (StripPrefix=2)→ /online-exam/** |
| | | */ |
| | | @Tag(name = "TMS在线考试", description = "OnlineExam") |
| | | @RestController |
| | | @RequestMapping("/online-exam") |
| | | @RequiredArgsConstructor |
| | | public class TmsOnlineExamController { |
| | | |
| | | private final TmsOnlineExamService onlineExamService; |
| | | |
| | | @Operation(summary = "我的试卷") |
| | | @GetMapping("/my-papers") |
| | | public ActionResult<PageListVO<TmsOnlineExamListVO>> myPapers(TmsOnlineExamQuery query) { |
| | | List<TmsOnlineExamListVO> list = onlineExamService.getMyPapers(query); |
| | | PageListVO<TmsOnlineExamListVO> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "试卷考试详情") |
| | | @GetMapping("/my-papers/{id}") |
| | | public ActionResult<TmsOnlineExamDetailVO> detail(@PathVariable("id") String id) { |
| | | return ActionResult.success(onlineExamService.getDetail(id)); |
| | | } |
| | | |
| | | @Operation(summary = "按答卷ID查看考试回顾") |
| | | @GetMapping("/exams/{examId}") |
| | | public ActionResult<TmsOnlineExamDetailVO> examReview(@PathVariable("examId") String examId) { |
| | | return ActionResult.success(onlineExamService.getExamReview(examId)); |
| | | } |
| | | |
| | | @Operation(summary = "开始或继续考试") |
| | | @PostMapping("/start") |
| | | public ActionResult<TmsOnlineExamPaperVO> start(@RequestBody TmsOnlineExamStartForm form) { |
| | | String paperId = form == null ? null : form.getMyPaperId(); |
| | | return ActionResult.success(onlineExamService.start(paperId)); |
| | | } |
| | | |
| | | @Operation(summary = "暂存作答") |
| | | @PostMapping("/{id}/save") |
| | | public ActionResult<String> save(@PathVariable("id") String id, @RequestBody TmsOnlineExamSubmitForm form) { |
| | | onlineExamService.saveAnswers(id, form); |
| | | return ActionResult.success("暂存成功"); |
| | | } |
| | | |
| | | @Operation(summary = "交卷") |
| | | @PostMapping("/{id}/submit") |
| | | public ActionResult<TmsOnlineExamSubmitResultVO> submit(@PathVariable("id") String id, |
| | | @RequestBody TmsOnlineExamSubmitForm form) { |
| | | return ActionResult.success(onlineExamService.submit(id, form)); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.paper.TmsPaperForm; |
| | | import jnpf.tmsEntity.paper.TmsPaperQuery; |
| | | import jnpf.tmsService.TmsPaperService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 试卷管理(含组卷)。网关 /api/tms/paper/** (StripPrefix=2)→ /paper/** |
| | | */ |
| | | @Tag(name = "TMS试卷", description = "Paper") |
| | | @RestController |
| | | @RequestMapping("/paper") |
| | | @RequiredArgsConstructor |
| | | public class TmsPaperController { |
| | | |
| | | private final TmsPaperService paperService; |
| | | |
| | | @Operation(summary = "试卷分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsPaperForm>> list(TmsPaperQuery query) { |
| | | List<TmsPaperForm> list = paperService.getList(query); |
| | | PageListVO<TmsPaperForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "试卷详情(含章节与试题)") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsPaperForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(paperService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增试卷(组卷)") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsPaperForm form) { |
| | | return ActionResult.success(paperService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新试卷(组卷)") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsPaperForm form) { |
| | | paperService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "废弃试卷") |
| | | @PutMapping("/{id}/invalidate") |
| | | public ActionResult<String> invalidate(@PathVariable("id") String id) { |
| | | paperService.invalidate(id); |
| | | return ActionResult.success("废弃成功"); |
| | | } |
| | | |
| | | @Operation(summary = "启用/停用试卷(开放/不开放)") |
| | | @PutMapping("/{id}/status") |
| | | public ActionResult<String> status(@PathVariable("id") String id, @RequestBody Map<String, String> body) { |
| | | paperService.setBizStatus(id, body == null ? null : body.get("bizStatus")); |
| | | return ActionResult.success("操作成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskLearnForm; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskLearnResult; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskMineForm; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskQuery; |
| | | import jnpf.tmsEntity.person.TmsPersonTaskSignResult; |
| | | import jnpf.tmsService.TmsPersonTaskService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 个人培训任务(学员端)。网关 /api/tms/person-task/** (StripPrefix=2)→ /person-task/** |
| | | */ |
| | | @Tag(name = "TMS个人培训任务", description = "PersonTask") |
| | | @RestController |
| | | @RequestMapping("/person-task") |
| | | @RequiredArgsConstructor |
| | | public class TmsPersonTaskController { |
| | | |
| | | private final TmsPersonTaskService personTaskService; |
| | | |
| | | @Operation(summary = "我的个人培训任务") |
| | | @GetMapping("/mine") |
| | | public ActionResult<PageListVO<TmsPersonTaskMineForm>> mine(TmsPersonTaskQuery query) { |
| | | List<TmsPersonTaskMineForm> list = personTaskService.getMine(query); |
| | | PageListVO<TmsPersonTaskMineForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "个人培训任务详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsPersonTaskMineForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(personTaskService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "在线签到") |
| | | @PostMapping("/{id}/sign") |
| | | public ActionResult<TmsPersonTaskSignResult> sign(@PathVariable("id") String id) { |
| | | return ActionResult.success(personTaskService.sign(id)); |
| | | } |
| | | |
| | | @Operation(summary = "累计学习时长(学满后系统自动确认完成并提示)") |
| | | @PostMapping("/{id}/learn") |
| | | public ActionResult<TmsPersonTaskLearnResult> learn( |
| | | @PathVariable("id") String id, |
| | | @RequestBody(required = false) TmsPersonTaskLearnForm form) { |
| | | return ActionResult.success(personTaskService.markLearned(id, form == null ? null : form.getSeconds())); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.tmsEntity.place.TmsPlaceOption; |
| | | import jnpf.tmsService.TmsPlaceService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 培训地点。网关 /api/tms/place/** → /place/** |
| | | */ |
| | | @Tag(name = "TMS培训地点", description = "Place") |
| | | @RestController |
| | | @RequestMapping("/place") |
| | | @RequiredArgsConstructor |
| | | public class TmsPlaceController { |
| | | |
| | | private final TmsPlaceService placeService; |
| | | |
| | | @Operation(summary = "启用地点下拉") |
| | | @GetMapping("/options") |
| | | public ActionResult<List<TmsPlaceOption>> options() { |
| | | return ActionResult.success(placeService.listOptions()); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.question.TmsQuestionBankOption; |
| | | import jnpf.tmsEntity.question.TmsQuestionForm; |
| | | import jnpf.tmsEntity.question.TmsQuestionQuery; |
| | | import jnpf.tmsService.TmsQuestionService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 试题 CRUD。网关 /api/tms/question/** (StripPrefix=2)→ /question/** |
| | | */ |
| | | @Tag(name = "TMS试题", description = "Question") |
| | | @RestController |
| | | @RequestMapping("/question") |
| | | @RequiredArgsConstructor |
| | | public class TmsQuestionController { |
| | | |
| | | private final TmsQuestionService questionService; |
| | | |
| | | @Operation(summary = "题库下拉") |
| | | @GetMapping("/banks") |
| | | public ActionResult<List<TmsQuestionBankOption>> banks() { |
| | | return ActionResult.success(questionService.listBanks()); |
| | | } |
| | | |
| | | @Operation(summary = "管理员下拉") |
| | | @GetMapping("/admins") |
| | | public ActionResult<List<Map<String, String>>> admins() { |
| | | return ActionResult.success(questionService.listAdmins()); |
| | | } |
| | | |
| | | @Operation(summary = "试题分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsQuestionForm>> list(TmsQuestionQuery query) { |
| | | List<TmsQuestionForm> list = questionService.getList(query); |
| | | PageListVO<TmsQuestionForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "试题详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsQuestionForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(questionService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增试题") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsQuestionForm form) { |
| | | return ActionResult.success(questionService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新试题") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsQuestionForm form) { |
| | | questionService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "废弃试题(逻辑删除)") |
| | | @PutMapping("/{id}/invalidate") |
| | | public ActionResult<String> invalidate(@PathVariable("id") String id) { |
| | | questionService.invalidate(id); |
| | | return ActionResult.success("废弃成功"); |
| | | } |
| | | |
| | | @Operation(summary = "废弃试题(逻辑删除)") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | questionService.invalidate(id); |
| | | return ActionResult.success("废弃成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.record.TmsRecordForm; |
| | | import jnpf.tmsEntity.record.TmsRecordQuery; |
| | | import jnpf.tmsService.TmsRecordService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 培训记录。网关 /api/tms/record/** (StripPrefix=2)→ /record/** |
| | | */ |
| | | @Tag(name = "TMS培训记录", description = "Record") |
| | | @RestController |
| | | @RequestMapping("/record") |
| | | @RequiredArgsConstructor |
| | | public class TmsRecordController { |
| | | |
| | | private final TmsRecordService recordService; |
| | | |
| | | @Operation(summary = "培训记录分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsRecordForm>> list(TmsRecordQuery query) { |
| | | List<TmsRecordForm> list = recordService.getList(query); |
| | | PageListVO<TmsRecordForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "培训记录详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsRecordForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(recordService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "更新培训记录") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsRecordForm form) { |
| | | recordService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "归档培训记录") |
| | | @PostMapping("/archive") |
| | | public ActionResult<Integer> archive(@RequestBody Map<String, List<String>> body) { |
| | | int count = recordService.archive(body == null ? null : body.get("ids")); |
| | | return ActionResult.success(count); |
| | | } |
| | | |
| | | @Operation(summary = "删除培训记录") |
| | | @DeleteMapping |
| | | public ActionResult<String> delete(@RequestBody Map<String, List<String>> body) { |
| | | recordService.delete(body == null ? null : body.get("ids")); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestPaperVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestQuery; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestRecordVO; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestStartForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitForm; |
| | | import jnpf.tmsEntity.selftest.TmsSelfTestSubmitResultVO; |
| | | import jnpf.tmsService.TmsSelfTestService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 自我检测。网关 /api/tms/self-test/** (StripPrefix=2)→ /self-test/** |
| | | */ |
| | | @Tag(name = "TMS自我检测", description = "SelfTest") |
| | | @RestController |
| | | @RequestMapping("/self-test") |
| | | @RequiredArgsConstructor |
| | | public class TmsSelfTestController { |
| | | |
| | | private final TmsSelfTestService selfTestService; |
| | | |
| | | @Operation(summary = "开始自我检测(抽题)") |
| | | @PostMapping("/start") |
| | | public ActionResult<TmsSelfTestPaperVO> start(@RequestBody TmsSelfTestStartForm form) { |
| | | return ActionResult.success(selfTestService.start(form)); |
| | | } |
| | | |
| | | @Operation(summary = "暂存作答(不交卷)") |
| | | @PostMapping("/{id}/save") |
| | | public ActionResult<String> save(@PathVariable("id") String id, |
| | | @RequestBody TmsSelfTestSubmitForm form) { |
| | | selfTestService.saveAnswers(id, form); |
| | | return ActionResult.success("暂存成功"); |
| | | } |
| | | |
| | | @Operation(summary = "交卷") |
| | | @PostMapping("/{id}/submit") |
| | | public ActionResult<TmsSelfTestSubmitResultVO> submit(@PathVariable("id") String id, |
| | | @RequestBody TmsSelfTestSubmitForm form) { |
| | | return ActionResult.success(selfTestService.submit(id, form)); |
| | | } |
| | | |
| | | @Operation(summary = "我的检测记录") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsSelfTestRecordVO>> list(TmsSelfTestQuery query) { |
| | | List<TmsSelfTestRecordVO> list = selfTestService.getList(query); |
| | | PageListVO<TmsSelfTestRecordVO> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "检测详情(重做回顾)") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsSelfTestPaperVO> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(selfTestService.getInfo(id)); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.signmode.TmsSignModeForm; |
| | | import jnpf.tmsEntity.signmode.TmsSignModeQuery; |
| | | import jnpf.tmsService.TmsSignModeService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 签到方式。网关 /api/tms/sign-mode/** (StripPrefix=2)→ /sign-mode/** |
| | | */ |
| | | @Tag(name = "TMS签到方式", description = "SignMode") |
| | | @RestController |
| | | @RequestMapping("/sign-mode") |
| | | @RequiredArgsConstructor |
| | | public class TmsSignModeController { |
| | | |
| | | private final TmsSignModeService signModeService; |
| | | |
| | | @Operation(summary = "签到方式分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsSignModeForm>> list(TmsSignModeQuery query) { |
| | | List<TmsSignModeForm> list = signModeService.getList(query); |
| | | PageListVO<TmsSignModeForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "签到方式详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsSignModeForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(signModeService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增签到方式") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsSignModeForm form) { |
| | | return ActionResult.success(signModeService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新签到方式") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsSignModeForm form) { |
| | | signModeService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "启用/停用") |
| | | @PutMapping("/{id}/enabled") |
| | | public ActionResult<String> enabled(@PathVariable("id") String id, @RequestBody Map<String, String> body) { |
| | | signModeService.setEnabled(id, body == null ? null : body.get("enabled")); |
| | | return ActionResult.success("操作成功"); |
| | | } |
| | | |
| | | @Operation(summary = "删除签到方式") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | signModeService.delete(id); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.task.TmsFromPlanResult; |
| | | import jnpf.tmsEntity.task.TmsTaskForm; |
| | | import jnpf.tmsEntity.task.TmsTaskQuery; |
| | | import jnpf.tmsService.TmsTaskService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import jnpf.util.StringUtil; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 培训任务管理。网关 /api/tms/task/** (StripPrefix=2)→ /task/** |
| | | */ |
| | | @Tag(name = "TMS培训任务", description = "Task") |
| | | @RestController |
| | | @RequestMapping("/task") |
| | | @RequiredArgsConstructor |
| | | public class TmsTaskController { |
| | | |
| | | private final TmsTaskService taskService; |
| | | |
| | | @Operation(summary = "培训任务分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsTaskForm>> list(TmsTaskQuery query) { |
| | | List<TmsTaskForm> list = taskService.getList(query); |
| | | PageListVO<TmsTaskForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "培训任务详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsTaskForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(taskService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增培训任务") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsTaskForm form) { |
| | | return ActionResult.success(taskService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新培训任务") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsTaskForm form) { |
| | | taskService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "删除培训任务(仅草稿)") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | taskService.delete(id); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | |
| | | @Operation(summary = "发布培训任务") |
| | | @PutMapping("/{id}/publish") |
| | | public ActionResult<String> publish(@PathVariable("id") String id) { |
| | | taskService.publish(id); |
| | | return ActionResult.success("发布成功"); |
| | | } |
| | | |
| | | @Operation(summary = "取消/终止培训任务") |
| | | @PutMapping("/{id}/cancel") |
| | | public ActionResult<String> cancel(@PathVariable("id") String id) { |
| | | taskService.cancel(id); |
| | | return ActionResult.success("取消成功"); |
| | | } |
| | | |
| | | @Operation(summary = "批量取消培训任务") |
| | | @PutMapping("/cancelBatch") |
| | | public ActionResult<String> cancelBatch(@RequestBody Map<String, List<String>> body) { |
| | | taskService.cancelBatch(body == null ? null : body.get("ids")); |
| | | return ActionResult.success("取消成功"); |
| | | } |
| | | |
| | | @Operation(summary = "恢复为草稿") |
| | | @PutMapping("/{id}/restore") |
| | | public ActionResult<String> restore(@PathVariable("id") String id) { |
| | | taskService.restore(id); |
| | | return ActionResult.success("已恢复为草稿"); |
| | | } |
| | | |
| | | @Operation(summary = "年计划审批通过:按子表生成未发布培训任务") |
| | | @PostMapping("/from-annual/{planId}") |
| | | public ActionResult<TmsFromPlanResult> fromAnnual(@PathVariable("planId") String planId) { |
| | | return ActionResult.success(taskService.createFromAnnual(planId)); |
| | | } |
| | | |
| | | @Operation(summary = "年计划审批通过") |
| | | @PostMapping("/from-annual") |
| | | public ActionResult<TmsFromPlanResult> fromAnnualBody( |
| | | @RequestBody(required = false) Map<String, Object> body, |
| | | @RequestParam(value = "id", required = false) String id, |
| | | @RequestParam(value = "planId", required = false) String planIdParam, |
| | | @RequestParam(value = "f_id", required = false) String fId) { |
| | | Map<String, Object> merged = new LinkedHashMap<>(); |
| | | if (body != null) { |
| | | merged.putAll(body); |
| | | } |
| | | // 数据接口预览常把「接口参数」放 query,不进 JSON Body |
| | | putIfAbsent(merged, "id", id); |
| | | putIfAbsent(merged, "planId", planIdParam); |
| | | putIfAbsent(merged, "f_id", fId); |
| | | return ActionResult.success(taskService.createFromAnnual(pickPlanId(merged))); |
| | | } |
| | | |
| | | @Operation(summary = "岗位培训计划审批通过:按子表生成未发布培训任务") |
| | | @PostMapping("/from-post/{planId}") |
| | | public ActionResult<TmsFromPlanResult> fromPost(@PathVariable("planId") String planId) { |
| | | return ActionResult.success(taskService.createFromPost(planId)); |
| | | } |
| | | |
| | | @Operation(summary = "岗位培训计划审批通过") |
| | | @PostMapping("/from-post") |
| | | public ActionResult<TmsFromPlanResult> fromPostBody( |
| | | @RequestBody(required = false) Map<String, Object> body, |
| | | @RequestParam(value = "id", required = false) String id, |
| | | @RequestParam(value = "planId", required = false) String planIdParam, |
| | | @RequestParam(value = "f_id", required = false) String fId) { |
| | | Map<String, Object> merged = new LinkedHashMap<>(); |
| | | if (body != null) { |
| | | merged.putAll(body); |
| | | } |
| | | putIfAbsent(merged, "id", id); |
| | | putIfAbsent(merged, "planId", planIdParam); |
| | | putIfAbsent(merged, "f_id", fId); |
| | | return ActionResult.success(taskService.createFromPost(pickPlanId(merged))); |
| | | } |
| | | |
| | | private static void putIfAbsent(Map<String, Object> map, String key, String value) { |
| | | if (StringUtil.isEmpty(value)) { |
| | | return; |
| | | } |
| | | Object existed = map.get(key); |
| | | if (existed == null || StringUtil.isEmpty(String.valueOf(existed)) |
| | | || "null".equalsIgnoreCase(String.valueOf(existed)) |
| | | || "value".equalsIgnoreCase(String.valueOf(existed).trim())) { |
| | | map.put(key, value.trim()); |
| | | } |
| | | } |
| | | |
| | | private static String pickPlanId(Map<String, Object> body) { |
| | | if (body == null || body.isEmpty()) { |
| | | return null; |
| | | } |
| | | for (String key : new String[]{"id", "planId", "f_id", "F_Id", "formId"}) { |
| | | Object v = body.get(key); |
| | | if (v == null) { |
| | | continue; |
| | | } |
| | | String text = String.valueOf(v).trim(); |
| | | if (StringUtil.isEmpty(text) || "null".equalsIgnoreCase(text) || "value".equalsIgnoreCase(text)) { |
| | | continue; |
| | | } |
| | | return text; |
| | | } |
| | | return null; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.tmsEntity.archive.TmsTrainArchiveDetailVO; |
| | | import jnpf.tmsService.TmsTrainArchiveService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * 个人培训档案。网关 /api/tms/train-archive/** (StripPrefix=2)→ /train-archive/** |
| | | */ |
| | | @Tag(name = "TMS个人培训档案", description = "TrainArchive") |
| | | @RestController |
| | | @RequestMapping("/train-archive") |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainArchiveController { |
| | | |
| | | private final TmsTrainArchiveService trainArchiveService; |
| | | |
| | | @Operation(summary = "我的个人培训档案") |
| | | @GetMapping("/mine") |
| | | public ActionResult<TmsTrainArchiveDetailVO> mine() { |
| | | return ActionResult.success(trainArchiveService.getMine()); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeForm; |
| | | import jnpf.tmsEntity.trainmode.TmsTrainModeQuery; |
| | | import jnpf.tmsService.TmsTrainModeService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 培训方式。网关 /api/tms/train-mode/** (StripPrefix=2)→ /train-mode/** |
| | | */ |
| | | @Tag(name = "TMS培训方式", description = "TrainMode") |
| | | @RestController |
| | | @RequestMapping("/train-mode") |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainModeController { |
| | | |
| | | private final TmsTrainModeService trainModeService; |
| | | |
| | | @Operation(summary = "培训方式分页列表") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsTrainModeForm>> list(TmsTrainModeQuery query) { |
| | | List<TmsTrainModeForm> list = trainModeService.getList(query); |
| | | PageListVO<TmsTrainModeForm> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "培训方式详情") |
| | | @GetMapping("/{id}") |
| | | public ActionResult<TmsTrainModeForm> info(@PathVariable("id") String id) { |
| | | return ActionResult.success(trainModeService.getInfo(id)); |
| | | } |
| | | |
| | | @Operation(summary = "新增培训方式") |
| | | @PostMapping |
| | | public ActionResult<String> create(@RequestBody TmsTrainModeForm form) { |
| | | return ActionResult.success(trainModeService.create(form)); |
| | | } |
| | | |
| | | @Operation(summary = "更新培训方式") |
| | | @PutMapping("/{id}") |
| | | public ActionResult<String> update(@PathVariable("id") String id, @RequestBody TmsTrainModeForm form) { |
| | | trainModeService.update(id, form); |
| | | return ActionResult.success("更新成功"); |
| | | } |
| | | |
| | | @Operation(summary = "启用/停用") |
| | | @PutMapping("/{id}/enabled") |
| | | public ActionResult<String> enabled(@PathVariable("id") String id, @RequestBody Map<String, String> body) { |
| | | trainModeService.setEnabled(id, body == null ? null : body.get("enabled")); |
| | | return ActionResult.success("操作成功"); |
| | | } |
| | | |
| | | @Operation(summary = "删除培训方式") |
| | | @DeleteMapping("/{id}") |
| | | public ActionResult<String> delete(@PathVariable("id") String id) { |
| | | trainModeService.delete(id); |
| | | return ActionResult.success("删除成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsController; |
| | | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jnpf.base.ActionResult; |
| | | import jnpf.base.vo.PageListVO; |
| | | import jnpf.base.vo.PaginationVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordCatalogVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordListVO; |
| | | import jnpf.tmsEntity.trainrecord.TmsTrainRecordQuery; |
| | | import jnpf.tmsService.TmsTrainRecordService; |
| | | import jnpf.util.JsonUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 个人培训记录。网关 /api/tms/train-record/** (StripPrefix=2)→ /train-record/** |
| | | */ |
| | | @Tag(name = "TMS个人培训记录", description = "TrainRecord") |
| | | @RestController |
| | | @RequestMapping("/train-record") |
| | | @RequiredArgsConstructor |
| | | public class TmsTrainRecordController { |
| | | |
| | | private final TmsTrainRecordService trainRecordService; |
| | | |
| | | @Operation(summary = "个人培训记录分页列表(按人员档案)") |
| | | @GetMapping("/list") |
| | | public ActionResult<PageListVO<TmsTrainRecordListVO>> list(TmsTrainRecordQuery query) { |
| | | List<TmsTrainRecordListVO> list = trainRecordService.getList(query); |
| | | PageListVO<TmsTrainRecordListVO> vo = new PageListVO<>(); |
| | | vo.setList(list); |
| | | vo.setPagination(JsonUtil.getJsonToBean(query, PaginationVO.class)); |
| | | return ActionResult.success(vo); |
| | | } |
| | | |
| | | @Operation(summary = "个人培训目录") |
| | | @GetMapping("/{id}/catalog") |
| | | public ActionResult<TmsTrainRecordCatalogVO> catalog(@PathVariable("id") String id, |
| | | @RequestParam(value = "startDate", required = false) String startDate, |
| | | @RequestParam(value = "endDate", required = false) String endDate) { |
| | | return ActionResult.success(trainRecordService.getCatalog(id, startDate, endDate)); |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 年度培训计划 tms_annual_plan |
| | | */ |
| | | @Data |
| | | @TableName("tms_annual_plan") |
| | | public class TmsAnnualPlanEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("plan_year") |
| | | private String planYear; |
| | | |
| | | @TableField("plan_no") |
| | | private String planNo; |
| | | |
| | | @TableField("plan_type") |
| | | private String planType; |
| | | |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 年度培训计划子表 tms_annual_plan_item |
| | | */ |
| | | @Data |
| | | @TableName("tms_annual_plan_item") |
| | | public class TmsAnnualPlanItemEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("train_month") |
| | | private String trainMonth; |
| | | |
| | | @TableField("train_subject") |
| | | private String trainSubject; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | @TableField("trainees") |
| | | private String trainees; |
| | | |
| | | @TableField("hours") |
| | | private java.math.BigDecimal hours; |
| | | |
| | | @TableField("teacher_id") |
| | | private String teacherId; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | @TableField("course_id") |
| | | private String courseId; |
| | | |
| | | /** pending / tasked / done / delayed */ |
| | | @TableField("line_status") |
| | | private String lineStatus; |
| | | |
| | | @TableField("task_id") |
| | | private String taskId; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训课程 tms_course |
| | | */ |
| | | @Data |
| | | @TableName("tms_course") |
| | | public class TmsCourseEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("course_no") |
| | | private String courseNo; |
| | | |
| | | @TableField("course_name") |
| | | private String courseName; |
| | | |
| | | @TableField("category") |
| | | private String category; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | @TableField("paper_id") |
| | | private String paperId; |
| | | |
| | | @TableField("hours") |
| | | private BigDecimal hours; |
| | | |
| | | @TableField("enabled") |
| | | private String enabled; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 考核方式配置 tms_eval_mode |
| | | */ |
| | | @Data |
| | | @TableName("tms_eval_mode") |
| | | public class TmsEvalModeEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("mode_code") |
| | | private String modeCode; |
| | | |
| | | @TableField("mode_name") |
| | | private String modeName; |
| | | |
| | | @TableField("need_paper") |
| | | private String needPaper; |
| | | |
| | | @TableField("need_quiz") |
| | | private String needQuiz; |
| | | |
| | | @TableField("need_practice") |
| | | private String needPractice; |
| | | |
| | | @TableField("enabled") |
| | | private String enabled; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 在线考试答卷 tms_exam |
| | | */ |
| | | @Data |
| | | @TableName("tms_exam") |
| | | public class TmsExamEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("task_id") |
| | | private String taskId; |
| | | |
| | | @TableField("person_task_id") |
| | | private String personTaskId; |
| | | |
| | | @TableField("record_id") |
| | | private String recordId; |
| | | |
| | | @TableField("paper_id") |
| | | private String paperId; |
| | | |
| | | @TableField("paper_name") |
| | | private String paperName; |
| | | |
| | | @TableField("user_id") |
| | | private String userId; |
| | | |
| | | @TableField("attempt_no") |
| | | private Integer attemptNo; |
| | | |
| | | @TableField("start_time") |
| | | private Date startTime; |
| | | |
| | | @TableField("submit_time") |
| | | private Date submitTime; |
| | | |
| | | @TableField("duration_min") |
| | | private Integer durationMin; |
| | | |
| | | @TableField("objective_score") |
| | | private BigDecimal objectiveScore; |
| | | |
| | | @TableField("subjective_score") |
| | | private BigDecimal subjectiveScore; |
| | | |
| | | @TableField("total_score") |
| | | private BigDecimal totalScore; |
| | | |
| | | @TableField("pass_score") |
| | | private BigDecimal passScore; |
| | | |
| | | @TableField("pass_flag") |
| | | private String passFlag; |
| | | |
| | | /** auto=已自动阅卷 / pending=待阅卷 */ |
| | | @TableField("grade_status") |
| | | private String gradeStatus; |
| | | |
| | | @TableField("grader_id") |
| | | private String graderId; |
| | | |
| | | @TableField("grade_time") |
| | | private Date gradeTime; |
| | | |
| | | /** doing / submitted */ |
| | | @TableField("exam_status") |
| | | private String examStatus; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 答卷试题快照 tms_exam_item |
| | | */ |
| | | @Data |
| | | @TableName("tms_exam_item") |
| | | public class TmsExamItemEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("question_id") |
| | | private String questionId; |
| | | |
| | | @TableField("question_type") |
| | | private String questionType; |
| | | |
| | | @TableField("stem") |
| | | private String stem; |
| | | |
| | | @TableField("options_json") |
| | | private String optionsJson; |
| | | |
| | | @TableField("correct_answer") |
| | | private String correctAnswer; |
| | | |
| | | @TableField("user_answer") |
| | | private String userAnswer; |
| | | |
| | | @TableField("score") |
| | | private BigDecimal score; |
| | | |
| | | @TableField("got_score") |
| | | private BigDecimal gotScore; |
| | | |
| | | /** 1=主观题 */ |
| | | @TableField("is_subjective") |
| | | private String isSubjective; |
| | | |
| | | @TableField("analysis") |
| | | private String analysis; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 培训教材 tms_material(任务回填编号用) |
| | | */ |
| | | @Data |
| | | @TableName("tms_material") |
| | | public class TmsMaterialEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField("material_no") |
| | | private String materialNo; |
| | | |
| | | @TableField("material_name") |
| | | private String materialName; |
| | | |
| | | @TableField("file_type") |
| | | private String fileType; |
| | | |
| | | @TableField("file_json") |
| | | private String fileJson; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 试卷主表 tms_paper |
| | | */ |
| | | @Data |
| | | @TableName("tms_paper") |
| | | public class TmsPaperEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | /** 试卷编号:年份后两位 + 4 位流水,如 260001 */ |
| | | @TableField("paper_no") |
| | | private String paperNo; |
| | | |
| | | @TableField("paper_name") |
| | | private String paperName; |
| | | |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField("duration_min") |
| | | private Integer durationMin; |
| | | |
| | | @TableField("exam_start") |
| | | private Date examStart; |
| | | |
| | | @TableField("exam_end") |
| | | private Date examEnd; |
| | | |
| | | @TableField("score_publish_time") |
| | | private Date scorePublishTime; |
| | | |
| | | @TableField("pass_score") |
| | | private BigDecimal passScore; |
| | | |
| | | @TableField("total_score") |
| | | private BigDecimal totalScore; |
| | | |
| | | /** paper=试卷顺序 / random=随机 */ |
| | | @TableField("sort_mode") |
| | | private String sortMode; |
| | | |
| | | /** 1=含主观题 / 0=否 */ |
| | | @TableField("has_subjective") |
| | | private String hasSubjective; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 试卷试题 tms_paper_question |
| | | */ |
| | | @Data |
| | | @TableName("tms_paper_question") |
| | | public class TmsPaperQuestionEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | /** 试卷ID */ |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("section_id") |
| | | private String sectionId; |
| | | |
| | | @TableField("question_id") |
| | | private String questionId; |
| | | |
| | | @TableField("score") |
| | | private BigDecimal score; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 试卷章节 tms_paper_section |
| | | */ |
| | | @Data |
| | | @TableName("tms_paper_section") |
| | | public class TmsPaperSectionEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | /** 试卷ID */ |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("section_name") |
| | | private String sectionName; |
| | | |
| | | @TableField("question_type") |
| | | private String questionType; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 个人培训档案 tms_person_file |
| | | */ |
| | | @Data |
| | | @TableName("tms_person_file") |
| | | public class TmsPersonFileEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("archive_no") |
| | | private String archiveNo; |
| | | |
| | | @TableField("archive_date") |
| | | private Date archiveDate; |
| | | |
| | | @TableField("user_id") |
| | | private String userId; |
| | | |
| | | @TableField("user_name") |
| | | private String userName; |
| | | |
| | | @TableField("dept_id") |
| | | private String deptId; |
| | | |
| | | @TableField("hire_date") |
| | | private Date hireDate; |
| | | |
| | | @TableField("leave_date") |
| | | private Date leaveDate; |
| | | |
| | | @TableField("main_post_id") |
| | | private String mainPostId; |
| | | |
| | | @TableField("part_post_id") |
| | | private String partPostId; |
| | | |
| | | /** valid / leave 等 */ |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 个人培训任务 tms_person_task |
| | | */ |
| | | @Data |
| | | @TableName("tms_person_task") |
| | | public class TmsPersonTaskEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("task_id") |
| | | private String taskId; |
| | | |
| | | @TableField("task_no") |
| | | private String taskNo; |
| | | |
| | | @TableField("user_id") |
| | | private String userId; |
| | | |
| | | @TableField("dept_id") |
| | | private String deptId; |
| | | |
| | | @TableField("hours") |
| | | private BigDecimal hours; |
| | | |
| | | /** todo / signed / done / expired / cancelled */ |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField("sign_time") |
| | | private Date signTime; |
| | | |
| | | @TableField("sign_mode") |
| | | private String signMode; |
| | | |
| | | @TableField("learn_seconds") |
| | | private Integer learnSeconds; |
| | | |
| | | @TableField("exam_score") |
| | | private BigDecimal examScore; |
| | | |
| | | @TableField("pass_flag") |
| | | private String passFlag; |
| | | |
| | | @TableField("guest_flag") |
| | | private String guestFlag; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 个人培训目录 tms_person_train |
| | | */ |
| | | @Data |
| | | @TableName("tms_person_train") |
| | | public class TmsPersonTrainEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("archive_id") |
| | | private String archiveId; |
| | | |
| | | @TableField("user_id") |
| | | private String userId; |
| | | |
| | | @TableField("record_id") |
| | | private String recordId; |
| | | |
| | | @TableField("record_no") |
| | | private String recordNo; |
| | | |
| | | @TableField("category") |
| | | private String category; |
| | | |
| | | @TableField("subject") |
| | | private String subject; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | /** 培训师(可为姓名或用户 ID,多选文本) */ |
| | | @TableField("teacher_id") |
| | | private String teacherId; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | @TableField("pass_flag") |
| | | private String passFlag; |
| | | |
| | | @TableField("start_time") |
| | | private Date startTime; |
| | | |
| | | @TableField("end_time") |
| | | private Date endTime; |
| | | |
| | | @TableField("hours") |
| | | private BigDecimal hours; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 个人培训档案-工作履历 tms_person_work |
| | | */ |
| | | @Data |
| | | @TableName("tms_person_work") |
| | | public class TmsPersonWorkEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("company_name") |
| | | private String companyName; |
| | | |
| | | @TableField("post_name") |
| | | private String postName; |
| | | |
| | | @TableField("hire_date") |
| | | private Date hireDate; |
| | | |
| | | @TableField("leave_date") |
| | | private Date leaveDate; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训地点 tms_place |
| | | */ |
| | | @Data |
| | | @TableName("tms_place") |
| | | public class TmsPlaceEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("place_no") |
| | | private String placeNo; |
| | | |
| | | @TableField("place_name") |
| | | private String placeName; |
| | | |
| | | @TableField("place_group") |
| | | private String placeGroup; |
| | | |
| | | @TableField("enabled") |
| | | private String enabled; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 岗位培训计划 tms_post_plan |
| | | */ |
| | | @Data |
| | | @TableName("tms_post_plan") |
| | | public class TmsPostPlanEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("plan_no") |
| | | private String planNo; |
| | | |
| | | @TableField("post_id") |
| | | private String postId; |
| | | |
| | | @TableField("post_name") |
| | | private String postName; |
| | | |
| | | @TableField("dept_id") |
| | | private String deptId; |
| | | |
| | | @TableField("plan_end_time") |
| | | private Date planEndTime; |
| | | |
| | | @TableField("post_desc") |
| | | private String postDesc; |
| | | |
| | | @TableField("trainees") |
| | | private String trainees; |
| | | |
| | | @TableField("catalog_id") |
| | | private String catalogId; |
| | | |
| | | @TableField("publish_status") |
| | | private String publishStatus; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 岗位培训计划子表 tms_post_plan_item |
| | | */ |
| | | @Data |
| | | @TableName("tms_post_plan_item") |
| | | public class TmsPostPlanItemEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("plan_time") |
| | | private Date planTime; |
| | | |
| | | @TableField("teacher_id") |
| | | private String teacherId; |
| | | |
| | | @TableField("subject") |
| | | private String subject; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | @TableField("hours") |
| | | private BigDecimal hours; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | @TableField("material_id") |
| | | private String materialId; |
| | | |
| | | @TableField("task_id") |
| | | private String taskId; |
| | | |
| | | /** pending / tasked / done */ |
| | | @TableField("line_status") |
| | | private String lineStatus; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 题库 tms_question_bank |
| | | */ |
| | | @Data |
| | | @TableName("tms_question_bank") |
| | | public class TmsQuestionBankEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("bank_name") |
| | | private String bankName; |
| | | |
| | | @TableField("question_count") |
| | | private Integer questionCount; |
| | | |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 试题主表 tms_question |
| | | */ |
| | | @Data |
| | | @TableName("tms_question") |
| | | public class TmsQuestionEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("question_no") |
| | | private String questionNo; |
| | | |
| | | @TableField("bank_id") |
| | | private String bankId; |
| | | |
| | | @TableField("question_type") |
| | | private String questionType; |
| | | |
| | | @TableField("difficulty") |
| | | private String difficulty; |
| | | |
| | | @TableField("source_type") |
| | | private String sourceType; |
| | | |
| | | @TableField("stem") |
| | | private String stem; |
| | | |
| | | @TableField("analysis") |
| | | private String analysis; |
| | | |
| | | @TableField("admin_user_id") |
| | | private String adminUserId; |
| | | |
| | | @TableField("biz_status") |
| | | private String bizStatus; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 试题选项子表 tms_question_option |
| | | */ |
| | | @Data |
| | | @TableName("tms_question_option") |
| | | public class TmsQuestionOptionEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | /** 试题主键 */ |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField("option_label") |
| | | private String optionLabel; |
| | | |
| | | @TableField("option_content") |
| | | private String optionContent; |
| | | |
| | | /** 1 正确 / 0 错误 */ |
| | | @TableField("is_correct") |
| | | private String isCorrect; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训记录 tms_record(发布任务时生成骨架) |
| | | */ |
| | | @Data |
| | | @TableName("tms_record") |
| | | public class TmsRecordEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("record_no") |
| | | private String recordNo; |
| | | |
| | | @TableField("task_id") |
| | | private String taskId; |
| | | |
| | | @TableField("task_no") |
| | | private String taskNo; |
| | | |
| | | @TableField("category") |
| | | private String category; |
| | | |
| | | @TableField("subject") |
| | | private String subject; |
| | | |
| | | @TableField("teacher_id") |
| | | private String teacherId; |
| | | |
| | | @TableField("train_type") |
| | | private String trainType; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | @TableField("start_time") |
| | | private Date startTime; |
| | | |
| | | @TableField("end_time") |
| | | private Date endTime; |
| | | |
| | | @TableField("close_time") |
| | | private Date closeTime; |
| | | |
| | | @TableField("archive_status") |
| | | private String archiveStatus; |
| | | |
| | | @TableField("sign_rate") |
| | | private Integer signRate; |
| | | |
| | | @TableField("pass_rate") |
| | | private Integer passRate; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 自我检测主表 tms_self_test |
| | | */ |
| | | @Data |
| | | @TableName("tms_self_test") |
| | | public class TmsSelfTestEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("bank_id") |
| | | private String bankId; |
| | | |
| | | @TableField("bank_name") |
| | | private String bankName; |
| | | |
| | | @TableField("user_id") |
| | | private String userId; |
| | | |
| | | @TableField("single_count") |
| | | private Integer singleCount; |
| | | |
| | | @TableField("multi_count") |
| | | private Integer multiCount; |
| | | |
| | | @TableField("judge_count") |
| | | private Integer judgeCount; |
| | | |
| | | @TableField("single_difficulty") |
| | | private String singleDifficulty; |
| | | |
| | | @TableField("multi_difficulty") |
| | | private String multiDifficulty; |
| | | |
| | | @TableField("judge_difficulty") |
| | | private String judgeDifficulty; |
| | | |
| | | @TableField("total_count") |
| | | private Integer totalCount; |
| | | |
| | | @TableField("correct_count") |
| | | private Integer correctCount; |
| | | |
| | | @TableField("score_rate") |
| | | private BigDecimal scoreRate; |
| | | |
| | | @TableField("start_time") |
| | | private Date startTime; |
| | | |
| | | @TableField("submit_time") |
| | | private Date submitTime; |
| | | |
| | | /** doing / submitted */ |
| | | @TableField("test_status") |
| | | private String testStatus; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 自我检测试题快照 tms_self_test_item |
| | | */ |
| | | @Data |
| | | @TableName("tms_self_test_item") |
| | | public class TmsSelfTestItemEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("question_id") |
| | | private String questionId; |
| | | |
| | | @TableField("question_no") |
| | | private String questionNo; |
| | | |
| | | @TableField("question_type") |
| | | private String questionType; |
| | | |
| | | @TableField("difficulty") |
| | | private String difficulty; |
| | | |
| | | @TableField("stem") |
| | | private String stem; |
| | | |
| | | @TableField("options_json") |
| | | private String optionsJson; |
| | | |
| | | @TableField("correct_answer") |
| | | private String correctAnswer; |
| | | |
| | | @TableField("user_answer") |
| | | private String userAnswer; |
| | | |
| | | /** 1 / 0 */ |
| | | @TableField("is_right") |
| | | private String isRight; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 签到方式配置 tms_sign_mode |
| | | */ |
| | | @Data |
| | | @TableName("tms_sign_mode") |
| | | public class TmsSignModeEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("mode_code") |
| | | private String modeCode; |
| | | |
| | | @TableField("mode_name") |
| | | private String modeName; |
| | | |
| | | @TableField("tip_seconds") |
| | | private Integer tipSeconds; |
| | | |
| | | @TableField("match_roster") |
| | | private String matchRoster; |
| | | |
| | | @TableField("enabled") |
| | | private String enabled; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训任务 tms_task |
| | | */ |
| | | @Data |
| | | @TableName("tms_task") |
| | | public class TmsTaskEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("task_no") |
| | | private String taskNo; |
| | | |
| | | @TableField("category") |
| | | private String category; |
| | | |
| | | @TableField("train_type") |
| | | private String trainType; |
| | | |
| | | @TableField("source_type") |
| | | private String sourceType; |
| | | |
| | | @TableField("source_plan_no") |
| | | private String sourcePlanNo; |
| | | |
| | | @TableField("source_id") |
| | | private String sourceId; |
| | | |
| | | @TableField("source_item_id") |
| | | private String sourceItemId; |
| | | |
| | | @TableField("subject") |
| | | private String subject; |
| | | |
| | | @TableField("key_points") |
| | | private String keyPoints; |
| | | |
| | | @TableField("material_id") |
| | | private String materialId; |
| | | |
| | | @TableField("material_no") |
| | | private String materialNo; |
| | | |
| | | @TableField("place_id") |
| | | private String placeId; |
| | | |
| | | @TableField("place_no") |
| | | private String placeNo; |
| | | |
| | | @TableField("place_name") |
| | | private String placeName; |
| | | |
| | | @TableField("start_time") |
| | | private Date startTime; |
| | | |
| | | @TableField("end_time") |
| | | private Date endTime; |
| | | |
| | | @TableField("close_time") |
| | | private Date closeTime; |
| | | |
| | | @TableField("paper_id") |
| | | private String paperId; |
| | | |
| | | @TableField("paper_name") |
| | | private String paperName; |
| | | |
| | | @TableField("exam_start") |
| | | private Date examStart; |
| | | |
| | | @TableField("exam_end") |
| | | private Date examEnd; |
| | | |
| | | @TableField("teacher_id") |
| | | private String teacherId; |
| | | |
| | | @TableField("train_mode") |
| | | private String trainMode; |
| | | |
| | | @TableField("eval_mode") |
| | | private String evalMode; |
| | | |
| | | /** 培训对象用户ID,逗号分隔 */ |
| | | @TableField("trainees") |
| | | private String trainees; |
| | | |
| | | @TableField("task_kind") |
| | | private String taskKind; |
| | | |
| | | @TableField("prev_task_no") |
| | | private String prevTaskNo; |
| | | |
| | | /** draft / published / cancelled / done */ |
| | | @TableField("publish_status") |
| | | private String publishStatus; |
| | | |
| | | @TableField("publish_time") |
| | | private Date publishTime; |
| | | |
| | | @TableField("retake_limit") |
| | | private Integer retakeLimit; |
| | | |
| | | @TableField("pass_score") |
| | | private BigDecimal passScore; |
| | | |
| | | @TableField("quiz_ratio") |
| | | private Integer quizRatio; |
| | | |
| | | @TableField("sign_rate") |
| | | private Integer signRate; |
| | | |
| | | @TableField("pass_rate") |
| | | private Integer passRate; |
| | | |
| | | @TableField("unfinished_rate") |
| | | private Integer unfinishedRate; |
| | | |
| | | @TableField("overdue_remind") |
| | | private String overdueRemind; |
| | | |
| | | @TableField("invite_flag") |
| | | private String inviteFlag; |
| | | |
| | | @TableField("dms_file_id") |
| | | private String dmsFileId; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField("f_flow_id") |
| | | private String flowId; |
| | | |
| | | @TableField("f_flow_task_id") |
| | | private String flowTaskId; |
| | | |
| | | @TableField("f_flow_state") |
| | | private Integer flowState; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训任务教材/附件 tms_task_file |
| | | */ |
| | | @Data |
| | | @TableName("tms_task_file") |
| | | public class TmsTaskFileEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("f_foreign_id") |
| | | private String foreignId; |
| | | |
| | | @TableField("file_no") |
| | | private String fileNo; |
| | | |
| | | @TableField("file_name") |
| | | private String fileName; |
| | | |
| | | @TableField("material_id") |
| | | private String materialId; |
| | | |
| | | @TableField("file_json") |
| | | private String fileJson; |
| | | |
| | | @TableField("can_download") |
| | | private String canDownload; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 培训方式配置 tms_train_mode |
| | | */ |
| | | @Data |
| | | @TableName("tms_train_mode") |
| | | public class TmsTrainModeEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | @TableField(value = "f_tenant_id", fill = FieldFill.INSERT) |
| | | private String tenantId; |
| | | |
| | | @TableField("mode_code") |
| | | private String modeCode; |
| | | |
| | | @TableField("mode_name") |
| | | private String modeName; |
| | | |
| | | /** scan / both */ |
| | | @TableField("sign_rule") |
| | | private String signRule; |
| | | |
| | | @TableField("allow_guest_sign") |
| | | private String allowGuestSign; |
| | | |
| | | @TableField("same_day_required") |
| | | private String sameDayRequired; |
| | | |
| | | /** 发布任务时是否必须选择培训地点(地点数据来自 tms_place) */ |
| | | @TableField("place_required") |
| | | private String placeRequired; |
| | | |
| | | @TableField("sort_no") |
| | | private Integer sortNo; |
| | | |
| | | @TableField("enabled") |
| | | private String enabled; |
| | | |
| | | @TableField("remark") |
| | | private String remark; |
| | | |
| | | @TableField(value = "f_creator_user_id", fill = FieldFill.INSERT) |
| | | private String creatorUserId; |
| | | |
| | | @TableField(value = "f_creator_time", fill = FieldFill.INSERT) |
| | | private Date creatorTime; |
| | | |
| | | @TableField(value = "f_last_modify_user_id", fill = FieldFill.UPDATE) |
| | | private String lastModifyUserId; |
| | | |
| | | @TableField(value = "f_last_modify_time", fill = FieldFill.UPDATE) |
| | | private Date lastModifyTime; |
| | | |
| | | @TableLogic |
| | | @TableField("f_delete_mark") |
| | | private Integer deleteMark; |
| | | |
| | | @TableField("f_delete_user_id") |
| | | private String deleteUserId; |
| | | |
| | | @TableField("f_delete_time") |
| | | private Date deleteTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 培训师档案。年计划子表 teacher_id 存的是本表 f_id,系统用户在 trainer_id。 |
| | | */ |
| | | @Data |
| | | @TableName("tms_trainer") |
| | | public class TmsTrainerEntity { |
| | | |
| | | @TableId("f_id") |
| | | private String id; |
| | | |
| | | /** 对应系统用户 */ |
| | | @TableField("trainer_id") |
| | | private String userId; |
| | | |
| | | @TableField("trainer_name") |
| | | private String trainerName; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.archive; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 个人培训档案-培训目录 |
| | | */ |
| | | @Data |
| | | public class TmsTrainArchiveCatalogVO { |
| | | |
| | | @Schema(description = "目录ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "培训记录编号") |
| | | private String recordNo; |
| | | |
| | | @Schema(description = "培训类型") |
| | | private String trainType; |
| | | |
| | | @Schema(description = "培训内容") |
| | | private String trainContent; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "培训师") |
| | | private String trainerName; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "是否合格 0/1") |
| | | private String passFlag; |
| | | |
| | | @Schema(description = "培训开始时间") |
| | | private String trainStart; |
| | | |
| | | @Schema(description = "培训结束时间") |
| | | private String trainEnd; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.archive; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 个人培训档案详情 |
| | | */ |
| | | @Data |
| | | public class TmsTrainArchiveDetailVO { |
| | | |
| | | @Schema(description = "基本信息") |
| | | private TmsTrainArchiveProfileVO profile; |
| | | |
| | | @Schema(description = "工作履历") |
| | | private List<TmsTrainArchiveWorkVO> workList = new ArrayList<>(); |
| | | |
| | | @Schema(description = "培训目录") |
| | | private List<TmsTrainArchiveCatalogVO> catalogList = new ArrayList<>(); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.archive; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 个人培训档案-基本信息 |
| | | */ |
| | | @Data |
| | | public class TmsTrainArchiveProfileVO { |
| | | |
| | | @Schema(description = "档案ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "档案编号") |
| | | private String archiveNo; |
| | | |
| | | @Schema(description = "建档时间 yyyy-MM-dd") |
| | | private String archiveDate; |
| | | |
| | | @Schema(description = "员工ID") |
| | | private String userId; |
| | | |
| | | @Schema(description = "员工姓名") |
| | | private String userName; |
| | | |
| | | @Schema(description = "所属部门") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "主要岗位") |
| | | private String mainPostName; |
| | | |
| | | @Schema(description = "兼职岗位") |
| | | private String partPostName; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.archive; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 个人培训档案-工作履历 |
| | | */ |
| | | @Data |
| | | public class TmsTrainArchiveWorkVO { |
| | | |
| | | @Schema(description = "履历ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "入职时间") |
| | | private String hireDate; |
| | | |
| | | @Schema(description = "离职时间") |
| | | private String leaveDate; |
| | | |
| | | @Schema(description = "公司名称") |
| | | private String companyName; |
| | | |
| | | @Schema(description = "任职岗位") |
| | | private String postName; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.course; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsCourseForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "课程编号") |
| | | private String courseNo; |
| | | |
| | | @Schema(description = "课程名称") |
| | | private String courseName; |
| | | |
| | | @Schema(description = "课程分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训方式编码") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "考核方式编码") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "关联试卷ID") |
| | | private String paperId; |
| | | |
| | | @Schema(description = "关联试卷名称") |
| | | private String paperName; |
| | | |
| | | @Schema(description = "学时") |
| | | private BigDecimal hours; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.course; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsCourseQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编号/名称)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "课程分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.course; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsPaperOption { |
| | | |
| | | @Schema(description = "试卷ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "试卷名称") |
| | | private String fullName; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.evalmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsEvalModeForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "方式编码") |
| | | private String modeCode; |
| | | |
| | | @Schema(description = "方式名称") |
| | | private String modeName; |
| | | |
| | | @Schema(description = "需要试卷 0/1") |
| | | private String needPaper; |
| | | |
| | | @Schema(description = "需要提问预设 0/1") |
| | | private String needQuiz; |
| | | |
| | | @Schema(description = "需要实操评分 0/1") |
| | | private String needPractice; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.evalmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsEvalModeQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编码/名称)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamAttemptVO { |
| | | |
| | | @Schema(description = "答卷ID") |
| | | private String examId; |
| | | |
| | | @Schema(description = "考试序号,1=首考") |
| | | private Integer attemptNo; |
| | | |
| | | @Schema(description = "考试类型文案") |
| | | private String attemptLabel; |
| | | |
| | | @Schema(description = "状态 notStarted/doing/submitted") |
| | | private String status; |
| | | |
| | | @Schema(description = "阅卷状态 auto/pending/graded") |
| | | private String gradeStatus; |
| | | |
| | | private String startTime; |
| | | private String submitTime; |
| | | private BigDecimal gotScore; |
| | | private String passFlag; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamDetailVO { |
| | | |
| | | private String id; |
| | | private String paperId; |
| | | private String paperName; |
| | | private String status; |
| | | private String examTimeText; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private Integer durationMin; |
| | | private BigDecimal gotScore; |
| | | private String startTime; |
| | | private String submitTime; |
| | | private String passFlag; |
| | | private String gradeStatus; |
| | | private String examId; |
| | | |
| | | @Schema(description = "当前/最近一次考试序号,1=首考") |
| | | private Integer attemptNo; |
| | | |
| | | @Schema(description = "考试类型文案:首考/补考") |
| | | private String attemptLabel; |
| | | |
| | | @Schema(description = "已交卷次数") |
| | | private Integer submittedCount; |
| | | |
| | | @Schema(description = "允许补考次数上限(不含首考)") |
| | | private Integer retakeLimit; |
| | | |
| | | @Schema(description = "剩余可补考次数") |
| | | private Integer remainingRetakes; |
| | | |
| | | @Schema(description = "当前是否可发起补考") |
| | | private Boolean canRetake; |
| | | |
| | | @Schema(description = "历史答卷(按 attemptNo 升序)") |
| | | private List<TmsOnlineExamAttemptVO> attempts; |
| | | |
| | | /** 已交卷时返回答题回顾 */ |
| | | private List<TmsOnlineExamQuestionVO> questions; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamGradeItemVO { |
| | | |
| | | private String id; |
| | | private String correctAnswer; |
| | | private BigDecimal gotScore; |
| | | /** 客观题对错;主观题为空 */ |
| | | private Boolean right; |
| | | private Boolean subjective; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamListVO { |
| | | |
| | | @Schema(description = "列表主键,当前为试卷ID") |
| | | private String id; |
| | | |
| | | private String paperId; |
| | | private String paperName; |
| | | private String status; |
| | | private String examStart; |
| | | private String examEnd; |
| | | private String examTimeText; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private Integer durationMin; |
| | | private BigDecimal gotScore; |
| | | private String passFlag; |
| | | private String examId; |
| | | |
| | | @Schema(description = "当前/最近一次考试序号,1=首考") |
| | | private Integer attemptNo; |
| | | |
| | | @Schema(description = "考试类型文案:首考/补考") |
| | | private String attemptLabel; |
| | | |
| | | @Schema(description = "已交卷次数") |
| | | private Integer submittedCount; |
| | | |
| | | @Schema(description = "允许补考次数上限(不含首考)") |
| | | private Integer retakeLimit; |
| | | |
| | | @Schema(description = "剩余可补考次数") |
| | | private Integer remainingRetakes; |
| | | |
| | | @Schema(description = "当前是否可发起补考") |
| | | private Boolean canRetake; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamOptionVO { |
| | | |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | /** 仅交卷回顾时回传 */ |
| | | private String isCorrect; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamPaperVO { |
| | | |
| | | private String examId; |
| | | private String paperId; |
| | | private String paperName; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private Integer durationMin; |
| | | /** 开考时间 yyyy-MM-dd HH:mm:ss,倒计时按此续算 */ |
| | | private String startTime; |
| | | /** 剩余秒数,null=不限时 */ |
| | | private Integer remainSeconds; |
| | | |
| | | @Schema(description = "开考时发现已超时并已自动交卷") |
| | | private Boolean autoSubmitted; |
| | | |
| | | @Schema(description = "考试序号,1=首考") |
| | | private Integer attemptNo; |
| | | |
| | | @Schema(description = "考试类型文案:首考/补考") |
| | | private String attemptLabel; |
| | | |
| | | private List<TmsOnlineExamQuestionVO> questions; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsOnlineExamQuery extends Pagination { |
| | | |
| | | @Schema(description = "试卷名称") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "notStarted/doing/submitted") |
| | | private String status; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamQuestionVO { |
| | | |
| | | /** 答卷快照行ID,作答按此提交 */ |
| | | private String id; |
| | | private String questionId; |
| | | private String questionType; |
| | | private String stem; |
| | | private BigDecimal score; |
| | | private BigDecimal gotScore; |
| | | private String userAnswer; |
| | | private String correctAnswer; |
| | | /** 客观题对错;主观题为空 */ |
| | | private Boolean right; |
| | | private Boolean subjective; |
| | | private List<TmsOnlineExamOptionVO> options; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamStartForm { |
| | | |
| | | /** 列表行ID,当前为试卷ID */ |
| | | private String myPaperId; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.Map; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamSubmitForm { |
| | | |
| | | /** 快照行ID -> 选项标签 / 标签数组 / 填空文本 */ |
| | | private Map<String, Object> answers; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.exam; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsOnlineExamSubmitResultVO { |
| | | |
| | | private BigDecimal objectiveScore; |
| | | private BigDecimal totalScore; |
| | | private String passFlag; |
| | | private String gradeStatus; |
| | | private Boolean pendingGrade; |
| | | private List<TmsOnlineExamGradeItemVO> items; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamGradeDetailVO { |
| | | private String examId; |
| | | private String sessionId; |
| | | private String paperName; |
| | | private String userName; |
| | | private String deptName; |
| | | private String submitTime; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private BigDecimal objectiveScore; |
| | | private BigDecimal subjectiveScore; |
| | | private String gradeStatus; |
| | | private List<TmsExamGradeItemVO> items; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsExamGradeExamVO { |
| | | private String id; |
| | | private String sessionId; |
| | | private String userId; |
| | | private String userName; |
| | | private String deptName; |
| | | private String submitTime; |
| | | private BigDecimal objectiveScore; |
| | | private BigDecimal subjectiveScore; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private String gradeStatus; |
| | | private String passFlag; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamGradeItemVO { |
| | | private String id; |
| | | private String questionType; |
| | | private String stem; |
| | | private List<TmsExamGradeOptionVO> options; |
| | | private String correctAnswer; |
| | | private String userAnswer; |
| | | private BigDecimal score; |
| | | private BigDecimal gotScore; |
| | | private String isSubjective; |
| | | private String analysis; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsExamGradeOptionVO { |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsExamGradeQuery extends Pagination { |
| | | private String keyword; |
| | | private String taskNo; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsExamGradeSessionVO { |
| | | /** 场次ID,当前用试卷ID */ |
| | | private String id; |
| | | private String taskNo; |
| | | private String taskSubject; |
| | | private String paperId; |
| | | private String paperName; |
| | | private Integer durationMin; |
| | | private String examStart; |
| | | private String examEnd; |
| | | private BigDecimal totalScore; |
| | | private BigDecimal passScore; |
| | | private Integer pendingCount; |
| | | private Integer submittedCount; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamGradeSubmitForm { |
| | | private String examId; |
| | | private List<ItemScore> items; |
| | | |
| | | @Data |
| | | public static class ItemScore { |
| | | private String id; |
| | | private BigDecimal gotScore; |
| | | } |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examgrade; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsExamGradeSubmitResultVO { |
| | | private String examId; |
| | | private BigDecimal subjectiveScore; |
| | | private BigDecimal totalScore; |
| | | private String passFlag; |
| | | private String gradeStatus; |
| | | /** 同场次下一份待批改答卷,无则空 */ |
| | | private String nextExamId; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamScoreDetailVO extends TmsExamScoreSummaryVO { |
| | | |
| | | private List<TmsExamScoreRowVO> rows; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamScoreExportForm { |
| | | |
| | | /** 汇总行ID,当前为试卷ID */ |
| | | private List<String> ids; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsExamScoreOptionVO { |
| | | |
| | | private String optionLabel; |
| | | private String optionContent; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamScorePaperItemVO { |
| | | |
| | | private String id; |
| | | private String questionType; |
| | | private String stem; |
| | | private List<TmsExamScoreOptionVO> options; |
| | | private String correctAnswer; |
| | | private String userAnswer; |
| | | private BigDecimal score; |
| | | private BigDecimal gotScore; |
| | | private Boolean isCorrect; |
| | | private String isSubjective; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsExamScorePaperVO { |
| | | |
| | | private String examId; |
| | | private String summaryId; |
| | | private String taskSubject; |
| | | private String taskNo; |
| | | private String paperName; |
| | | private String userId; |
| | | private String userName; |
| | | private String deptName; |
| | | private String examStart; |
| | | private String examEnd; |
| | | private BigDecimal score; |
| | | private BigDecimal passScore; |
| | | private BigDecimal totalScore; |
| | | private String passFlag; |
| | | private String sourceIp; |
| | | private List<TmsExamScorePaperItemVO> items; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsExamScoreQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词:试卷名称/主题") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "培训编号(当前无任务时按试卷ID匹配)") |
| | | private String taskNo; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsExamScoreRowVO { |
| | | |
| | | private String id; |
| | | private String summaryId; |
| | | private String taskSubject; |
| | | private String taskNo; |
| | | private String userId; |
| | | private String userName; |
| | | private String deptName; |
| | | private String examStart; |
| | | private String examEnd; |
| | | private BigDecimal score; |
| | | private BigDecimal passScore; |
| | | private String passFlag; |
| | | private String sourceIp; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.examscore; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsExamScoreSummaryVO { |
| | | |
| | | private String id; |
| | | private String taskSubject; |
| | | private String taskNo; |
| | | private String paperId; |
| | | private String paperName; |
| | | private Boolean paperInvalid; |
| | | private Integer participantCount; |
| | | private Integer failCount; |
| | | private BigDecimal maxScore; |
| | | private BigDecimal minScore; |
| | | private BigDecimal avgScore; |
| | | private BigDecimal passScore; |
| | | private BigDecimal totalScore; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.paper; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsPaperForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "试卷编号") |
| | | private String paperNo; |
| | | |
| | | @Schema(description = "试卷名称") |
| | | private String paperName; |
| | | |
| | | @Schema(description = "状态 open/closed/invalid") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "考试时长(分钟)") |
| | | private Integer durationMin; |
| | | |
| | | @Schema(description = "开考时间 yyyy-MM-dd HH:mm:ss") |
| | | private String examStart; |
| | | |
| | | @Schema(description = "结考时间 yyyy-MM-dd HH:mm:ss") |
| | | private String examEnd; |
| | | |
| | | @Schema(description = "成绩公布时间 yyyy-MM-dd HH:mm:ss") |
| | | private String scorePublishTime; |
| | | |
| | | @Schema(description = "合格分数") |
| | | private BigDecimal passScore; |
| | | |
| | | @Schema(description = "试卷总分") |
| | | private BigDecimal totalScore; |
| | | |
| | | @Schema(description = "试题排序 paper/random") |
| | | private String sortMode; |
| | | |
| | | @Schema(description = "含主观题 1/0") |
| | | private String hasSubjective; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | |
| | | @Schema(description = "创建时间") |
| | | private String creatorTime; |
| | | |
| | | @Schema(description = "试题数量(列表用)") |
| | | private Integer questionCount; |
| | | |
| | | @Schema(description = "章节及试题") |
| | | private List<TmsPaperSectionForm> sections; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.paper; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsPaperQuery extends Pagination { |
| | | |
| | | @Schema(description = "状态 open/closed/invalid") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "关键词(试卷编号/名称)") |
| | | private String keyword; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.paper; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsPaperQuestionForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "章节ID(前端临时 key 或已保存 ID)") |
| | | private String sectionId; |
| | | |
| | | @Schema(description = "试题ID") |
| | | private String questionId; |
| | | |
| | | @Schema(description = "试题编号") |
| | | private String questionNo; |
| | | |
| | | @Schema(description = "题干") |
| | | private String stem; |
| | | |
| | | @Schema(description = "题型") |
| | | private String questionType; |
| | | |
| | | @Schema(description = "题库名称") |
| | | private String bankName; |
| | | |
| | | @Schema(description = "本题分值") |
| | | private BigDecimal score; |
| | | |
| | | @Schema(description = "排序") |
| | | private Integer sortNo; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.paper; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsPaperSectionForm { |
| | | |
| | | @Schema(description = "主键(前端临时 key 或已保存 ID)") |
| | | private String id; |
| | | |
| | | @Schema(description = "章节名称") |
| | | private String sectionName; |
| | | |
| | | @Schema(description = "默认题型") |
| | | private String questionType; |
| | | |
| | | @Schema(description = "排序") |
| | | private Integer sortNo; |
| | | |
| | | @Schema(description = "章节内试题") |
| | | private List<TmsPaperQuestionForm> questions; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskFileForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "文件名") |
| | | private String name; |
| | | |
| | | @Schema(description = "预览地址") |
| | | private String url; |
| | | |
| | | @Schema(description = "文件扩展名") |
| | | private String fileExt; |
| | | |
| | | @Schema(description = "预览类型 image/video/audio/pdf/office/other") |
| | | private String previewType; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskLearnForm { |
| | | |
| | | @Schema(description = "本次累计增加的学习秒数") |
| | | private Integer seconds; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskLearnResult { |
| | | |
| | | @Schema(description = "已学秒数") |
| | | private Integer learnedSeconds; |
| | | |
| | | @Schema(description = "要求秒数") |
| | | private Integer requiredSeconds; |
| | | |
| | | @Schema(description = "学习进度") |
| | | private String learnStatus; |
| | | |
| | | @Schema(description = "个人状态") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "是否可考") |
| | | private Boolean canExam; |
| | | |
| | | @Schema(description = "考试提示") |
| | | private String examTip; |
| | | |
| | | @Schema(description = "本次是否刚自动完成") |
| | | private Boolean autoCompleted; |
| | | |
| | | @Schema(description = "系统提示") |
| | | private String message; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskMineForm { |
| | | |
| | | @Schema(description = "个人任务ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "任务ID") |
| | | private String taskId; |
| | | |
| | | @Schema(description = "培训编号") |
| | | private String taskNo; |
| | | |
| | | @Schema(description = "培训主题") |
| | | private String subject; |
| | | |
| | | @Schema(description = "培训分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "开始时间") |
| | | private String startTime; |
| | | |
| | | @Schema(description = "结束时间") |
| | | private String endTime; |
| | | |
| | | @Schema(description = "关闭时间") |
| | | private String closeTime; |
| | | |
| | | @Schema(description = "地点") |
| | | private String placeName; |
| | | |
| | | @Schema(description = "培训要点") |
| | | private String keyPoints; |
| | | |
| | | @Schema(description = "个人状态") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "签到时间") |
| | | private String signTime; |
| | | |
| | | @Schema(description = "是否已签到") |
| | | private Boolean signed; |
| | | |
| | | @Schema(description = "当前是否可在线签到") |
| | | private Boolean canSign; |
| | | |
| | | @Schema(description = "不可签到原因") |
| | | private String signTip; |
| | | |
| | | @Schema(description = "学习秒数") |
| | | private Integer learnSeconds; |
| | | |
| | | @Schema(description = "学习进度:未学习/学习中/已学满") |
| | | private String learnStatus; |
| | | |
| | | @Schema(description = "要求课时") |
| | | private java.math.BigDecimal requiredHours; |
| | | |
| | | @Schema(description = "要求学习秒数") |
| | | private Integer requiredSeconds; |
| | | |
| | | @Schema(description = "已学秒数") |
| | | private Integer learnedSeconds; |
| | | |
| | | @Schema(description = "成绩") |
| | | private BigDecimal examScore; |
| | | |
| | | @Schema(description = "是否合格") |
| | | private String passFlag; |
| | | |
| | | @Schema(description = "试卷ID") |
| | | private String paperId; |
| | | |
| | | @Schema(description = "试卷名称") |
| | | private String paperName; |
| | | |
| | | @Schema(description = "是否可考") |
| | | private Boolean canExam; |
| | | |
| | | @Schema(description = "不可考原因") |
| | | private String examTip; |
| | | |
| | | @Schema(description = "教材/附件") |
| | | private List<TmsPersonTaskFileForm> files; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsPersonTaskQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编号/主题)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "个人任务状态") |
| | | private String bizStatus; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.person; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskSignResult { |
| | | |
| | | @Schema(description = "签到时间") |
| | | private String signTime; |
| | | |
| | | @Schema(description = "个人状态") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "是否已签到") |
| | | private Boolean signed; |
| | | |
| | | @Schema(description = "当前是否可在线签到") |
| | | private Boolean canSign; |
| | | |
| | | @Schema(description = "不可签到原因") |
| | | private String signTip; |
| | | |
| | | @Schema(description = "是否可考") |
| | | private Boolean canExam; |
| | | |
| | | @Schema(description = "考试提示") |
| | | private String examTip; |
| | | |
| | | @Schema(description = "系统提示") |
| | | private String message; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.place; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 培训地点下拉选项 |
| | | */ |
| | | @Data |
| | | public class TmsPlaceOption { |
| | | |
| | | @Schema(description = "地点ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "地点编号") |
| | | private String placeNo; |
| | | |
| | | @Schema(description = "地点名称(下拉展示)") |
| | | private String fullName; |
| | | |
| | | @Schema(description = "地点组别") |
| | | private String placeGroup; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.question; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsQuestionBankOption { |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "题库名称") |
| | | private String fullName; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.question; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 试题详情/保存体(与前端 QuestionEntity 对齐) |
| | | */ |
| | | @Data |
| | | public class TmsQuestionForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "试题编号") |
| | | private String questionNo; |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "题库名称") |
| | | private String bankName; |
| | | |
| | | @Schema(description = "题型 single/multi/judge/blank/essay") |
| | | private String questionType; |
| | | |
| | | @Schema(description = "难度") |
| | | private String difficulty; |
| | | |
| | | @Schema(description = "来源") |
| | | private String sourceType; |
| | | |
| | | @Schema(description = "题干") |
| | | private String stem; |
| | | |
| | | @Schema(description = "解析") |
| | | private String analysis; |
| | | |
| | | @Schema(description = "管理员用户ID") |
| | | private String adminUserId; |
| | | |
| | | @Schema(description = "管理员姓名") |
| | | private String adminUserName; |
| | | |
| | | @Schema(description = "状态") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "创建时间") |
| | | private String creatorTime; |
| | | |
| | | @Schema(description = "填空混杂批改(前端字段,当前库表未落库)") |
| | | private Boolean blankMixMode; |
| | | |
| | | @Schema(description = "选项") |
| | | private List<TmsQuestionOptionForm> options; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.question; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsQuestionOptionForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "序号") |
| | | private Integer sortNo; |
| | | |
| | | @Schema(description = "选项标识 A/B/T/F/1") |
| | | private String optionLabel; |
| | | |
| | | @Schema(description = "选项内容") |
| | | private String optionContent; |
| | | |
| | | @Schema(description = "是否正确 1/0") |
| | | private String isCorrect; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.question; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsQuestionQuery extends Pagination { |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "题型") |
| | | private String questionType; |
| | | |
| | | @Schema(description = "状态 open/closed/invalid") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "管理员用户ID") |
| | | private String adminUserId; |
| | | |
| | | @Schema(description = "关键词(编号/题干)") |
| | | private String keyword; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.record; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsRecordAttachmentForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "文件名") |
| | | private String name; |
| | | |
| | | @Schema(description = "预览/下载地址") |
| | | private String url; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.record; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsRecordForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "记录编号") |
| | | private String recordNo; |
| | | |
| | | @Schema(description = "任务ID") |
| | | private String taskId; |
| | | |
| | | @Schema(description = "任务编号") |
| | | private String taskNo; |
| | | |
| | | @Schema(description = "培训分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训主题") |
| | | private String subject; |
| | | |
| | | @Schema(description = "培训师用户ID") |
| | | private String teacherId; |
| | | |
| | | @Schema(description = "培训师姓名") |
| | | private String trainerName; |
| | | |
| | | @Schema(description = "培训类型") |
| | | private String trainType; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "开始时间") |
| | | private String startTime; |
| | | |
| | | @Schema(description = "结束时间") |
| | | private String endTime; |
| | | |
| | | @Schema(description = "关闭时间") |
| | | private String closeTime; |
| | | |
| | | @Schema(description = "归档状态") |
| | | private String archiveStatus; |
| | | |
| | | @Schema(description = "签到率") |
| | | private Integer signRate; |
| | | |
| | | @Schema(description = "合格率") |
| | | private Integer passRate; |
| | | |
| | | @Schema(description = "培训要点") |
| | | private String keyPoints; |
| | | |
| | | @Schema(description = "培训地点") |
| | | private String placeName; |
| | | |
| | | @Schema(description = "培训对象姓名串") |
| | | private String trainees; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | |
| | | @Schema(description = "附件") |
| | | private List<TmsRecordAttachmentForm> attachments; |
| | | |
| | | @Schema(description = "参加人员") |
| | | private List<TmsRecordParticipantForm> participants; |
| | | |
| | | @Schema(description = "签到名单") |
| | | private List<TmsRecordSignForm> signList; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.record; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsRecordParticipantForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "用户ID") |
| | | private String userId; |
| | | |
| | | @Schema(description = "姓名") |
| | | private String userName; |
| | | |
| | | @Schema(description = "部门") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "签到日期") |
| | | private String signDate; |
| | | |
| | | @Schema(description = "考试成绩") |
| | | private BigDecimal examScore; |
| | | |
| | | @Schema(description = "是否合格 1/0") |
| | | private String passFlag; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.record; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsRecordQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编号/主题)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "编号") |
| | | private String recordNo; |
| | | |
| | | @Schema(description = "培训分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训师姓名(模糊,后置过滤)") |
| | | private String trainerName; |
| | | |
| | | @Schema(description = "列表模式 main/ready/archived") |
| | | private String listMode; |
| | | |
| | | @Schema(description = "归档状态") |
| | | private String archiveStatus; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.record; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsRecordSignForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "姓名") |
| | | private String userName; |
| | | |
| | | @Schema(description = "部门") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "签到时间") |
| | | private String signTime; |
| | | |
| | | @Schema(description = "签到方式") |
| | | private String signMode; |
| | | |
| | | @Schema(description = "旁听标志") |
| | | private String guestFlag; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 试卷选项(与前端对齐) |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestOptionVO { |
| | | |
| | | @Schema(description = "选项标识") |
| | | private String optionLabel; |
| | | |
| | | @Schema(description = "选项内容") |
| | | private String optionContent; |
| | | |
| | | @Schema(description = "是否正确 1/0") |
| | | private String isCorrect; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 抽题结果 / 检测详情(回顾) |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestPaperVO { |
| | | |
| | | @Schema(description = "答卷ID(即 paperId)") |
| | | private String paperId; |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "题库名称") |
| | | private String bankName; |
| | | |
| | | @Schema(description = "状态 doing/submitted") |
| | | private String testStatus; |
| | | |
| | | @Schema(description = "总题数") |
| | | private Integer totalCount; |
| | | |
| | | @Schema(description = "正确题数") |
| | | private Integer correctCount; |
| | | |
| | | @Schema(description = "正确率") |
| | | private BigDecimal scoreRate; |
| | | |
| | | @Schema(description = "开始时间") |
| | | private String startTime; |
| | | |
| | | @Schema(description = "交卷时间") |
| | | private String submitTime; |
| | | |
| | | @Schema(description = "题目列表") |
| | | private List<TmsSelfTestQuestionVO> questions; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | /** |
| | | * 自我检测记录分页查询 |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsSelfTestQuery extends Pagination { |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "状态 doing/submitted") |
| | | private String testStatus; |
| | | |
| | | @Schema(description = "开始时间起 yyyy-MM-dd HH:mm:ss") |
| | | private String startTimeBegin; |
| | | |
| | | @Schema(description = "开始时间止 yyyy-MM-dd HH:mm:ss") |
| | | private String startTimeEnd; |
| | | |
| | | @Schema(description = "关键词(题库名称)") |
| | | private String keyword; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 试卷题目(含作答回顾字段) |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestQuestionVO { |
| | | |
| | | @Schema(description = "试题ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "试题编号") |
| | | private String questionNo; |
| | | |
| | | @Schema(description = "题型") |
| | | private String questionType; |
| | | |
| | | @Schema(description = "难度") |
| | | private String difficulty; |
| | | |
| | | @Schema(description = "题干") |
| | | private String stem; |
| | | |
| | | @Schema(description = "选项") |
| | | private List<TmsSelfTestOptionVO> options; |
| | | |
| | | @Schema(description = "正确答案(逗号分隔标签)") |
| | | private String correctAnswer; |
| | | |
| | | @Schema(description = "用户作答(逗号分隔标签)") |
| | | private String userAnswer; |
| | | |
| | | @Schema(description = "是否正确 1/0") |
| | | private String isRight; |
| | | |
| | | @Schema(description = "排序号") |
| | | private Integer sortNo; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 自我检测记录列表项 |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestRecordVO { |
| | | |
| | | @Schema(description = "答卷ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "题库名称") |
| | | private String bankName; |
| | | |
| | | @Schema(description = "总题数") |
| | | private Integer totalCount; |
| | | |
| | | @Schema(description = "正确题数") |
| | | private Integer correctCount; |
| | | |
| | | @Schema(description = "正确率") |
| | | private BigDecimal scoreRate; |
| | | |
| | | @Schema(description = "状态") |
| | | private String testStatus; |
| | | |
| | | @Schema(description = "开始时间") |
| | | private String startTime; |
| | | |
| | | @Schema(description = "交卷时间") |
| | | private String submitTime; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 开始自我检测请求 |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestStartForm { |
| | | |
| | | @Schema(description = "题库ID") |
| | | private String bankId; |
| | | |
| | | @Schema(description = "题库名称(可选)") |
| | | private String bankName; |
| | | |
| | | @Schema(description = "单选题设置") |
| | | private TmsSelfTestTypeSetting single; |
| | | |
| | | @Schema(description = "多选题设置") |
| | | private TmsSelfTestTypeSetting multi; |
| | | |
| | | @Schema(description = "判断题设置") |
| | | private TmsSelfTestTypeSetting judge; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 交卷请求 |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestSubmitForm { |
| | | |
| | | @Schema(description = "作答:questionId -> 选项标签(单选/判断) 或 标签数组(多选)") |
| | | private Map<String, Object> answers; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 交卷结果 |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestSubmitResultVO { |
| | | |
| | | @Schema(description = "答卷ID") |
| | | private String paperId; |
| | | |
| | | @Schema(description = "总题数") |
| | | private Integer totalCount; |
| | | |
| | | @Schema(description = "正确题数") |
| | | private Integer correctCount; |
| | | |
| | | @Schema(description = "正确率 0-100") |
| | | private BigDecimal scoreRate; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.selftest; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 题型抽题条件 |
| | | */ |
| | | @Data |
| | | public class TmsSelfTestTypeSetting { |
| | | |
| | | @Schema(description = "抽题数量") |
| | | private Integer count; |
| | | |
| | | @Schema(description = "难度 veryEasy/easier/normal/harder/veryHard") |
| | | private String difficulty; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.signmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsSignModeForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "方式编码") |
| | | private String modeCode; |
| | | |
| | | @Schema(description = "方式名称") |
| | | private String modeName; |
| | | |
| | | @Schema(description = "要点阅读秒数") |
| | | private Integer tipSeconds; |
| | | |
| | | @Schema(description = "须匹配名单 0/1") |
| | | private String matchRoster; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.signmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsSignModeQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编码/名称)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.task; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsFromPlanResult { |
| | | |
| | | @Schema(description = "来源计划ID") |
| | | private String planId; |
| | | |
| | | @Schema(description = "新生成任务数") |
| | | private int created; |
| | | |
| | | @Schema(description = "已存在而跳过的行数") |
| | | private int skipped; |
| | | |
| | | @Schema(description = "本次新生成的任务ID") |
| | | private List<String> taskIds = new ArrayList<>(); |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.task; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class TmsPersonTaskForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "用户ID") |
| | | private String userId; |
| | | |
| | | @Schema(description = "部门ID") |
| | | private String deptId; |
| | | |
| | | @Schema(description = "业务状态") |
| | | private String bizStatus; |
| | | |
| | | @Schema(description = "签到时间") |
| | | private String signTime; |
| | | |
| | | @Schema(description = "考试成绩") |
| | | private BigDecimal examScore; |
| | | |
| | | @Schema(description = "是否合格") |
| | | private String passFlag; |
| | | |
| | | @Schema(description = "旁听标志") |
| | | private String guestFlag; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.task; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsTaskFileForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "编号") |
| | | private String fileNo; |
| | | |
| | | @Schema(description = "文件名称") |
| | | private String fileName; |
| | | |
| | | @Schema(description = "教材ID") |
| | | private String materialId; |
| | | |
| | | @Schema(description = "附件") |
| | | private String fileJson; |
| | | |
| | | @Schema(description = "允许下载 1/0") |
| | | private String canDownload; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.task; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsTaskForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "培训编号") |
| | | private String taskNo; |
| | | |
| | | @Schema(description = "培训分类") |
| | | private String category; |
| | | |
| | | @Schema(description = "培训类型") |
| | | private String trainType; |
| | | |
| | | @Schema(description = "来源类型") |
| | | private String sourceType; |
| | | |
| | | @Schema(description = "来源计划号") |
| | | private String sourcePlanNo; |
| | | |
| | | @Schema(description = "来源单据ID") |
| | | private String sourceId; |
| | | |
| | | @Schema(description = "来源明细ID") |
| | | private String sourceItemId; |
| | | |
| | | @Schema(description = "培训主题") |
| | | private String subject; |
| | | |
| | | @Schema(description = "培训要点") |
| | | private String keyPoints; |
| | | |
| | | @Schema(description = "教材ID") |
| | | private String materialId; |
| | | |
| | | @Schema(description = "教材编号") |
| | | private String materialNo; |
| | | |
| | | @Schema(description = "地点ID") |
| | | private String placeId; |
| | | |
| | | @Schema(description = "地点编号") |
| | | private String placeNo; |
| | | |
| | | @Schema(description = "地点名称") |
| | | private String placeName; |
| | | |
| | | @Schema(description = "开始时间") |
| | | private String startTime; |
| | | |
| | | @Schema(description = "结束时间") |
| | | private String endTime; |
| | | |
| | | @Schema(description = "关闭时间") |
| | | private String closeTime; |
| | | |
| | | @Schema(description = "试卷ID") |
| | | private String paperId; |
| | | |
| | | @Schema(description = "试卷名称") |
| | | private String paperName; |
| | | |
| | | @Schema(description = "考试开始") |
| | | private String examStart; |
| | | |
| | | @Schema(description = "考试结束") |
| | | private String examEnd; |
| | | |
| | | @Schema(description = "培训师用户ID") |
| | | private String teacherId; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "培训对象用户ID列表(表单)") |
| | | private List<String> traineeIds; |
| | | |
| | | @Schema(description = "培训对象(逗号串,列表回显)") |
| | | private String trainees; |
| | | |
| | | @Schema(description = "任务类型 new/continue") |
| | | private String taskKind; |
| | | |
| | | @Schema(description = "上次任务编号") |
| | | private String prevTaskNo; |
| | | |
| | | @Schema(description = "发布状态") |
| | | private String publishStatus; |
| | | |
| | | @Schema(description = "发布时间") |
| | | private String publishTime; |
| | | |
| | | @Schema(description = "重考次数上限") |
| | | private Integer retakeLimit; |
| | | |
| | | @Schema(description = "合格分") |
| | | private BigDecimal passScore; |
| | | |
| | | @Schema(description = "提问占比") |
| | | private Integer quizRatio; |
| | | |
| | | @Schema(description = "逾期提醒 1/0") |
| | | private String overdueRemind; |
| | | |
| | | @Schema(description = "可邀请旁听 1/0") |
| | | private String inviteFlag; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | |
| | | @Schema(description = "创建时间") |
| | | private String creatorTime; |
| | | |
| | | @Schema(description = "创建人") |
| | | private String creatorUserId; |
| | | |
| | | @Schema(description = "个人任务人数(详情)") |
| | | private Integer personCount; |
| | | |
| | | @Schema(description = "个人任务列表(详情)") |
| | | private List<TmsPersonTaskForm> personTasks; |
| | | |
| | | @Schema(description = "教材/附件清单") |
| | | private List<TmsTaskFileForm> files; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.task; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsTaskQuery extends Pagination { |
| | | |
| | | @Schema(description = "发布状态 draft/published/cancelled/done") |
| | | private String publishStatus; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "来源类型") |
| | | private String sourceType; |
| | | |
| | | @Schema(description = "关键词(编号/主题)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "开始时间起 yyyy-MM-dd") |
| | | private String startTimeFrom; |
| | | |
| | | @Schema(description = "开始时间止 yyyy-MM-dd") |
| | | private String startTimeTo; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 培训方式表单 / 列表项 |
| | | */ |
| | | @Data |
| | | public class TmsTrainModeForm { |
| | | |
| | | @Schema(description = "主键") |
| | | private String id; |
| | | |
| | | @Schema(description = "方式编码") |
| | | private String modeCode; |
| | | |
| | | @Schema(description = "方式名称") |
| | | private String modeName; |
| | | |
| | | @Schema(description = "签到规则 scan/both") |
| | | private String signRule; |
| | | |
| | | @Schema(description = "允许名单外签到 0/1") |
| | | private String allowGuestSign; |
| | | |
| | | @Schema(description = "起止须同一天 0/1") |
| | | private String sameDayRequired; |
| | | |
| | | @Schema(description = "地点必填 0/1(任务选地点来自培训地点管理)") |
| | | private String placeRequired; |
| | | |
| | | @Schema(description = "排序") |
| | | private Integer sortNo; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | |
| | | @Schema(description = "备注") |
| | | private String remark; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainmode; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsTrainModeQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(编码/名称)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "启用状态 0/1") |
| | | private String enabled; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainrecord; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsTrainRecordCatalogItemVO { |
| | | |
| | | @Schema(description = "目录行ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "培训记录编号") |
| | | private String recordNo; |
| | | |
| | | @Schema(description = "培训类型") |
| | | private String trainType; |
| | | |
| | | @Schema(description = "培训内容") |
| | | private String trainContent; |
| | | |
| | | @Schema(description = "培训方式") |
| | | private String trainMode; |
| | | |
| | | @Schema(description = "培训师") |
| | | private String trainerName; |
| | | |
| | | @Schema(description = "考核方式") |
| | | private String evalMode; |
| | | |
| | | @Schema(description = "培训结果(成绩)") |
| | | private Object trainResult; |
| | | |
| | | @Schema(description = "是否合格 0/1/空") |
| | | private String passFlag; |
| | | |
| | | @Schema(description = "具体培训时间 yyyy-MM-dd") |
| | | private String trainDate; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainrecord; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class TmsTrainRecordCatalogVO { |
| | | |
| | | @Schema(description = "档案ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "姓名") |
| | | private String userName; |
| | | |
| | | @Schema(description = "档案编号") |
| | | private String archiveNo; |
| | | |
| | | @Schema(description = "培训目录") |
| | | private List<TmsTrainRecordCatalogItemVO> list; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainrecord; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class TmsTrainRecordListVO { |
| | | |
| | | @Schema(description = "档案ID") |
| | | private String id; |
| | | |
| | | @Schema(description = "档案编号") |
| | | private String archiveNo; |
| | | |
| | | @Schema(description = "进岗时间 yyyy-MM-dd") |
| | | private String postDate; |
| | | |
| | | @Schema(description = "姓名") |
| | | private String userName; |
| | | |
| | | @Schema(description = "部门") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "岗位") |
| | | private String postName; |
| | | |
| | | @Schema(description = "兼职岗位") |
| | | private String partPostName; |
| | | |
| | | @Schema(description = "专业") |
| | | private String major; |
| | | |
| | | @Schema(description = "学历") |
| | | private String education; |
| | | |
| | | @Schema(description = "技术职称") |
| | | private String techTitle; |
| | | |
| | | @Schema(description = "入职时间") |
| | | private String hireDate; |
| | | |
| | | @Schema(description = "离职日期") |
| | | private String leaveDate; |
| | | } |
| New file |
| | |
| | | package jnpf.tmsEntity.trainrecord; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jnpf.base.Pagination; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class TmsTrainRecordQuery extends Pagination { |
| | | |
| | | @Schema(description = "关键词(姓名/部门/档案编号)") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "档案编号") |
| | | private String archiveNo; |
| | | } |
| | |
| | | - file:${JNPF_CONFIG_DIR:config}/shared/mq.yaml |
| | | - file:${JNPF_CONFIG_DIR:config}/shared/discovery.yaml |
| | | - optional:file:${JNPF_CONFIG_DIR:config}/host-override/discovery.yaml |
| | | # 放在公共 datasource.yaml 之后,覆盖库名到 jnpf_tms |
| | | - optional:classpath:tms-datasource.yaml |
| | | |
| | | # 应用服务器 |
| | | server: |
| New file |
| | |
| | | # jnpf-tms 独立业务库(在 application.yml 中于公共 datasource.yaml 之后 import) |
| | | spring: |
| | | datasource: |
| | | db-name: jnpf_tms |
| | | db-schema: public |
| | | prepare-url: jdbc:postgresql://{host}:{port}/jnpf_tms?currentSchema=public |