Browse Source

三方确认的工序邮件提醒,需要按工序顺序判断

master
han\hanst 2 weeks ago
parent
commit
ce485f6c78
  1. 7
      src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java
  2. 309
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java
  3. 25
      src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml

7
src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpTriConfirmMapper.java

@ -23,4 +23,11 @@ public interface ErfExpTriConfirmMapper extends BaseMapper<ErfExpTriConfirm> {
* @return 待确认工序列表
*/
List<ErfExpTriConfirm> getPendingTriConfirmListByUser(@Param("userId") Long userId);
/**
* 查询所有待三方确认的工序用于邮件提醒
*
* @return 待确认工序列表
*/
List<java.util.Map<String, Object>> getPendingTriConfirmList();
}

309
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<Map<String, Object>> triConfirmList = erfExpTriConfirmDetailMapper.getPendingTriConfirmList();
// 从主表 erf_exp_tri_confirm 查询待确认的工序状态为"待确认"
List<Map<String, Object>> allProcessList = erfExpTriConfirmMapper.getPendingTriConfirmList();
if (triConfirmList.isEmpty()) {
if (allProcessList.isEmpty()) {
log.info("暂无待三方确认的工序,无需发送提醒邮件");
return;
}
log.info("待三方确认工序数: {}", triConfirmList.size());
log.info("查询到 {} 个待确认的工序记录", allProcessList.size());
// 按申请单分组以便检查工序顺序
Map<String, List<Map<String, Object>>> groupByApplyNo = new HashMap<>();
for (Map<String, Object> process : allProcessList) {
String applyNo = (String) process.get("apply_no");
groupByApplyNo.computeIfAbsent(applyNo, k -> new ArrayList<>()).add(process);
}
// 按角色分组
// 按角色分组待确认工序只包含可以确认的工序
List<Map<String, Object>> prodPendingList = new ArrayList<>();
List<Map<String, Object>> qualityPendingList = new ArrayList<>();
List<Map<String, Object>> techPendingList = new ArrayList<>();
for (Map<String, Object> record : triConfirmList) {
String roleType = (String) record.get("role_type");
// 遍历每个申请单的工序按顺序判断
for (Map.Entry<String, List<Map<String, Object>>> entry : groupByApplyNo.entrySet()) {
String applyNo = entry.getKey();
List<Map<String, Object>> 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<String, Object> 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<Long, UserPendingTasks> userTasksMap = new HashMap<>();
// 处理生产负责人
for (Map<String, Object> 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<String, Object> 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<String, Object> 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<ErfExpTriConfirm> 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<ErfExpTriConfirmDetail> 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<ErfFlowNodeInstance> pendingNodes, List<ErfExpApply> allApplyList) {
try {
// 1. 获取审批人信息
@ -300,7 +428,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<th>申请单号</th>");
emailBody.append("<th>事业部</th>");
emailBody.append("<th>试验名称</th>");
emailBody.append("<th>试验产品型号</th>");
emailBody.append("<th>实验类型</th>");
emailBody.append("<th>创建人</th>");
emailBody.append("<th>创建时间</th>");
@ -315,7 +442,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<td>").append(apply.getApplyNo()).append("</td>");
emailBody.append("<td>").append(apply.getBuNo() != null ? apply.getBuNo() : "").append("</td>");
emailBody.append("<td>").append(apply.getTitle() != null ? apply.getTitle() : "").append("</td>");
emailBody.append("<td>").append(apply.getProductType() != null ? apply.getProductType() : "").append("</td>");
emailBody.append("<td>").append(apply.getExperimentType() != null ? apply.getExperimentType() : "").append("</td>");
emailBody.append("<td>").append(apply.getCreatorName() != null ? apply.getCreatorName() : "").append("</td>");
emailBody.append("<td>").append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
@ -375,7 +501,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<th>申请单号</th>");
emailBody.append("<th>事业部</th>");
emailBody.append("<th>试验名称</th>");
emailBody.append("<th>试验产品型号</th>");
emailBody.append("<th>实验类型</th>");
emailBody.append("<th>创建人</th>");
emailBody.append("<th>创建时间</th>");
@ -390,7 +515,6 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<td>").append(apply.getApplyNo()).append("</td>");
emailBody.append("<td>").append(apply.getBuNo() != null ? apply.getBuNo() : "").append("</td>");
emailBody.append("<td>").append(apply.getTitle() != null ? apply.getTitle() : "").append("</td>");
emailBody.append("<td>").append(apply.getProductType() != null ? apply.getProductType() : "").append("</td>");
emailBody.append("<td>").append(apply.getExperimentType() != null ? apply.getExperimentType() : "").append("</td>");
emailBody.append("<td>").append(apply.getCreatorName() != null ? apply.getCreatorName() : "").append("</td>");
emailBody.append("<td>").append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
@ -417,38 +541,55 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
}
}
private void sendTriConfirmReminderByRole(String roleName, List<Map<String, Object>> pendingList) {
/**
* 为指定用户发送三方确认提醒邮件包含该用户所有角色的待确认工序
*
* @param userTasks 用户待确认任务
*/
private void sendTriConfirmReminderToUser(UserPendingTasks userTasks) {
try {
// 1. 查询该角色的所有用户
List<UserEmailInfoDto> 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("<html><body>");
emailBody.append("<h3>尊敬的 ").append(userName).append(":</h3>");
emailBody.append("<p>您好!系统检测到您有 <b>").append(pendingList.size()).append("</b> 个待确认的工序,请及时处理。</p>");
emailBody.append("<p><b>确认角色:</b>").append(roleName).append("</p>");
emailBody.append("<hr/>");
emailBody.append("<table border='1' cellpadding='5' cellspacing='0' style='border-collapse: collapse; width: 100%;'>");
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("<html><body>");
emailBody.append("<h3>尊敬的 ").append(userTasks.getUserName()).append(":</h3>");
emailBody.append("<p>您好!系统检测到您有 <b>").append(totalTasks).append("</b> 个待确认的工序,请及时处理。</p>");
emailBody.append("<p><b>您的确认角色:</b>").append(roles).append("</p>");
emailBody.append("<hr/>");
// 按角色分别显示
for (String roleName : userTasks.getRoles()) {
List<Map<String, Object>> tasks = userTasks.getTasksByRole(roleName);
emailBody.append("<h4 style='color: #FF9800; margin-top: 20px;'>").append(roleName)
.append(" (").append(tasks.size()).append("个)</h4>");
emailBody.append("<table border='1' cellpadding='5' cellspacing='0' style='border-collapse: collapse; width: 100%; margin-bottom: 20px;'>");
emailBody.append("<thead style='background-color: #FF9800; color: white;'>");
emailBody.append("<tr>");
emailBody.append("<th>序号</th>");
emailBody.append("<th>申请单号</th>");
emailBody.append("<th>事业部</th>");
emailBody.append("<th>工序名称</th>");
emailBody.append("<th>工序顺序</th>");
emailBody.append("<th>试验名称</th>");
emailBody.append("<th>试验产品型号</th>");
emailBody.append("<th>创建人</th>");
emailBody.append("<th>创建时间</th>");
emailBody.append("</tr>");
@ -456,7 +597,7 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<tbody>");
int index = 1;
for (Map<String, Object> record : pendingList) {
for (Map<String, Object> record : tasks) {
java.sql.Timestamp submitTime = (java.sql.Timestamp) record.get("submit_time");
emailBody.append("<tr>");
@ -464,8 +605,8 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("<td>").append(record.get("apply_no") != null ? record.get("apply_no") : "").append("</td>");
emailBody.append("<td>").append(record.get("bu_no") != null ? record.get("bu_no") : "").append("</td>");
emailBody.append("<td>").append(record.get("process_step") != null ? record.get("process_step") : "").append("</td>");
emailBody.append("<td>第").append(record.get("process_seq") != null ? record.get("process_seq") : "").append("道</td>");
emailBody.append("<td>").append(record.get("title") != null ? record.get("title") : "").append("</td>");
emailBody.append("<td>").append(record.get("product_type") != null ? record.get("product_type") : "").append("</td>");
emailBody.append("<td>").append(record.get("creator_name") != null ? record.get("creator_name") : "").append("</td>");
emailBody.append("<td>").append(submitTime != null ? DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(submitTime.toLocalDateTime()) : "")
.append("</td>");
@ -474,20 +615,74 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("</tbody>");
emailBody.append("</table>");
emailBody.append("<br/>");
emailBody.append("<p style='color: #888;'>此邮件由系统自动发送,请勿回复。</p>");
emailBody.append("<p style='color: #888;'>发送时间:").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("</p>");
emailBody.append("</body></html>");
}
sendMail(subject, emailBody.toString(), new String[]{userEmail},
"三方确认提醒");
emailBody.append("<p style='color: #888;'>此邮件由系统自动发送,请勿回复。</p>");
emailBody.append("<p style='color: #888;'>发送时间:").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("</p>");
emailBody.append("</body></html>");
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<String, List<Map<String, Object>>> tasksByRole = new LinkedHashMap<>();
public UserPendingTasks(Long userId, String userName) {
this.userId = userId;
this.userName = userName;
}
public void addTask(String roleName, Map<String, Object> task) {
tasksByRole.computeIfAbsent(roleName, k -> new ArrayList<>()).add(task);
}
public Long getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
public Set<String> getRoles() {
return tasksByRole.keySet();
}
public List<Map<String, Object>> getTasksByRole(String roleName) {
return tasksByRole.getOrDefault(roleName, Collections.emptyList());
}
public int getTotalTaskCount() {
return tasksByRole.values().stream().mapToInt(List::size).sum();
}
}

25
src/main/resources/mapper/erf/ErfExpTriConfirmMapper.xml

@ -43,4 +43,29 @@
ORDER BY tc.process_seq
</select>
<!-- 查询所有待三方确认的工序(用于邮件提醒,需要按工序顺序判断) -->
<select id="getPendingTriConfirmList" resultType="java.util.Map">
SELECT tc.apply_no AS apply_no,
tc.process_step AS process_step,
tc.process_seq AS process_seq,
tc.prod_approver_user_id AS prod_approver_user_id,
tc.prod_approver_name AS prod_approver_name,
tc.qa_approver_user_id AS qa_approver_user_id,
tc.qa_approver_name AS qa_approver_name,
tc.tech_approver_user_id AS tech_approver_user_id,
tc.tech_approver_name AS tech_approver_name,
tc.status AS status,
a.bu_no AS bu_no,
a.creator_name AS creator_name,
a.title AS title,
a.product_type AS product_type,
a.submit_time AS submit_time,
a.status AS apply_status
FROM erf_exp_tri_confirm tc
INNER JOIN erf_exp_apply a ON tc.apply_no = a.apply_no
WHERE a.status = '生产中'
AND tc.status = '待确认'
ORDER BY tc.apply_no, tc.process_seq
</select>
</mapper>
Loading…
Cancel
Save