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;
|
}
|
}
|
}
|