From ce485f6c789ba04a9fcc55ec009ae650bcc7b64c Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Tue, 10 Feb 2026 15:19:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E6=96=B9=E7=A1=AE=E8=AE=A4=E7=9A=84?= =?UTF-8?q?=E5=B7=A5=E5=BA=8F=E9=82=AE=E4=BB=B6=E6=8F=90=E9=86=92=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E6=8C=89=E5=B7=A5=E5=BA=8F=E9=A1=BA=E5=BA=8F?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../erf/mapper/ErfExpTriConfirmMapper.java | 7 + .../impl/ErfApprovalReminderServiceImpl.java | 309 ++++++++++++++---- .../mapper/erf/ErfExpTriConfirmMapper.xml | 25 ++ 3 files changed, 284 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java index 14c01530..f8bfc434 100644 --- a/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java +++ b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java @@ -23,4 +23,11 @@ public interface ErfExpTriConfirmMapper extends BaseMapper { * @return 待确认工序列表 */ List getPendingTriConfirmListByUser(@Param("userId") Long userId); + + /** + * 查询所有待三方确认的工序(用于邮件提醒) + * + * @return 待确认工序列表 + */ + List> getPendingTriConfirmList(); } diff --git a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java index 9e1cd636..5a6173ae 100644 --- a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java @@ -3,9 +3,12 @@ package com.xujie.sys.modules.erf.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.xujie.sys.common.utils.MailUtil; import com.xujie.sys.modules.erf.entity.ErfExpApply; +import com.xujie.sys.modules.erf.entity.ErfExpTriConfirm; +import com.xujie.sys.modules.erf.entity.ErfExpTriConfirmDetail; import com.xujie.sys.modules.erf.entity.ErfFlowNodeInstance; import com.xujie.sys.modules.erf.mapper.ErfExpApplyMapper; import com.xujie.sys.modules.erf.mapper.ErfExpTriConfirmDetailMapper; +import com.xujie.sys.modules.erf.mapper.ErfExpTriConfirmMapper; import com.xujie.sys.modules.erf.mapper.ErfFlowNodeInstanceMapper; import com.xujie.sys.modules.erf.service.ErfApprovalReminderService; import com.xujie.sys.modules.pms.data.MailSendAddressData; @@ -48,6 +51,9 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic @Autowired private ErfExpTriConfirmDetailMapper erfExpTriConfirmDetailMapper; + @Autowired + private ErfExpTriConfirmMapper erfExpTriConfirmMapper; + @Value("${erf.reminder.manager.enabled:false}") private boolean managerReminderEnabled; @@ -203,44 +209,127 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic log.info("=== 开始执行三方确认邮件提醒 ==="); try { - // 使用Mapper查询待三方确认的工序 - List> triConfirmList = erfExpTriConfirmDetailMapper.getPendingTriConfirmList(); + // ✅ 从主表 erf_exp_tri_confirm 查询待确认的工序(状态为"待确认") + List> allProcessList = erfExpTriConfirmMapper.getPendingTriConfirmList(); - if (triConfirmList.isEmpty()) { + if (allProcessList.isEmpty()) { log.info("暂无待三方确认的工序,无需发送提醒邮件"); return; } - log.info("待三方确认工序数: {}", triConfirmList.size()); + log.info("查询到 {} 个待确认的工序记录", allProcessList.size()); + + // ✅ 按申请单分组,以便检查工序顺序 + Map>> groupByApplyNo = new HashMap<>(); + for (Map process : allProcessList) { + String applyNo = (String) process.get("apply_no"); + groupByApplyNo.computeIfAbsent(applyNo, k -> new ArrayList<>()).add(process); + } - // 按角色分组 + // ✅ 按角色分组待确认工序(只包含可以确认的工序) List> prodPendingList = new ArrayList<>(); List> qualityPendingList = new ArrayList<>(); List> techPendingList = new ArrayList<>(); - for (Map record : triConfirmList) { - String roleType = (String) record.get("role_type"); + // ✅ 遍历每个申请单的工序,按顺序判断 + for (Map.Entry>> entry : groupByApplyNo.entrySet()) { + String applyNo = entry.getKey(); + List> processList = entry.getValue(); + + // 按工序顺序排序 + processList.sort((a, b) -> { + Integer seqA = (Integer) a.get("process_seq"); + Integer seqB = (Integer) b.get("process_seq"); + return (seqA != null ? seqA : 0) - (seqB != null ? seqB : 0); + }); + + log.info("申请单 {} 有 {} 个待确认工序", applyNo, processList.size()); + + // ✅ 检查每个工序,判断是否可以确认 + for (Map process : processList) { + Integer processSeq = (Integer) process.get("process_seq"); + String processStep = (String) process.get("process_step"); + + // ✅ 判断是否可以确认:第一道工序直接可确认,其他工序需要上一道已完成 + boolean canConfirm = false; + + if (processSeq == null || processSeq == 1) { + // 第一道工序可以直接确认 + canConfirm = true; + log.info(" 工序 [{}] - 序号: {} - 第一道工序,可以确认", processStep, processSeq); + } else { + // 检查上一道工序是否已完成三方确认 + boolean previousCompleted = isPreviousProcessCompleted(applyNo, processSeq); + canConfirm = previousCompleted; + log.info(" 工序 [{}] - 序号: {} - 上一道工序{}, {}", + processStep, processSeq, + previousCompleted ? "已完成" : "未完成", + canConfirm ? "可以确认" : "等待中"); + } + + // ✅ 如果可以确认,检查三个角色哪些还未确认 + if (canConfirm) { + // 检查生产负责人是否已确认 + if (!isRoleConfirmed(applyNo, processStep, "PROD")) { + prodPendingList.add(process); + log.info(" → 生产负责人待确认: {}", process.get("prod_approver_name")); + } + + // 检查质量负责人是否已确认 + if (!isRoleConfirmed(applyNo, processStep, "QA")) { + qualityPendingList.add(process); + log.info(" → 质量负责人待确认: {}", process.get("qa_approver_name")); + } + + // 检查技术负责人是否已确认 + if (!isRoleConfirmed(applyNo, processStep, "TECH")) { + techPendingList.add(process); + log.info(" → 技术负责人待确认: {}", process.get("tech_approver_name")); + } + } + } + } + + log.info("统计结果 - 生产: {}, 质量: {}, 技术: {}", + prodPendingList.size(), qualityPendingList.size(), techPendingList.size()); - if ("PROD".equals(roleType)) { - prodPendingList.add(record); - } else if ("QA".equals(roleType)) { - qualityPendingList.add(record); - } else if ("TECH".equals(roleType)) { - techPendingList.add(record); + // ✅ 按负责人分组,合并同一个人的多个角色任务 + Map userTasksMap = new HashMap<>(); + + // 处理生产负责人 + for (Map process : prodPendingList) { + Long userId = getLongValue(process.get("prod_approver_user_id")); + String userName = (String) process.get("prod_approver_name"); + if (userId != null && userName != null) { + userTasksMap.computeIfAbsent(userId, k -> new UserPendingTasks(userId, userName)) + .addTask("生产负责人", process); } } - // 发送邮件 - if (!prodPendingList.isEmpty()) { - sendTriConfirmReminderByRole("生产负责人", prodPendingList); + // 处理质量负责人 + for (Map process : qualityPendingList) { + Long userId = getLongValue(process.get("qa_approver_user_id")); + String userName = (String) process.get("qa_approver_name"); + if (userId != null && userName != null) { + userTasksMap.computeIfAbsent(userId, k -> new UserPendingTasks(userId, userName)) + .addTask("质量负责人", process); + } } - if (!qualityPendingList.isEmpty()) { - sendTriConfirmReminderByRole("质量负责人", qualityPendingList); + // 处理技术负责人 + for (Map process : techPendingList) { + Long userId = getLongValue(process.get("tech_approver_user_id")); + String userName = (String) process.get("tech_approver_name"); + if (userId != null && userName != null) { + userTasksMap.computeIfAbsent(userId, k -> new UserPendingTasks(userId, userName)) + .addTask("技术负责人", process); + } } - if (!techPendingList.isEmpty()) { - sendTriConfirmReminderByRole("技术负责人", techPendingList); + // ✅ 为每个人发送一封邮件,包含他所有角色的待确认工序 + log.info("需要发送提醒的用户数: {}", userTasksMap.size()); + for (UserPendingTasks userTasks : userTasksMap.values()) { + sendTriConfirmReminderToUser(userTasks); } log.info("=== 三方确认邮件提醒执行完成 ==="); @@ -250,6 +339,45 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic } } + /** + * 检查上一道工序是否已完成三方确认 + * + * @param applyNo 申请单号 + * @param currentSeq 当前工序顺序号 + * @return true=上一道已完成, false=上一道未完成 + */ + private boolean isPreviousProcessCompleted(String applyNo, int currentSeq) { + if (currentSeq <= 1) { + return true; // 第一道工序无需检查 + } + + QueryWrapper query = new QueryWrapper<>(); + query.eq("apply_no", applyNo) + .eq("process_seq", currentSeq - 1) + .eq("status", "已确认"); + + long count = erfExpTriConfirmMapper.selectCount(query); + return count > 0; + } + + /** + * 检查某个角色是否已确认 + * + * @param applyNo 申请单号 + * @param processStep 工序名称 + * @param roleType 角色类型(PROD/QA/TECH) + * @return true=已确认, false=未确认 + */ + private boolean isRoleConfirmed(String applyNo, String processStep, String roleType) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("apply_no", applyNo) + .eq("process_step", processStep) + .eq("role_type", roleType) + .eq("confirm_status", "已确认"); + + return erfExpTriConfirmDetailMapper.selectCount(query) > 0; + } + private void sendManagerReminderEmail(Long approverId, List pendingNodes, List allApplyList) { try { // 1. 获取审批人信息 @@ -300,7 +428,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append("申请单号"); emailBody.append("事业部"); emailBody.append("试验名称"); - emailBody.append("试验产品型号"); emailBody.append("实验类型"); emailBody.append("创建人"); emailBody.append("创建时间"); @@ -315,7 +442,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append("").append(apply.getApplyNo()).append(""); emailBody.append("").append(apply.getBuNo() != null ? apply.getBuNo() : "").append(""); emailBody.append("").append(apply.getTitle() != null ? apply.getTitle() : "").append(""); - emailBody.append("").append(apply.getProductType() != null ? apply.getProductType() : "").append(""); emailBody.append("").append(apply.getExperimentType() != null ? apply.getExperimentType() : "").append(""); emailBody.append("").append(apply.getCreatorName() != null ? apply.getCreatorName() : "").append(""); emailBody.append("").append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") @@ -375,7 +501,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append("申请单号"); emailBody.append("事业部"); emailBody.append("试验名称"); - emailBody.append("试验产品型号"); emailBody.append("实验类型"); emailBody.append("创建人"); emailBody.append("创建时间"); @@ -390,7 +515,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append("").append(apply.getApplyNo()).append(""); emailBody.append("").append(apply.getBuNo() != null ? apply.getBuNo() : "").append(""); emailBody.append("").append(apply.getTitle() != null ? apply.getTitle() : "").append(""); - emailBody.append("").append(apply.getProductType() != null ? apply.getProductType() : "").append(""); emailBody.append("").append(apply.getExperimentType() != null ? apply.getExperimentType() : "").append(""); emailBody.append("").append(apply.getCreatorName() != null ? apply.getCreatorName() : "").append(""); emailBody.append("").append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") @@ -417,38 +541,55 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic } } - private void sendTriConfirmReminderByRole(String roleName, List> pendingList) { + /** + * 为指定用户发送三方确认提醒邮件(包含该用户所有角色的待确认工序) + * + * @param userTasks 用户待确认任务 + */ + private void sendTriConfirmReminderToUser(UserPendingTasks userTasks) { try { - // 1. 查询该角色的所有用户 - List users = sysUserDao.getUserEmailListByRoleName(roleName); - if (users.isEmpty()) { - log.warn("角色 {} 没有配置用户或用户未配置邮箱", roleName); + // 1. 获取用户邮箱信息 + UserEmailInfoDto user = sysUserDao.getUserEmailInfoById(userTasks.getUserId()); + if (user == null) { + log.warn("用户ID {} 不存在,跳过发送邮件", userTasks.getUserId()); return; } - // 2. 为每位用户发送邮件 - for (UserEmailInfoDto user : users) { - String userName = user.getUsername(); - String userEmail = user.getEmail(); - - String subject = String.format("【三方确认提醒】您有 %d 个待确认的工序 - %s", - pendingList.size(), roleName); - - StringBuilder emailBody = new StringBuilder(); - emailBody.append(""); - emailBody.append("

尊敬的 ").append(userName).append(":

"); - emailBody.append("

您好!系统检测到您有 ").append(pendingList.size()).append(" 个待确认的工序,请及时处理。

"); - emailBody.append("

确认角色:").append(roleName).append("

"); - emailBody.append("
"); - emailBody.append(""); + String userEmail = user.getEmail(); + if (userEmail == null || userEmail.trim().isEmpty()) { + log.warn("用户 {} 未配置邮箱,跳过发送邮件", userTasks.getUserName()); + return; + } + + // 2. 统计待确认工序数量 + int totalTasks = userTasks.getTotalTaskCount(); + String roles = String.join("、", userTasks.getRoles()); + + String subject = String.format("【三方确认提醒】您有 %d 个待确认的工序 - %s", totalTasks, roles); + + // 3. 构建邮件内容 + StringBuilder emailBody = new StringBuilder(); + emailBody.append(""); + emailBody.append("

尊敬的 ").append(userTasks.getUserName()).append(":

"); + emailBody.append("

您好!系统检测到您有 ").append(totalTasks).append(" 个待确认的工序,请及时处理。

"); + emailBody.append("

您的确认角色:").append(roles).append("

"); + emailBody.append("
"); + + // 按角色分别显示 + for (String roleName : userTasks.getRoles()) { + List> tasks = userTasks.getTasksByRole(roleName); + + emailBody.append("

").append(roleName) + .append(" (").append(tasks.size()).append("个)

"); + emailBody.append("
"); emailBody.append(""); emailBody.append(""); emailBody.append(""); emailBody.append(""); emailBody.append(""); emailBody.append(""); + emailBody.append(""); emailBody.append(""); - emailBody.append(""); emailBody.append(""); emailBody.append(""); emailBody.append(""); @@ -456,7 +597,7 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append(""); int index = 1; - for (Map record : pendingList) { + for (Map record : tasks) { java.sql.Timestamp submitTime = (java.sql.Timestamp) record.get("submit_time"); emailBody.append(""); @@ -464,8 +605,8 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append(""); emailBody.append(""); emailBody.append(""); + emailBody.append(""); emailBody.append(""); - emailBody.append(""); emailBody.append(""); emailBody.append(""); @@ -474,20 +615,74 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic emailBody.append(""); emailBody.append("
序号申请单号事业部工序名称工序顺序试验名称试验产品型号创建人创建时间
").append(record.get("apply_no") != null ? record.get("apply_no") : "").append("").append(record.get("bu_no") != null ? record.get("bu_no") : "").append("").append(record.get("process_step") != null ? record.get("process_step") : "").append("第").append(record.get("process_seq") != null ? record.get("process_seq") : "").append("道").append(record.get("title") != null ? record.get("title") : "").append("").append(record.get("product_type") != null ? record.get("product_type") : "").append("").append(record.get("creator_name") != null ? record.get("creator_name") : "").append("").append(submitTime != null ? DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(submitTime.toLocalDateTime()) : "") .append("
"); - emailBody.append("
"); - emailBody.append("

此邮件由系统自动发送,请勿回复。

"); - emailBody.append("

发送时间:").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("

"); - emailBody.append(""); + } - sendMail(subject, emailBody.toString(), new String[]{userEmail}, - "三方确认提醒"); + emailBody.append("

此邮件由系统自动发送,请勿回复。

"); + emailBody.append("

发送时间:").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("

"); + emailBody.append(""); - log.info("已向 {} - {} ({}) 发送三方确认提醒邮件,待确认数: {}", - roleName, userName, userEmail, pendingList.size()); - } + // 4. 发送邮件 + sendMail(subject, emailBody.toString(), new String[]{userEmail}, "三方确认提醒"); + + log.info("✅ 已向 {} ({}) 发送三方确认提醒邮件,角色: [{}], 待确认数: {}", + userTasks.getUserName(), userEmail, roles, totalTasks); } catch (Exception e) { - log.error("发送三方确认提醒邮件失败 (角色: {}): {}", roleName, e.getMessage(), e); + log.error("发送三方确认提醒邮件失败 (用户: {}): {}", userTasks.getUserName(), e.getMessage(), e); + } + } + + /** + * 获取Long值(兼容Integer和Long类型) + */ + private Long getLongValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Integer) { + return ((Integer) value).longValue(); + } + return null; + } + + /** + * 用户待确认任务集合(按角色分组) + */ + private static class UserPendingTasks { + private final Long userId; + private final String userName; + private final Map>> tasksByRole = new LinkedHashMap<>(); + + public UserPendingTasks(Long userId, String userName) { + this.userId = userId; + this.userName = userName; + } + + public void addTask(String roleName, Map task) { + tasksByRole.computeIfAbsent(roleName, k -> new ArrayList<>()).add(task); + } + + public Long getUserId() { + return userId; + } + + public String getUserName() { + return userName; + } + + public Set getRoles() { + return tasksByRole.keySet(); + } + + public List> getTasksByRole(String roleName) { + return tasksByRole.getOrDefault(roleName, Collections.emptyList()); + } + + public int getTotalTaskCount() { + return tasksByRole.values().stream().mapToInt(List::size).sum(); } } diff --git a/src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml b/src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml index 3bc43211..a2e45d66 100644 --- a/src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml +++ b/src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml @@ -43,4 +43,29 @@ ORDER BY tc.process_seq + + +