安全学习待办、sse、统计、列表

This commit is contained in:
2026-05-21 16:50:44 +08:00
parent 382b4ce73e
commit ecec4526fd
4 changed files with 192 additions and 93 deletions

View File

@@ -250,15 +250,10 @@ public class SysFlowServiceImpl implements ISysFlowService {
lqw.eq(SysFlowTask::getIsRead, Integer.valueOf(isRead)); lqw.eq(SysFlowTask::getIsRead, Integer.valueOf(isRead));
} }
lqw.orderByDesc(SysFlowTask::getCreateTime); lqw.orderByDesc(SysFlowTask::getCreateTime);
Page<SysFlowTask> page = taskMapper.selectPage(pageQuery.build(), lqw); List<SysFlowTask> records = taskMapper.selectList(lqw);
fillFlowInfo(records);
fillFlowInfo(page.getRecords()); filterInvalidTrainingStudyTodos(records);
int removedCount = filterInvalidTrainingStudyTodos(page.getRecords()); return buildTodoPage(records, pageQuery);
if (removedCount > 0) {
page.setTotal(Math.max(0, page.getTotal() - removedCount));
}
return TableDataInfo.build(page);
} }
@Override @Override
@@ -270,9 +265,9 @@ public class SysFlowServiceImpl implements ISysFlowService {
lqw.ne(SysFlowTask::getApproverId, loginUser.getBusinessUserId()); lqw.ne(SysFlowTask::getApproverId, loginUser.getBusinessUserId());
} }
lqw.orderByDesc(SysFlowTask::getCreateTime); lqw.orderByDesc(SysFlowTask::getCreateTime);
Page<SysFlowTask> page = taskMapper.selectPage(pageQuery.build(), lqw); List<SysFlowTask> records = taskMapper.selectList(lqw);
fillFlowInfo(records);
List<SysFlowTask> records = page.getRecords(); filterInvalidTrainingStudyTodos(records);
if (CollUtil.isNotEmpty(records)) { if (CollUtil.isNotEmpty(records)) {
List<String> approverIds = records.stream() List<String> approverIds = records.stream()
.map(SysFlowTask::getApproverId) .map(SysFlowTask::getApproverId)
@@ -315,9 +310,25 @@ public class SysFlowServiceImpl implements ISysFlowService {
} }
} }
fillFlowInfo(records); return buildTodoPage(records, pageQuery);
}
return TableDataInfo.build(page); private TableDataInfo<SysFlowTask> buildTodoPage(List<SysFlowTask> records, PageQuery pageQuery) {
if (CollUtil.isEmpty(records)) {
return new TableDataInfo<>(Collections.emptyList(), 0);
}
Integer pageNum = pageQuery.getPageNum();
Integer pageSize = pageQuery.getPageSize();
if (pageNum == null || pageNum <= 0) {
pageNum = PageQuery.DEFAULT_PAGE_NUM;
}
if (pageSize == null || pageSize <= 0) {
pageSize = PageQuery.DEFAULT_PAGE_SIZE;
}
int total = records.size();
int fromIndex = Math.min((pageNum - 1) * pageSize, total);
int toIndex = Math.min(fromIndex + pageSize, total);
return new TableDataInfo<>(records.subList(fromIndex, toIndex), total);
} }
/** /**
@@ -517,13 +528,6 @@ public class SysFlowServiceImpl implements ISysFlowService {
} }
} }
/**
* 过滤并清理失效的培训学习待办:
* - 计划已关闭
* - 计划已过期
* - 且未开启补学
* 同时要求学员参与记录仍然有效。
*/
private int filterInvalidTrainingStudyTodos(List<SysFlowTask> tasks) { private int filterInvalidTrainingStudyTodos(List<SysFlowTask> tasks) {
if (CollUtil.isEmpty(tasks)) { if (CollUtil.isEmpty(tasks)) {
return 0; return 0;
@@ -586,9 +590,6 @@ public class SysFlowServiceImpl implements ISysFlowService {
if (CollUtil.isEmpty(invalidTasks)) { if (CollUtil.isEmpty(invalidTasks)) {
return 0; return 0;
} }
for (SysFlowTask task : invalidTasks) {
deleteDirectTodo("TRAINING_STUDY", task.getBusinessId(), task.getApproverId());
}
tasks.removeIf(task -> invalidTasks.stream().anyMatch(invalid -> tasks.removeIf(task -> invalidTasks.stream().anyMatch(invalid ->
Objects.equals(invalid.getTaskId(), task.getTaskId()))); Objects.equals(invalid.getTaskId(), task.getTaskId())));
return invalidTasks.size(); return invalidTasks.size();
@@ -601,13 +602,16 @@ public class SysFlowServiceImpl implements ISysFlowService {
if (!Objects.equals(training.getIsEnabled(), 1L)) { if (!Objects.equals(training.getIsEnabled(), 1L)) {
return false; return false;
} }
if (Objects.equals(training.getIsMakeUp(), 1L)) {
return true;
}
if (training.getStartTime() == null || training.getEndTime() == null) { if (training.getStartTime() == null || training.getEndTime() == null) {
return false; return false;
} }
return training.getStartTime().compareTo(now) <= 0 && training.getEndTime().compareTo(now) >= 0; if (training.getStartTime().compareTo(now) > 0) {
return false;
}
if (training.getEndTime().compareTo(now) >= 0) {
return true;
}
return Objects.equals(training.getIsMakeUp(), 1L);
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ package com.hotwj.platform.securityManagement.training.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.flow.service.ISysFlowService;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager;
@@ -12,6 +13,8 @@ import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo;
import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo;
import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper;
import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; import com.hotwj.platform.securityManagement.training.service.IHotTrainingService;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant;
import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
@@ -20,7 +23,9 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -35,8 +40,12 @@ import java.util.Map;
@Service @Service
public class HotTrainingServiceImpl implements IHotTrainingService { public class HotTrainingServiceImpl implements IHotTrainingService {
private static final String FLOW_CODE_TRAINING_STUDY = "TRAINING_STUDY";
private final HotTrainingMapper baseMapper; private final HotTrainingMapper baseMapper;
private final HotCompanySafetyManagerMapper managerMapper; private final HotCompanySafetyManagerMapper managerMapper;
private final HotTrainingParticipantMapper participantMapper;
private final ISysFlowService flowService;
private final IHotSystemNotificationService notificationService; private final IHotSystemNotificationService notificationService;
/** /**
@@ -159,6 +168,8 @@ public class HotTrainingServiceImpl implements IHotTrainingService {
HotTraining before = baseMapper.selectById(update.getId()); HotTraining before = baseMapper.selectById(update.getId());
boolean ok = baseMapper.updateById(update) > 0; boolean ok = baseMapper.updateById(update) > 0;
if (ok && before != null) { if (ok && before != null) {
HotTraining after = baseMapper.selectById(update.getId());
syncTrainingStudyVisibilityOnGain(before, after);
Long oldHours = before.getClassHours(); Long oldHours = before.getClassHours();
Long newHours = update.getClassHours(); Long newHours = update.getClassHours();
boolean hoursConfigured = newHours != null && !newHours.equals(oldHours); boolean hoursConfigured = newHours != null && !newHours.equals(oldHours);
@@ -192,6 +203,102 @@ public class HotTrainingServiceImpl implements IHotTrainingService {
return ok; return ok;
} }
private void syncTrainingStudyVisibilityOnGain(HotTraining before, HotTraining after) {
if (before == null || after == null || after.getId() == null) {
return;
}
Date now = new Date();
if (isTrainingStudyAccessible(before, now) || !isTrainingStudyAccessible(after, now)) {
return;
}
List<HotTrainingParticipant> participants = participantMapper.selectList(
Wrappers.<HotTrainingParticipant>lambdaQuery()
.eq(HotTrainingParticipant::getTrainingId, after.getId())
.eq(HotTrainingParticipant::getIsDeleted, 0L)
);
if (participants == null || participants.isEmpty()) {
return;
}
Long companyId = after.getCompanyId();
String title = "安全教育培训:" + StringUtils.blankToDefault(after.getPlanName(), "培训");
String businessId = String.valueOf(after.getId());
List<String> driverIds = new ArrayList<>();
List<String> managerIds = new ArrayList<>();
for (HotTrainingParticipant participant : participants) {
if (participant == null || StringUtils.isBlank(participant.getUserId())) {
continue;
}
if (Long.valueOf(1L).equals(participant.getIsCompleted())) {
continue;
}
if (flowService.hasPendingTask(FLOW_CODE_TRAINING_STUDY, businessId, participant.getUserId())) {
collectNotificationReceiver(participant, driverIds, managerIds);
continue;
}
try {
flowService.pushDirectTodo(FLOW_CODE_TRAINING_STUDY, businessId, title, companyId, participant.getUserId());
collectNotificationReceiver(participant, driverIds, managerIds);
} catch (Exception e) {
log.warn("培训恢复可见后补发待办失败 trainingId={} userId={} err={}", after.getId(), participant.getUserId(), e.getMessage());
}
}
sendTrainingStudyNotifications(after, driverIds, "驾驶员");
sendTrainingStudyNotifications(after, managerIds, "管理员");
}
private void collectNotificationReceiver(HotTrainingParticipant participant, List<String> driverIds, List<String> managerIds) {
if (participant == null || StringUtils.isBlank(participant.getUserId())) {
return;
}
if (Long.valueOf(2L).equals(participant.getPersonType())) {
managerIds.add(participant.getUserId());
} else {
driverIds.add(participant.getUserId());
}
}
private void sendTrainingStudyNotifications(HotTraining training, List<String> receiverIds, String receiverType) {
if (training == null || receiverIds == null || receiverIds.isEmpty()) {
return;
}
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo();
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType(receiverType);
msg.setReceiverIds(receiverIds);
msg.setContent("您有一个" + StringUtils.blankToDefault(training.getPlanName(), "培训") + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("培训恢复可见后发送系统消息失败 trainingId={} receiverType={} count={} err={}",
training.getId(), receiverType, receiverIds.size(), e.getMessage());
}
}
private boolean isTrainingStudyAccessible(HotTraining training, Date now) {
if (training == null || now == null) {
return false;
}
if (!Long.valueOf(1L).equals(training.getIsEnabled())) {
return false;
}
if (training.getStartTime() == null || training.getEndTime() == null) {
return false;
}
if (training.getStartTime().after(now)) {
return false;
}
if (!training.getEndTime().before(now)) {
return true;
}
return Long.valueOf(1L).equals(training.getIsMakeUp());
}
/** /**
* 保存前的数据校验 * 保存前的数据校验
*/ */

View File

@@ -177,19 +177,7 @@ public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipan
HotTrainingVo training = trainingMapper.selectVoById(add.getTrainingId()); HotTrainingVo training = trainingMapper.selectVoById(add.getTrainingId());
String planName = training != null ? training.getPlanName() : "培训"; String planName = training != null ? training.getPlanName() : "培训";
pushTrainingTodo(add.getTrainingId(), add.getCompanyId(), add.getUserId(), planName); pushTrainingTodo(add.getTrainingId(), add.getCompanyId(), add.getUserId(), planName);
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); sendTrainingNotificationIfVisible(training, List.of(add.getUserId()), "驾驶员");
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType("驾驶员");
msg.setReceiverIds(java.util.List.of(add.getUserId()));
msg.setContent("您有一个" + planName + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("分配培训计划后发送系统消息失败 trainingId={} userId={} err={}", add.getTrainingId(), add.getUserId(), e.getMessage());
}
} }
return flag; return flag;
} }
@@ -224,32 +212,20 @@ public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipan
notify = true; notify = true;
} }
if (notify) { if (notify) {
HotTrainingVo training = trainingMapper.selectVoById(update.getTrainingId()); Long resolvedTrainingId = update.getTrainingId();
if (resolvedTrainingId == null && before != null) {
resolvedTrainingId = before.getTrainingId();
}
HotTrainingVo training = trainingMapper.selectVoById(resolvedTrainingId);
String planName = training != null ? training.getPlanName() : "培训"; String planName = training != null ? training.getPlanName() : "培训";
String receiver = update.getUserId(); String receiver = update.getUserId();
if (org.dromara.common.core.utils.StringUtils.isNotBlank(receiver)) { if (org.dromara.common.core.utils.StringUtils.isNotBlank(receiver)) {
Long participantId = update.getId();
if (participantId == null && before != null) {
participantId = before.getId();
}
Long todoTrainingId = update.getTrainingId(); Long todoTrainingId = update.getTrainingId();
if (todoTrainingId == null && before != null) { if (todoTrainingId == null && before != null) {
todoTrainingId = before.getTrainingId(); todoTrainingId = before.getTrainingId();
} }
pushTrainingTodo(todoTrainingId, update.getCompanyId(), receiver, planName); pushTrainingTodo(todoTrainingId, update.getCompanyId(), receiver, planName);
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); sendTrainingNotificationIfVisible(training, List.of(receiver), "驾驶员");
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType("驾驶员");
msg.setReceiverIds(java.util.List.of(receiver));
msg.setContent("您有一个" + planName + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("更新参与人员后发送系统消息失败 trainingId={} userId={} err={}", update.getTrainingId(), receiver, e.getMessage());
}
} }
} }
} }
@@ -388,34 +364,10 @@ public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipan
} }
if (!notifyManagerIds.isEmpty()) { if (!notifyManagerIds.isEmpty()) {
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); sendTrainingNotificationIfVisible(training, notifyManagerIds, "管理员");
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType("管理员");
msg.setReceiverIds(notifyManagerIds);
msg.setContent("您有一个" + training.getPlanName() + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("分配培训计划后发送系统消息失败 trainingId={} type=manager count={} err={}", bo.getTrainingId(), notifyManagerIds.size(), e.getMessage());
}
} }
if (!notifyDriverIds.isEmpty()) { if (!notifyDriverIds.isEmpty()) {
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); sendTrainingNotificationIfVisible(training, notifyDriverIds, "驾驶员");
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType("驾驶员");
msg.setReceiverIds(notifyDriverIds);
msg.setContent("您有一个" + training.getPlanName() + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("分配培训计划后发送系统消息失败 trainingId={} type=driver count={} err={}", bo.getTrainingId(), notifyDriverIds.size(), e.getMessage());
}
} }
return success; return success;
@@ -524,6 +476,45 @@ public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipan
} }
} }
private void sendTrainingNotificationIfVisible(HotTrainingVo training, List<String> receiverIds, String receiverType) {
if (!isTrainingStudyAccessible(training, new Date()) || receiverIds == null || receiverIds.isEmpty()) {
return;
}
HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo();
msg.setLevel("普通");
msg.setSourceType("安全教育培训");
msg.setSenderType("SYSTEM");
msg.setReceiverType(receiverType);
msg.setReceiverIds(receiverIds);
msg.setContent("您有一个" + StringUtils.blankToDefault(training.getPlanName(), "培训") + "培训需要进行学习");
msg.setIsDeleted(0L);
try {
notificationService.insertByBo(msg);
} catch (Exception e) {
log.warn("发送安全教育培训系统消息失败 trainingId={} receiverType={} count={} err={}",
training.getId(), receiverType, receiverIds.size(), e.getMessage());
}
}
private boolean isTrainingStudyAccessible(HotTrainingVo training, Date now) {
if (training == null || now == null) {
return false;
}
if (!Long.valueOf(1L).equals(training.getIsEnabled())) {
return false;
}
if (training.getStartTime() == null || training.getEndTime() == null) {
return false;
}
if (training.getStartTime().after(now)) {
return false;
}
if (!training.getEndTime().before(now)) {
return true;
}
return Long.valueOf(1L).equals(training.getIsMakeUp());
}
/** /**
* 人员信息载体(用于封装姓名/证件/电话/人员类型) * 人员信息载体(用于封装姓名/证件/电话/人员类型)
*/ */

View File

@@ -25,15 +25,12 @@
AND p.user_id = #{userId} AND p.user_id = #{userId}
AND p.is_deleted = 0 AND p.is_deleted = 0
AND t.is_enabled = 1 AND t.is_enabled = 1
AND t.start_time IS NOT NULL
AND t.end_time IS NOT NULL
AND t.start_time &lt;= #{now}
AND ( AND (
t.is_make_up = 1 t.end_time &gt;= #{now}
OR ( OR (t.end_time &lt; #{now} AND t.is_make_up = 1)
(t.is_make_up IS NULL OR t.is_make_up !=1)
AND t.start_time IS NOT NULL
AND t.end_time IS NOT NULL
AND t.start_time &lt;= #{now}
AND t.end_time &gt;= #{now}
)
) )
ORDER BY t.start_time DESC, p.id DESC ORDER BY t.start_time DESC, p.id DESC
</select> </select>