Browse Source

邮件加跳转

master
han\hanst 2 weeks ago
parent
commit
2f51b77994
  1. 11
      src/main/java/com/xujie/sys/modules/erf/controller/ErfExpApplyController.java
  2. 3
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java
  3. 395
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpApplyServiceImpl.java

11
src/main/java/com/xujie/sys/modules/erf/controller/ErfExpApplyController.java

@ -1,5 +1,6 @@
package com.xujie.sys.modules.erf.controller;
import com.xujie.sys.common.exception.XJException;
import com.xujie.sys.common.utils.PageUtils;
import com.xujie.sys.common.utils.R;
import com.xujie.sys.modules.erf.data.ErfExpApplyData;
@ -87,9 +88,12 @@ public class ErfExpApplyController extends AbstractController {
try {
String applyNo = erfExpApplyService.saveExpApply(data);
return R.ok("保存成功").put("applyNo", applyNo);
} catch (XJException e) {
log.warn("保存申请单业务校验失败: {}", e.getMessage());
return R.error(e.getMessage());
} catch (Exception e) {
log.error("保存申请单失败: " + e.getMessage(), e);
return R.error("保存失败: " + e.getMessage());
return R.error("保存失败,请联系管理员");
}
}
@ -276,9 +280,12 @@ public class ErfExpApplyController extends AbstractController {
sourceApplyNo,applyNo, copyTriConfirm, copyAttachment,copyRawMaterialList, currentUserId);
return R.ok("复制成功").put("newApplyNo", newApplyNo);
} catch (XJException e) {
log.warn("复制申请单业务校验失败: {}", e.getMessage());
return R.error(e.getMessage());
} catch (Exception e) {
log.error("复制失败: " + e.getMessage(), e);
return R.error("复制失败: " + e.getMessage());
return R.error("复制失败,请联系管理员");
}
}

3
src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java

@ -66,6 +66,7 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
private static final String APPROVAL_PAGE_URL = "http://172.26.68.20:9001/#/erf-expApplyApproval";
private static final String PLANNER_PAGE_URL = "http://172.26.68.20:9001/#/erf-plannerSchedule";
private static final String TRI_CONFIRM_PAGE_URL = "http://172.26.68.20:9001/#/erf-triConfirm";
@Value("${erf.reminder.manager.enabled:false}")
private boolean managerReminderEnabled;
@ -658,7 +659,7 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
emailBody.append("</table>");
}
emailBody.append("<a style='color:#0f84b0;' href='http://172.26.68.20:9001/#/erf-triConfirm'>前往三方确认页面</a>");
emailBody.append("<a style='color:#0f84b0;' href='").append(TRI_CONFIRM_PAGE_URL).append("'>前往三方确认页面</a>");
emailBody.append("<p style='color: #888;'>发送时间:").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("</p>");
emailBody.append("</body></html>");

395
src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpApplyServiceImpl.java

@ -59,7 +59,10 @@ import static com.xujie.sys.modules.sys.controller.AbstractController.getUser;
@Transactional(rollbackFor = Exception.class)
public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfExpApply> implements ErfExpApplyService {
private static final String APPLY_PAGE_URL = "http://172.26.68.20:9001/#/erf-expApplyList";
private static final String APPROVAL_PAGE_URL = "http://172.26.68.20:9001/#/erf-expApplyApproval";
private static final String PLANNER_PAGE_URL = "http://172.26.68.20:9001/#/erf-plannerSchedule";
private static final String TRI_CONFIRM_PAGE_URL = "http://172.26.68.20:9001/#/erf-triConfirm";
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
@Autowired
@ -128,44 +131,73 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
BeanUtils.copyProperties(data, entity);
entity.setApplyNo(newApplyNo);
if (isNew) {
// 新增
entity.setStatus("草稿");
entity.setCreateTime(new Date());
entity.setUpdateTime(new Date());
String site = erfExpApplyMapper.getSiteByBu(data.getBuNo());
entity.setSite(site);
this.save(entity);
log.info("新增工程试验单: {}", newApplyNo);
} else {
// 修改 - 通过原始单号查找记录
originalApplyNo = originalApplyNo.trim();
ErfExpApply existing = this.getById(originalApplyNo);
if (existing == null) {
throw new XJException("试验单不存在: " + originalApplyNo);
}
entity.setUpdateTime(new Date());
if (!newApplyNo.equals(originalApplyNo)) {
// 试验单号发生变更
// 第一步级联更新所有关联表Druid防注入不允许多语句逐条执行
log.info("试验单号变更: {} -> {}", originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowInstanceApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowNodeInstanceApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowApproveLogApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateTriConfirmApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateTriConfirmDetailApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateRawMaterialApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateOssApplyNo(originalApplyNo, newApplyNo);
// 第二步更新主表主键用旧值定位改为新值
erfExpApplyMapper.updateMainApplyNo(originalApplyNo, newApplyNo);
}
// 第三步更新主表其他字段此时主键已是新值updateById可正确定位
this.updateById(entity);
log.info("修改工程试验单: {} (原单号: {})", newApplyNo, originalApplyNo);
try {
if (isNew) {
// 新增前先校验试验单号避免将数据库主键异常直接暴露给前端
if (this.getById(newApplyNo) != null) {
throw new XJException("试验单号已存在,请修改后重试");
}
entity.setStatus("草稿");
entity.setCreateTime(new Date());
entity.setUpdateTime(new Date());
String site = erfExpApplyMapper.getSiteByBu(data.getBuNo());
entity.setSite(site);
this.save(entity);
log.info("新增工程试验单: {}", newApplyNo);
} else {
// 修改 - 通过原始单号查找记录
originalApplyNo = originalApplyNo.trim();
ErfExpApply existing = this.getById(originalApplyNo);
if (existing == null) {
throw new XJException("试验单不存在: " + originalApplyNo);
}
entity.setUpdateTime(new Date());
if (!newApplyNo.equals(originalApplyNo)) {
// 试验单号发生变更时先校验目标单号是否已存在
if (this.getById(newApplyNo) != null) {
throw new XJException("试验单号已存在,请修改后重试");
}
// 第一步级联更新所有关联表Druid防注入不允许多语句逐条执行
log.info("试验单号变更: {} -> {}", originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowInstanceApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowNodeInstanceApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateFlowApproveLogApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateTriConfirmApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateTriConfirmDetailApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateRawMaterialApplyNo(originalApplyNo, newApplyNo);
erfExpApplyMapper.updateOssApplyNo(originalApplyNo, newApplyNo);
// 第二步更新主表主键用旧值定位改为新值
erfExpApplyMapper.updateMainApplyNo(originalApplyNo, newApplyNo);
}
// 第三步更新主表其他字段此时主键已是新值updateById可正确定位
this.updateById(entity);
log.info("修改工程试验单: {} (原单号: {})", newApplyNo, originalApplyNo);
}
} catch (Exception e) {
if (isDuplicateApplyNoException(e)) {
log.warn("保存试验单发生主键重复,applyNo={}, error={}", newApplyNo, e.getMessage());
throw new XJException("试验单号已存在,请修改后重试");
}
throw e;
}
return newApplyNo;
}
private boolean isDuplicateApplyNoException(Throwable throwable) {
Throwable current = throwable;
while (current != null) {
String message = current.getMessage();
if (message != null && (message.contains("PK_erf_exp_apply")
|| message.contains("PRIMARY KEY")
|| message.contains("重复键值")
|| message.contains("duplicate key"))) {
return true;
}
current = current.getCause();
}
return false;
}
@Override
public void submitExpApply(ErfExpApplyData data) {
ErfExpApply entity = this.getById(data.getApplyNo());
@ -320,7 +352,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
if (approvedEntity != null) {
List<Long> plannerUserIds = extractPlannerIdsFromFlowRemark(data.getApplyNo());
if (!plannerUserIds.isEmpty()) {
sendPlannerSubmitNotification(approvedEntity, plannerUserIds);
sendPlannerApprovalPassedNotification(approvedEntity, plannerUserIds);
} else {
log.warn("流程 remark 中未解析到计划员ID,跳过计划员邮件: {}", data.getApplyNo());
}
@ -1677,9 +1709,10 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
return;
}
LinkedHashSet<String> emailSet = new LinkedHashSet<>();
LinkedHashSet<String> roleSummarySet = new LinkedHashSet<>();
LinkedHashSet<String> successEmailSet = new LinkedHashSet<>();
List<String> recipientSummary = new ArrayList<>();
String subject = String.format("【工程试验申请取消通知】%s - %s",
entity.getApplyNo(), StringUtils.isNotBlank(entity.getTitle()) ? entity.getTitle() : "无标题");
for (Map.Entry<Long, Set<String>> entry : recipientRoleMap.entrySet()) {
Long userId = entry.getKey();
@ -1688,38 +1721,50 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
log.warn("取消通知收件人不存在,userId={}, applyNo={}", userId, entity.getApplyNo());
continue;
}
String email = userInfo.getEmail() != null ? userInfo.getEmail().trim() : "";
if (email.isEmpty()) {
Set<String> userRoles = entry.getValue() == null || entry.getValue().isEmpty()
? new LinkedHashSet<>(Collections.singletonList("流程相关人员"))
: new LinkedHashSet<>(entry.getValue());
String roleSummary = String.join("、", userRoles);
LinkedHashSet<String> recipientEmails = new LinkedHashSet<>();
appendEmailRecipients(recipientEmails, userInfo.getEmail());
appendEmailRecipients(recipientEmails, userInfo.getEmail2());
if (recipientEmails.isEmpty()) {
log.warn("取消通知收件人未配置邮箱,userId={}, username={}, applyNo={}",
userId, userInfo.getUsername(), entity.getApplyNo());
continue;
}
emailSet.add(email);
roleSummarySet.addAll(entry.getValue());
String body = buildCancelNotificationBody(entity, operatorName, userRoles);
try {
MailUtil.sendMail(subject, body, recipientEmails.toArray(new String[0]), mailSendData);
successEmailSet.addAll(recipientEmails);
} catch (Exception ex) {
log.error("发送取消通知邮件失败,userId={}, username={}, applyNo={}, error={}",
userId, userInfo.getUsername(), entity.getApplyNo(), ex.getMessage(), ex);
continue;
}
recipientSummary.add(String.format("%s(%s)",
userInfo.getUsername(), String.join("、", entry.getValue())));
StringUtils.isNotBlank(userInfo.getUsername()) ? userInfo.getUsername() : String.valueOf(userId),
roleSummary));
}
if (emailSet.isEmpty()) {
if (successEmailSet.isEmpty()) {
log.warn("已收集收件人但无有效邮箱,取消通知未发送,applyNo={}", entity.getApplyNo());
return;
}
String subject = String.format("【工程试验申请取消通知】%s - %s",
entity.getApplyNo(), StringUtils.isNotBlank(entity.getTitle()) ? entity.getTitle() : "无标题");
String body = buildCancelNotificationBody(entity, operatorName, roleSummarySet);
MailUtil.sendMail(subject, body, emailSet.toArray(new String[0]), mailSendData);
SendMailRecord mailRecord = new SendMailRecord();
mailRecord.setType("工程试验申请取消通知");
mailRecord.setDocumentNo(entity.getApplyNo());
mailRecord.setRecipient(String.join(";", emailSet));
mailRecord.setRecipient(String.join(";", successEmailSet));
mailRecord.setSendDate(new Date());
qcMapper.saveSendMailRecord(mailRecord);
log.info("取消通知邮件发送完成,applyNo={}, 收件人数量={}, 收件人={}",
entity.getApplyNo(), emailSet.size(), recipientSummary);
entity.getApplyNo(), successEmailSet.size(), recipientSummary);
} catch (Exception e) {
log.error("发送试验单取消通知失败,applyNo={}, error={}", entity.getApplyNo(), e.getMessage(), e);
}
@ -1896,15 +1941,15 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
.add(roleName);
}
private String buildCancelNotificationBody(ErfExpApply entity, String operatorName, Set<String> roleSummarySet) {
private String buildCancelNotificationBody(ErfExpApply entity, String operatorName, Set<String> recipientRoleSet) {
String cancelTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String expectedFinishDateStr = entity.getExpectedFinishDate() != null
? DateTimeFormatter.ofPattern("yyyy-MM-dd")
.format(entity.getExpectedFinishDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime())
: "";
String roleSummary = roleSummarySet == null || roleSummarySet.isEmpty()
String roleSummary = recipientRoleSet == null || recipientRoleSet.isEmpty()
? "流程相关人员"
: String.join("、", roleSummarySet);
: String.join("、", recipientRoleSet);
StringBuilder body = new StringBuilder();
body.append("<html><body>")
@ -1916,7 +1961,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
.append("<p><b>取消时间:</b>")
.append(cancelTime)
.append("</p>")
.append("<p><b>通知范围:</b>")
.append("<p><b>您的接收角色:</b>")
.append(roleSummary)
.append("</p>")
.append("<hr/>")
@ -1937,6 +1982,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
body.append("<p><b>说明:</b>该试验单为 High Risk,三方确认相关人员已纳入本次通知范围。</p>");
}
body.append(buildMailJumpLinkBlock(recipientRoleSet));
body.append("<p style='color:#888;'>发送时间:")
.append(cancelTime)
.append("</p>")
@ -1944,6 +1990,57 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
return body.toString();
}
private String buildMailJumpLinkBlock(Set<String> roleSet) {
LinkedHashMap<String, String> jumpLinkMap = new LinkedHashMap<>();
if (roleSet != null) {
for (String roleName : roleSet) {
if (StringUtils.isBlank(roleName)) {
continue;
}
if (isTriConfirmRoleForLink(roleName)) {
jumpLinkMap.put(TRI_CONFIRM_PAGE_URL, "前往三方确认页面");
continue;
}
if (isPlannerRoleForLink(roleName)) {
jumpLinkMap.put(PLANNER_PAGE_URL, "前往计划员排产页面");
continue;
}
if (isManagerRoleForLink(roleName)) {
jumpLinkMap.put(APPROVAL_PAGE_URL, "前往审批待办页面");
}
}
}
if (jumpLinkMap.isEmpty()) {
jumpLinkMap.put(APPROVAL_PAGE_URL, "前往审批待办页面");
}
StringBuilder jumpLinks = new StringBuilder();
jumpLinks.append("<p><b>快捷跳转:</b></p>");
for (Map.Entry<String, String> entry : jumpLinkMap.entrySet()) {
jumpLinks.append("<p><a style='color:#0f84b0;' href='")
.append(entry.getKey())
.append("'>")
.append(entry.getValue())
.append("</a></p>");
}
return jumpLinks.toString();
}
private boolean isManagerRoleForLink(String roleName) {
return roleName.contains("经理");
}
private boolean isPlannerRoleForLink(String roleName) {
return roleName.contains("计划员");
}
private boolean isTriConfirmRoleForLink(String roleName) {
return roleName.contains("三方确认")
|| roleName.contains("生产负责人")
|| roleName.contains("质量负责人")
|| roleName.contains("技术负责人");
}
@Override
public String copyExpApply(String sourceApplyNo,String applyNo, Boolean copyTriConfirm, Boolean copyAttachment,Boolean copyRawMaterialList, Long currentUserId) {
log.info("=== 开始复制试验单 === 源试验单号: {}, 复制三方确认: {}, 复制附件: {}",
@ -1952,73 +2049,88 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
if (StringUtils.isBlank(applyNo)) {
throw new XJException("新试验单号不能为空");
}
applyNo = applyNo.trim();
// 1. 查询源试验单
ErfExpApply sourceEntity = this.getById(sourceApplyNo);
if (sourceEntity == null) {
throw new XJException("源试验单不存在");
}
if (sourceApplyNo != null && sourceApplyNo.trim().equals(applyNo)) {
throw new XJException("新试验单号不能与源试验单号相同");
}
if (this.getById(applyNo) != null) {
throw new XJException("试验单号已存在,请修改后重试");
}
try {
// 2. 创建新试验单实体复制基本信息
ErfExpApply newEntity = new ErfExpApply();
BeanUtils.copyProperties(sourceEntity, newEntity);
newEntity.setApplyNo(applyNo);
// 4. 重置状态和流程相关字段
newEntity.setStatus("草稿");
newEntity.setCurrentStep(null);
newEntity.setActualFinishDate(null);
newEntity.setFinalStatus(null);
newEntity.setFinalQuantity(null);
newEntity.setPlannerUserId(null);
newEntity.setPlannerName(null);
newEntity.setWorkOrderNo(null);
newEntity.setScheduledDate(null);
newEntity.setSubmitTime(null);
// 5. 设置创建人信息
String creatorName = getUser() != null ? getUser().getUserDisplay(): "系统";
newEntity.setCreatorUserId(currentUserId);
newEntity.setCreatorName(creatorName);
newEntity.setCreateTime(new Date());
// 6. 保存新试验单
this.save(newEntity);
log.info("新试验单已创建: {}", applyNo);
// 7. 复制三方确认信息仅High Risk
if (Boolean.TRUE.equals(copyTriConfirm) && "High Risk".equals(sourceEntity.getExperimentType())) {
try {
erfTriConfirmService.copyTriConfirmRecords(sourceApplyNo, applyNo);
log.info("三方确认信息复制成功");
} catch (Exception e) {
log.warn("复制三方确认信息失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
// 8. 复制附件
if (Boolean.TRUE.equals(copyAttachment)) {
try {
// 调用附件服务复制附件
copyAttachments(sourceApplyNo, applyNo);
log.info("附件复制成功");
} catch (Exception e) {
log.warn("复制附件失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
// 9. 复制原材料清单
if (Boolean.TRUE.equals(copyRawMaterialList)) {
try {
erfExpRawMaterialService.copyRawMaterialList(sourceApplyNo, applyNo);
log.info("原材料清单复制成功");
} catch (Exception e) {
log.warn("复制原材料清单失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
// 2. 创建新试验单实体复制基本信息
ErfExpApply newEntity = new ErfExpApply();
BeanUtils.copyProperties(sourceEntity, newEntity);
newEntity.setApplyNo(applyNo);
// 4. 重置状态和流程相关字段
newEntity.setStatus("草稿");
newEntity.setCurrentStep(null);
newEntity.setActualFinishDate(null);
newEntity.setFinalStatus(null);
newEntity.setFinalQuantity(null);
newEntity.setPlannerUserId(null);
newEntity.setPlannerName(null);
newEntity.setWorkOrderNo(null);
newEntity.setScheduledDate(null);
newEntity.setSubmitTime(null);
// 5. 设置创建人信息
String creatorName = getUser() != null ? getUser().getUserDisplay(): "系统";
newEntity.setCreatorUserId(currentUserId);
newEntity.setCreatorName(creatorName);
newEntity.setCreateTime(new Date());
// 6. 保存新试验单
this.save(newEntity);
log.info("新试验单已创建: {}", applyNo);
// 7. 复制三方确认信息仅High Risk
if (Boolean.TRUE.equals(copyTriConfirm) && "High Risk".equals(sourceEntity.getExperimentType())) {
try {
erfTriConfirmService.copyTriConfirmRecords(sourceApplyNo, applyNo);
log.info("三方确认信息复制成功");
} catch (Exception e) {
log.warn("复制三方确认信息失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
// 8. 复制附件
if (Boolean.TRUE.equals(copyAttachment)) {
try {
// 调用附件服务复制附件
copyAttachments(sourceApplyNo, applyNo);
log.info("附件复制成功");
} catch (Exception e) {
log.warn("复制附件失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
// 9. 复制原材料清单
if (Boolean.TRUE.equals(copyRawMaterialList)) {
try {
erfExpRawMaterialService.copyRawMaterialList(sourceApplyNo, applyNo);
log.info("原材料清单复制成功");
} catch (Exception e) {
log.warn("复制原材料清单失败: {}", e.getMessage());
// 不抛出异常继续执行
}
}
log.info("=== 复制试验单完成 === 新试验单号: {}", applyNo);
return applyNo;
log.info("=== 复制试验单完成 === 新试验单号: {}", applyNo);
return applyNo;
} catch (Exception e) {
if (isDuplicateApplyNoException(e)) {
log.warn("复制试验单发生主键重复,applyNo={}, error={}", applyNo, e.getMessage());
throw new XJException("试验单号已存在,请修改后重试");
}
throw e;
}
}
/**
@ -2185,10 +2297,37 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
* @param plannerUserIds 计划员用户ID列表
*/
private void sendPlannerSubmitNotification(ErfExpApply entity, List<Long> plannerUserIds) {
String subject = String.format("【工程试验申请下达通知】%s - %s",
entity.getApplyNo(), entity.getTitle() != null ? entity.getTitle() : "");
String introText = "您好!以下工程试验单已下达,请及时安排生产。";
sendPlannerNotification(entity, plannerUserIds, subject, introText,
"工程试验申请下达通知", "下达通知");
}
/**
* 经理审批通过后发送邮件通知计划员进入排产
*
* @param entity 试验单实体
* @param plannerUserIds 计划员用户ID列表
*/
private void sendPlannerApprovalPassedNotification(ErfExpApply entity, List<Long> plannerUserIds) {
String subject = String.format("【工程试验申请待排产】%s - %s",
entity.getApplyNo(), entity.getTitle() != null ? entity.getTitle() : "");
String introText = "您好!以下工程试验单经理审批已完成,当前已进入计划员排产环节,请及时安排生产。";
sendPlannerNotification(entity, plannerUserIds, subject, introText,
"工程试验申请经理审批通过待排产通知", "待排产通知");
}
/**
* 发送计划员通知邮件下达通知 / 待排产通知
*/
private void sendPlannerNotification(ErfExpApply entity, List<Long> plannerUserIds,
String subject, String introText,
String mailRecordType, String sceneName) {
try {
MailSendAddressData mailSendData = qcMapper.getSendMailFromAddress();
if (mailSendData == null) {
log.error("邮件发送配置未设置,无法发送下达通知邮件");
log.error("邮件发送配置未设置,无法发送{}邮件", sceneName);
return;
}
@ -2198,9 +2337,6 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
.format(entity.getExpectedFinishDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime())
: todayStr;
String subject = String.format("【工程试验申请下达通知】%s - %s",
entity.getApplyNo(), entity.getTitle() != null ? entity.getTitle() : "");
// 构建邮件正文内容固定收件人不同
String tableBody = buildPlannerNotificationTable(entity, todayStr, expectedFinishDateStr);
@ -2210,21 +2346,22 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
try {
UserEmailInfoDto planner = sysUserDao.getUserEmailInfoById(plannerId);
if (planner == null) {
log.warn("计划员ID {} 不存在,跳过下达邮件通知", plannerId);
log.warn("计划员ID {} 不存在,跳过{}邮件通知", plannerId, sceneName);
continue;
}
Set<String> plannerEmails = new LinkedHashSet<>();
appendEmailRecipients(plannerEmails, planner.getEmail());
appendEmailRecipients(plannerEmails, planner.getEmail2());
if (plannerEmails.isEmpty()) {
log.warn("计划员 {} 未配置邮箱(email/email2),跳过下达邮件通知", planner.getUsername());
log.warn("计划员 {} 未配置邮箱(email/email2),跳过{}邮件通知", planner.getUsername(), sceneName);
continue;
}
String body = "<html><body>"
+ "<h3>尊敬的 " + planner.getUsername() + " 计划员:</h3>"
+ "<p>您好!以下工程试验单已下达,请及时安排生产。</p>"
+ "<p>" + introText + "</p>"
+ "<hr/>" + tableBody
+ "<br/><p><a style='color:#0f84b0;' href='" + PLANNER_PAGE_URL + "'>前往计划员排产页面</a></p>"
+ "<br/><p style='color:#888;'>发送时间:"
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ "</p></body></html>";
@ -2232,16 +2369,16 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
String[] recipients = plannerEmails.toArray(new String[0]);
MailUtil.sendMail(subject, body, recipients, mailSendData);
successEmails.addAll(plannerEmails);
log.info("已向计划员 {} ({}) 发送下达通知邮件,试验单: {}",
planner.getUsername(), String.join(";", plannerEmails), entity.getApplyNo());
log.info("已向计划员 {} ({}) 发送{}邮件,试验单: {}",
planner.getUsername(), String.join(";", plannerEmails), sceneName, entity.getApplyNo());
} catch (Exception e) {
log.error("向计划员ID {} 发送邮件失败: {}", plannerId, e.getMessage());
log.error("向计划员ID {} 发送{}邮件失败: {}", plannerId, sceneName, e.getMessage());
}
}
if (!successEmails.isEmpty()) {
SendMailRecord mailRecord = new SendMailRecord();
mailRecord.setType("工程试验申请下达通知");
mailRecord.setType(mailRecordType);
mailRecord.setDocumentNo(entity.getApplyNo());
mailRecord.setRecipient(String.join(";", successEmails));
mailRecord.setSendDate(new Date());
@ -2249,7 +2386,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
}
} catch (Exception e) {
log.error("发送下达通知邮件失败,试验单: {}, 错误: {}", entity.getApplyNo(), e.getMessage(), e);
log.error("发送{}邮件失败,试验单: {}, 错误: {}", sceneName, entity.getApplyNo(), e.getMessage(), e);
}
}
@ -2313,6 +2450,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
+ "<p>您好!您作为试验负责人负责的工程试验单,经理层审批已全部通过,流程已进入<strong>计划员排产</strong>环节。</p>"
+ "<p>请留意后续排产与执行安排。</p>"
+ "<hr/>" + tableBody
+ "<br/><p><a style='color:#0f84b0;' href='" + APPLY_PAGE_URL + "'>前往工程试验申请管理页面</a></p>"
+ "<br/><p style='color:#888;'>发送时间:"
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ "</p></body></html>";
@ -2483,6 +2621,7 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx
+ "<p>您好!以下工程试验单已完成排产,请做好相应备料准备。</p>"
+ "<hr/>" + mainTable
+ "<br/><h4>原材料清单:</h4>" + rawMaterialTable
+ "<br/><p><a style='color:#0f84b0;' href='" + PLANNER_PAGE_URL + "'>前往计划员排产页面</a></p>"
+ "<br/><p style='color:#888;'>发送时间:"
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ "</p></body></html>";

Loading…
Cancel
Save