|
|
@ -7,8 +7,13 @@ import com.github.pagehelper.PageHelper; |
|
|
import com.github.pagehelper.PageInfo; |
|
|
import com.github.pagehelper.PageInfo; |
|
|
import com.xujie.sys.common.exception.XJException; |
|
|
import com.xujie.sys.common.exception.XJException; |
|
|
import com.xujie.sys.common.utils.DateUtils; |
|
|
import com.xujie.sys.common.utils.DateUtils; |
|
|
|
|
|
import com.xujie.sys.common.utils.MailUtil; |
|
|
import com.xujie.sys.common.utils.PageUtils; |
|
|
import com.xujie.sys.common.utils.PageUtils; |
|
|
import com.xujie.sys.modules.erf.data.ErfExpApplyData; |
|
|
import com.xujie.sys.modules.erf.data.ErfExpApplyData; |
|
|
|
|
|
import com.xujie.sys.modules.pms.data.MailSendAddressData; |
|
|
|
|
|
import com.xujie.sys.modules.pms.data.SendMailRecord; |
|
|
|
|
|
import com.xujie.sys.modules.pms.mapper.QcMapper; |
|
|
|
|
|
import com.xujie.sys.modules.sys.dto.UserEmailInfoDto; |
|
|
import com.xujie.sys.modules.erf.data.ErfFlowApprovalData; |
|
|
import com.xujie.sys.modules.erf.data.ErfFlowApprovalData; |
|
|
import com.xujie.sys.modules.erf.data.ErfFlowStatusData; |
|
|
import com.xujie.sys.modules.erf.data.ErfFlowStatusData; |
|
|
import com.xujie.sys.modules.erf.data.ErfPlannerScheduleData; |
|
|
import com.xujie.sys.modules.erf.data.ErfPlannerScheduleData; |
|
|
@ -74,6 +79,9 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
@Autowired |
|
|
@Autowired |
|
|
private ErfExpRawMaterialMapper erfExpRawMaterialMapper; |
|
|
private ErfExpRawMaterialMapper erfExpRawMaterialMapper; |
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
|
private QcMapper qcMapper; |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public PageUtils searchExpApplyList(ErfExpApplyData data) { |
|
|
public PageUtils searchExpApplyList(ErfExpApplyData data) { |
|
|
PageHelper.startPage(data.getPage(), data.getLimit()); |
|
|
PageHelper.startPage(data.getPage(), data.getLimit()); |
|
|
@ -139,7 +147,14 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
if (data.getQualityManagerIds() == null || data.getQualityManagerIds().isEmpty()) { |
|
|
if (data.getQualityManagerIds() == null || data.getQualityManagerIds().isEmpty()) { |
|
|
throw new XJException("质量经理未指定"); |
|
|
throw new XJException("质量经理未指定"); |
|
|
} |
|
|
} |
|
|
if (data.getPlannerUserId() == null) { |
|
|
|
|
|
|
|
|
// 兼容:plannerUserIds 优先,若只传了旧字段 plannerUserId 则包装成列表 |
|
|
|
|
|
List<Long> plannerUserIds = data.getPlannerUserIds(); |
|
|
|
|
|
if (plannerUserIds == null || plannerUserIds.isEmpty()) { |
|
|
|
|
|
if (data.getPlannerUserId() != null) { |
|
|
|
|
|
plannerUserIds = Collections.singletonList(data.getPlannerUserId()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (plannerUserIds == null || plannerUserIds.isEmpty()) { |
|
|
throw new XJException("计划员未指定"); |
|
|
throw new XJException("计划员未指定"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -153,12 +168,12 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
entity.setCurrentStep("技术经理审批"); |
|
|
entity.setCurrentStep("技术经理审批"); |
|
|
this.updateById(entity); |
|
|
this.updateById(entity); |
|
|
|
|
|
|
|
|
// 准备审批人信息(含下达时选定的计划员) |
|
|
|
|
|
|
|
|
// 准备审批人信息(含下达时选定的多位计划员) |
|
|
Map<String, Object> approverInfo = new HashMap<>(); |
|
|
Map<String, Object> approverInfo = new HashMap<>(); |
|
|
approverInfo.put("techManagerId", data.getTechManagerId()); |
|
|
approverInfo.put("techManagerId", data.getTechManagerId()); |
|
|
approverInfo.put("prodManagerIds", data.getProdManagerIds()); |
|
|
approverInfo.put("prodManagerIds", data.getProdManagerIds()); |
|
|
approverInfo.put("qualityManagerIds", data.getQualityManagerIds()); |
|
|
approverInfo.put("qualityManagerIds", data.getQualityManagerIds()); |
|
|
approverInfo.put("plannerId", data.getPlannerUserId()); |
|
|
|
|
|
|
|
|
approverInfo.put("plannerIds", plannerUserIds); |
|
|
|
|
|
|
|
|
// 无论首次还是重新下达,都新建节点(startFlowWithApprovers 内部会先删除旧节点) |
|
|
// 无论首次还是重新下达,都新建节点(startFlowWithApprovers 内部会先删除旧节点) |
|
|
erfFlowEngineService.startFlowWithApprovers( |
|
|
erfFlowEngineService.startFlowWithApprovers( |
|
|
@ -172,16 +187,19 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
String techManagerName = getUserDisplayById(data.getTechManagerId()); |
|
|
String techManagerName = getUserDisplayById(data.getTechManagerId()); |
|
|
String prodManagerNames = getUserDisplaysByIds(data.getProdManagerIds()); |
|
|
String prodManagerNames = getUserDisplaysByIds(data.getProdManagerIds()); |
|
|
String qualityManagerNames = getUserDisplaysByIds(data.getQualityManagerIds()); |
|
|
String qualityManagerNames = getUserDisplaysByIds(data.getQualityManagerIds()); |
|
|
String plannerName = getUserDisplayById(data.getPlannerUserId()); |
|
|
|
|
|
|
|
|
String plannerNames = getUserDisplaysByIds(plannerUserIds); |
|
|
|
|
|
|
|
|
saveApprovalLog(entity.getApplyNo(), "下达", null, |
|
|
saveApprovalLog(entity.getApplyNo(), "下达", null, |
|
|
String.format("%s - 技术经理: %s, 生产经理: %s, 质量经理: %s, 计划员: %s", |
|
|
String.format("%s - 技术经理: %s, 生产经理: %s, 质量经理: %s, 计划员: %s", |
|
|
action, techManagerName, prodManagerNames, qualityManagerNames, plannerName), |
|
|
|
|
|
|
|
|
action, techManagerName, prodManagerNames, qualityManagerNames, plannerNames), |
|
|
entity.getCreatorUserId(), entity.getCreatorName()); |
|
|
entity.getCreatorUserId(), entity.getCreatorName()); |
|
|
|
|
|
|
|
|
log.info("{}工程实验申请单: {}, 技术经理ID: {}, 生产经理IDs: {}, 质量经理IDs: {}, 计划员ID: {}", |
|
|
|
|
|
|
|
|
log.info("{}工程实验申请单: {}, 技术经理ID: {}, 生产经理IDs: {}, 质量经理IDs: {}, 计划员IDs: {}", |
|
|
action, entity.getApplyNo(), data.getTechManagerId(), data.getProdManagerIds(), |
|
|
action, entity.getApplyNo(), data.getTechManagerId(), data.getProdManagerIds(), |
|
|
data.getQualityManagerIds(), data.getPlannerUserId()); |
|
|
|
|
|
|
|
|
data.getQualityManagerIds(), plannerUserIds); |
|
|
|
|
|
|
|
|
|
|
|
// 下达后发送邮件通知所有计划员 |
|
|
|
|
|
sendPlannerSubmitNotification(entity, plannerUserIds); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
@ -473,7 +491,30 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
public void plannerSchedule(ErfPlannerScheduleData data) { |
|
|
public void plannerSchedule(ErfPlannerScheduleData data) { |
|
|
log.info("=== 计划员排产 === 申请单: {}, 工单号: {}, 排产日期: {}", |
|
|
log.info("=== 计划员排产 === 申请单: {}, 工单号: {}, 排产日期: {}", |
|
|
data.getApplyNo(), data.getWorkOrderNo(), data.getScheduledDate()); |
|
|
data.getApplyNo(), data.getWorkOrderNo(), data.getScheduledDate()); |
|
|
|
|
|
|
|
|
|
|
|
QueryWrapper<ErfFlowNodeInstance> nodeQuery = new QueryWrapper<>(); |
|
|
|
|
|
nodeQuery.eq("apply_no", data.getApplyNo()) |
|
|
|
|
|
.eq("node_code", "计划员排产") |
|
|
|
|
|
.eq("status", "待审核") |
|
|
|
|
|
.eq("assignee_user_id", data.getPlannerUserId()) |
|
|
|
|
|
.orderByDesc("attempt_no") |
|
|
|
|
|
.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); |
|
|
|
|
|
ErfFlowNodeInstance nodeInstance = erfFlowNodeInstanceMapper.selectOne(nodeQuery); |
|
|
|
|
|
if (nodeInstance == null) { |
|
|
|
|
|
// 如果已经被其他计划员处理了,或者当前用户不是待办人,则提示具体是谁排产的 |
|
|
|
|
|
QueryWrapper<ErfFlowNodeInstance> otherNodeQuery = new QueryWrapper<>(); |
|
|
|
|
|
otherNodeQuery.eq("apply_no", data.getApplyNo()) |
|
|
|
|
|
.eq("node_code", "计划员排产") |
|
|
|
|
|
.eq("status", "已完成") |
|
|
|
|
|
.orderByDesc("complete_time") |
|
|
|
|
|
.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); |
|
|
|
|
|
ErfFlowNodeInstance otherNode = erfFlowNodeInstanceMapper.selectOne(otherNodeQuery); |
|
|
|
|
|
if (otherNode != null) { |
|
|
|
|
|
String otherPlannerName = otherNode.getAssigneeName(); |
|
|
|
|
|
throw new XJException("排产失败,已被计划员 " + otherPlannerName + " 处理"); |
|
|
|
|
|
} else { |
|
|
|
|
|
throw new XJException("排产失败,当前用户没有权限或已被其他人处理"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
ErfExpApply entity = this.getById(data.getApplyNo()); |
|
|
ErfExpApply entity = this.getById(data.getApplyNo()); |
|
|
if (entity == null) { |
|
|
if (entity == null) { |
|
|
throw new XJException("申请单不存在"); |
|
|
throw new XJException("申请单不存在"); |
|
|
@ -508,13 +549,6 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
|
|
|
|
|
|
// ===== 关键修复:排产完成后自动流转到下一节点 ===== |
|
|
// ===== 关键修复:排产完成后自动流转到下一节点 ===== |
|
|
// 更新当前节点状态为已完成 |
|
|
// 更新当前节点状态为已完成 |
|
|
QueryWrapper<ErfFlowNodeInstance> nodeQuery = new QueryWrapper<>(); |
|
|
|
|
|
nodeQuery.eq("apply_no", data.getApplyNo()) |
|
|
|
|
|
.eq("node_code", "计划员排产") |
|
|
|
|
|
.eq("status", "待审核") |
|
|
|
|
|
.orderByDesc("attempt_no") |
|
|
|
|
|
.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); |
|
|
|
|
|
ErfFlowNodeInstance nodeInstance = erfFlowNodeInstanceMapper.selectOne(nodeQuery); |
|
|
|
|
|
|
|
|
|
|
|
if (nodeInstance != null) { |
|
|
if (nodeInstance != null) { |
|
|
nodeInstance.setStatus("已完成"); |
|
|
nodeInstance.setStatus("已完成"); |
|
|
@ -529,6 +563,27 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
.eq("node_code", nodeInstance.getNodeCode()) |
|
|
.eq("node_code", nodeInstance.getNodeCode()) |
|
|
.eq("attempt_no", nodeInstance.getAttemptNo())); |
|
|
.eq("attempt_no", nodeInstance.getAttemptNo())); |
|
|
|
|
|
|
|
|
|
|
|
// 将同一申请单"计划员排产"节点中其他仍处于待审核的节点标记为已跳过 |
|
|
|
|
|
// (多计划员场景:任意一人排产后,其余人不可再排产) |
|
|
|
|
|
List<ErfFlowNodeInstance> otherPendingNodes = erfFlowNodeInstanceMapper.selectList( |
|
|
|
|
|
new QueryWrapper<ErfFlowNodeInstance>() |
|
|
|
|
|
.eq("apply_no", data.getApplyNo()) |
|
|
|
|
|
.eq("node_code", "计划员排产") |
|
|
|
|
|
.eq("status", "待审核") |
|
|
|
|
|
); |
|
|
|
|
|
for (ErfFlowNodeInstance other : otherPendingNodes) { |
|
|
|
|
|
other.setStatus("已跳过"); |
|
|
|
|
|
other.setComment("其他计划员已完成排产,本任务自动跳过"); |
|
|
|
|
|
other.setCompleteTime(new Date()); |
|
|
|
|
|
erfFlowNodeInstanceMapper.update(other, new QueryWrapper<ErfFlowNodeInstance>() |
|
|
|
|
|
.eq("apply_no", other.getApplyNo()) |
|
|
|
|
|
.eq("node_code", other.getNodeCode()) |
|
|
|
|
|
.eq("attempt_no", other.getAttemptNo())); |
|
|
|
|
|
} |
|
|
|
|
|
if (!otherPendingNodes.isEmpty()) { |
|
|
|
|
|
log.info("已将 {} 个其他计划员排产节点标记为已跳过", otherPendingNodes.size()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 调用流程引擎自动流转到下一节点 |
|
|
// 调用流程引擎自动流转到下一节点 |
|
|
erfFlowEngineService.moveToNextNode(data.getApplyNo(), "计划员排产", null); |
|
|
erfFlowEngineService.moveToNextNode(data.getApplyNo(), "计划员排产", null); |
|
|
log.info("排产完成,已自动流转到下一节点"); |
|
|
log.info("排产完成,已自动流转到下一节点"); |
|
|
@ -1377,6 +1432,97 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
return newApplyNo; |
|
|
return newApplyNo; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 下达申请单后发送邮件通知计划员 |
|
|
|
|
|
* |
|
|
|
|
|
* @param entity 申请单实体 |
|
|
|
|
|
* @param plannerUserIds 计划员用户ID列表 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void sendPlannerSubmitNotification(ErfExpApply entity, List<Long> plannerUserIds) { |
|
|
|
|
|
try { |
|
|
|
|
|
MailSendAddressData mailSendData = qcMapper.getSendMailFromAddress(); |
|
|
|
|
|
if (mailSendData == null) { |
|
|
|
|
|
log.error("邮件发送配置未设置,无法发送下达通知邮件"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String todayStr = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()); |
|
|
|
|
|
String submitDateStr = entity.getSubmitTime() != null |
|
|
|
|
|
? DateTimeFormatter.ofPattern("yyyy-MM-dd") |
|
|
|
|
|
.format(entity.getSubmitTime().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, submitDateStr); |
|
|
|
|
|
|
|
|
|
|
|
List<String> successEmails = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
for (Long plannerId : plannerUserIds) { |
|
|
|
|
|
try { |
|
|
|
|
|
UserEmailInfoDto planner = sysUserDao.getUserEmailInfoById(plannerId); |
|
|
|
|
|
if (planner == null) { |
|
|
|
|
|
log.warn("计划员ID {} 不存在,跳过下达邮件通知", plannerId); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
String plannerEmail = planner.getEmail(); |
|
|
|
|
|
if (plannerEmail == null || plannerEmail.trim().isEmpty()) { |
|
|
|
|
|
log.warn("计划员 {} 未配置邮箱,跳过下达邮件通知", planner.getUsername()); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String body = "<html><body>" |
|
|
|
|
|
+ "<h3>尊敬的 " + planner.getUsername() + " 计划员:</h3>" |
|
|
|
|
|
+ "<p>您好!以下工程实验申请单已下达,请及时安排生产。</p>" |
|
|
|
|
|
+ "<hr/>" + tableBody |
|
|
|
|
|
+ "<br/><p style='color:#888;'>发送时间:" |
|
|
|
|
|
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) |
|
|
|
|
|
+ "</p></body></html>"; |
|
|
|
|
|
|
|
|
|
|
|
MailUtil.sendMail(subject, body, new String[]{plannerEmail}, mailSendData); |
|
|
|
|
|
successEmails.add(plannerEmail); |
|
|
|
|
|
log.info("已向计划员 {} ({}) 发送下达通知邮件,申请单: {}", |
|
|
|
|
|
planner.getUsername(), plannerEmail, entity.getApplyNo()); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("向计划员ID {} 发送邮件失败: {}", plannerId, e.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!successEmails.isEmpty()) { |
|
|
|
|
|
SendMailRecord mailRecord = new SendMailRecord(); |
|
|
|
|
|
mailRecord.setType("工程实验申请下达通知"); |
|
|
|
|
|
mailRecord.setDocumentNo(entity.getApplyNo()); |
|
|
|
|
|
mailRecord.setRecipient(String.join(";", successEmails)); |
|
|
|
|
|
mailRecord.setSendDate(new Date()); |
|
|
|
|
|
qcMapper.saveSendMailRecord(mailRecord); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("发送下达通知邮件失败,申请单: {}, 错误: {}", entity.getApplyNo(), e.getMessage(), e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String buildPlannerNotificationTable(ErfExpApply entity, String todayStr, String submitDateStr) { |
|
|
|
|
|
return "<table border='1' cellpadding='6' cellspacing='0' " |
|
|
|
|
|
+ "style='border-collapse:collapse; width:100%; font-size:13px;'>" |
|
|
|
|
|
+ "<thead style='background-color:#2196F3; color:white;'><tr>" |
|
|
|
|
|
+ "<th>收到邮件日期</th><th>项目号</th><th>实验单号</th><th>项目名称</th>" |
|
|
|
|
|
+ "<th>安排生产工序</th><th>数量</th><th>备注</th><th>需求日期</th><th>工程师</th>" |
|
|
|
|
|
+ "</tr></thead><tbody><tr>" |
|
|
|
|
|
+ "<td>" + todayStr + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getProjectNo() != null ? entity.getProjectNo() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getWorkOrderNo()!=null ? entity.getWorkOrderNo() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getTitle() != null ? entity.getTitle() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getProcessRequirement() != null ? entity.getProcessRequirement() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getQuantityReq() != null ? entity.getQuantityReq() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getRemark() != null ? entity.getRemark() : "") + "</td>" |
|
|
|
|
|
+ "<td>" + submitDateStr + "</td>" |
|
|
|
|
|
+ "<td>" + (entity.getCreatorName() != null ? entity.getCreatorName() : "") + "</td>" |
|
|
|
|
|
+ "</tr></tbody></table>"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 复制附件 |
|
|
* 复制附件 |
|
|
* 使用OSS系统(sys_oss表) |
|
|
* 使用OSS系统(sys_oss表) |
|
|
|