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 getList(TmsTrainRecordQuery query) { LambdaQueryWrapper 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 page = personFileMapper.selectPage(new Page<>(current, size), qw); List rows = page.getRecords(); if (rows == null || rows.isEmpty()) { query.setData(Collections.emptyList(), page.getTotal()); return Collections.emptyList(); } Map deptNames = loadDeptNames(rows.stream() .map(TmsPersonFileEntity::getDeptId) .filter(StringUtil::isNotEmpty) .collect(Collectors.toSet())); Map postNames = loadPostNames(rows); List 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 items = new ArrayList<>(); Set 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 loadPersonTrains(TmsPersonFileEntity file) { LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); qw.and(w -> w.eq(TmsPersonTrainEntity::getArchiveId, file.getId()) .or() .eq(TmsPersonTrainEntity::getUserId, file.getUserId())); qw.orderByDesc(TmsPersonTrainEntity::getStartTime); List 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 loadFromPersonTasks(String userId) { if (StringUtil.isEmpty(userId)) { return Collections.emptyList(); } LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); qw.eq(TmsPersonTaskEntity::getUserId, userId); qw.ne(TmsPersonTaskEntity::getBizStatus, "cancelled"); qw.orderByDesc(TmsPersonTaskEntity::getCreatorTime); List pts = personTaskMapper.selectList(qw); if (pts == null || pts.isEmpty()) { return Collections.emptyList(); } Set taskIds = pts.stream() .map(TmsPersonTaskEntity::getTaskId) .filter(StringUtil::isNotEmpty) .collect(Collectors.toSet()); Map taskMap = new HashMap<>(); if (!taskIds.isEmpty()) { for (TmsTaskEntity t : taskMapper.selectBatchIds(taskIds)) { if (t != null) { taskMap.put(t.getId(), t); } } } Set teacherIds = taskMap.values().stream() .map(TmsTaskEntity::getTeacherId) .filter(StringUtil::isNotEmpty) .flatMap(s -> splitIds(s).stream()) .collect(Collectors.toSet()); Map teacherNames = loadUserNames(teacherIds); List 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 deptNames, Map 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 loadDeptNames(Set deptIds) { Map 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 loadPostNames(List rows) { Set 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 map = new HashMap<>(); if (ids.isEmpty()) { return map; } try { List 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 loadUserNames(Set userIds) { Map map = new HashMap<>(); if (userIds == null || userIds.isEmpty()) { return map; } try { List 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 nameMap) { if (StringUtil.isEmpty(teacherId)) { return ""; } List ids = splitIds(teacherId); if (ids.isEmpty()) { return teacherId; } List names = new ArrayList<>(); for (String id : ids) { names.add(nameMap.getOrDefault(id, id)); } return String.join("、", names); } private List splitIds(String raw) { if (StringUtil.isEmpty(raw)) { return Collections.emptyList(); } String[] parts = raw.split("[,,;;\\s]+"); List 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; } } }