Browse Source

计划员允许多选

ecss-tx
han\hanst 7 months ago
parent
commit
e2cf24d9a0
  1. 1
      src/main/java/com/xujie/sys/config/ShiroConfig.java
  2. 7
      src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java
  3. 38
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalReminderServiceImpl.java
  4. 176
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpApplyServiceImpl.java
  5. 81
      src/main/java/com/xujie/sys/modules/erf/service/impl/ErfFlowEngineServiceImpl.java

1
src/main/java/com/xujie/sys/config/ShiroConfig.java

@ -47,6 +47,7 @@ public class ShiroConfig {
// 刷新接口 // 刷新接口
filterMap.put("/tcp", "anon"); filterMap.put("/tcp", "anon");
filterMap.put("/ckp-file/**", "anon"); filterMap.put("/ckp-file/**", "anon");
filterMap.put("/oss/2/**", "anon");
// filterMap.put("/**", "authc"); // filterMap.put("/**", "authc");
filterMap.put("/webjars/**", "anon"); filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon"); filterMap.put("/druid/**", "anon");

7
src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java

@ -91,10 +91,15 @@ public class ErfExpApplyData implements Serializable {
private BigDecimal finalQuantity; private BigDecimal finalQuantity;
/** /**
* 计划员用户ID
* 计划员用户ID单个兼容旧接口
*/ */
private Long plannerUserId; private Long plannerUserId;
/**
* 计划员用户ID列表下达时多选
*/
private List<Long> plannerUserIds;
/** /**
* 计划员姓名 * 计划员姓名
*/ */

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

@ -177,19 +177,41 @@ public class ErfApprovalReminderServiceImpl implements ErfApprovalReminderServic
return; return;
} }
// 3. 获取计划员角色用户
List<UserEmailInfoDto> planners = sysUserDao.getUserEmailListByRoleName("计划员");
// 3. 从待排产节点实例中提取被指定的计划员去重
// 多计划员场景只通知被分配到该申请单的计划员而非全部计划员角色用户
Set<Long> assignedPlannerIds = nodeList.stream()
.filter(n -> n.getAssigneeUserId() != null)
.map(ErfFlowNodeInstance::getAssigneeUserId)
.collect(Collectors.toSet());
if (planners.isEmpty()) {
log.warn("系统中没有配置计划员角色的用户或用户未配置邮箱");
if (assignedPlannerIds.isEmpty()) {
log.warn("待排产节点没有分配计划员,跳过提醒");
return; return;
} }
log.info("待排产申请单数: {}, 计划员数量: {}", validApplyList.size(), planners.size());
log.info("待排产申请单数: {}, 被指定计划员数量: {}", validApplyList.size(), assignedPlannerIds.size());
// 4. 为每位被指定的计划员发送邮件仅发送与其相关的申请单
Set<String> validApplyNoSet = validApplyList.stream()
.map(ErfExpApply::getApplyNo)
.collect(Collectors.toSet());
// 4. 为每位计划员发送邮件
for (UserEmailInfoDto planner : planners) {
sendPlannerReminderEmail(planner.getUserId(), validApplyList);
for (Long plannerId : assignedPlannerIds) {
// 筛选出该计划员负责且状态有效的申请单
List<String> plannerApplyNos = nodeList.stream()
.filter(n -> plannerId.equals(n.getAssigneeUserId())
&& validApplyNoSet.contains(n.getApplyNo()))
.map(ErfFlowNodeInstance::getApplyNo)
.distinct()
.collect(Collectors.toList());
List<ErfExpApply> plannerApplyList = validApplyList.stream()
.filter(a -> plannerApplyNos.contains(a.getApplyNo()))
.collect(Collectors.toList());
if (!plannerApplyList.isEmpty()) {
sendPlannerReminderEmail(plannerId, plannerApplyList);
}
} }
log.info("=== 计划员排产邮件提醒执行完成 ==="); log.info("=== 计划员排产邮件提醒执行完成 ===");

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

@ -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表

81
src/main/java/com/xujie/sys/modules/erf/service/impl/ErfFlowEngineServiceImpl.java

@ -103,21 +103,31 @@ public class ErfFlowEngineServiceImpl implements ErfFlowEngineService {
List<Long> prodManagerIds = (List<Long>) approverInfo.get("prodManagerIds"); List<Long> prodManagerIds = (List<Long>) approverInfo.get("prodManagerIds");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<Long> qualityManagerIds = (List<Long>) approverInfo.get("qualityManagerIds"); List<Long> qualityManagerIds = (List<Long>) approverInfo.get("qualityManagerIds");
Long plannerId = (Long) approverInfo.get("plannerId");
@SuppressWarnings("unchecked")
List<Long> plannerIds = (List<Long>) approverInfo.get("plannerIds");
// 兼容旧字段
if (plannerIds == null || plannerIds.isEmpty()) {
Long plannerId = (Long) approverInfo.get("plannerId");
if (plannerId != null) {
plannerIds = Collections.singletonList(plannerId);
}
}
if (plannerIds == null) plannerIds = Collections.emptyList();
log.info("审批人信息 - 技术经理: {}, 生产经理: {}, 质量经理: {}, 计划员: {}",
techManagerId, prodManagerIds, qualityManagerIds, plannerId);
log.info("审批人信息 - 技术经理: {}, 生产经理: {}, 质量经理: {}, 计划员(多): {}",
techManagerId, prodManagerIds, qualityManagerIds, plannerIds);
// 1. 删除该申请单已有的所有节点实例确保每次下达都是全新的节点 // 1. 删除该申请单已有的所有节点实例确保每次下达都是全新的节点
deleteFlowNodes(applyNo); deleteFlowNodes(applyNo);
// 2. 将审批人信息序列化保存到remark字段含下达时选定的计划员
// 2. 将审批人信息序列化保存到remark字段含下达时选定的多位计划员
final List<Long> finalPlannerIds = plannerIds;
String approverJson = String.format( String approverJson = String.format(
"{\"techManagerId\":%d,\"prodManagerIds\":[%s],\"qualityManagerIds\":[%s],\"plannerId\":%d}",
"{\"techManagerId\":%d,\"prodManagerIds\":[%s],\"qualityManagerIds\":[%s],\"plannerIds\":[%s]}",
techManagerId, techManagerId,
prodManagerIds.stream().map(String::valueOf).collect(Collectors.joining(",")), prodManagerIds.stream().map(String::valueOf).collect(Collectors.joining(",")),
qualityManagerIds.stream().map(String::valueOf).collect(Collectors.joining(",")), qualityManagerIds.stream().map(String::valueOf).collect(Collectors.joining(",")),
plannerId != null ? plannerId : 0
finalPlannerIds.stream().map(String::valueOf).collect(Collectors.joining(","))
); );
// 3. 创建或更新流程实例 // 3. 创建或更新流程实例
@ -204,7 +214,13 @@ public class ErfFlowEngineServiceImpl implements ErfFlowEngineService {
// 驳回后重新审批的情况从其他节点驳回到生产/质量经理 // 驳回后重新审批的情况从其他节点驳回到生产/质量经理
log.info("驳回后重新审批,创建节点:{}", nextNodeCode); log.info("驳回后重新审批,创建节点:{}", nextNodeCode);
createApproverNodesFromFlowInstance(applyNo, nextNodeCode, flowInstance); createApproverNodesFromFlowInstance(applyNo, nextNodeCode, flowInstance);
}else if ("样品确认".equals(nextNodeCode)) {
} else if ("计划员排产".equals(nextNodeCode)) {
// 计划员排产可能有多个计划员任意一人完成即可
log.info("进入计划员排产阶段,为每位计划员创建节点");
createApproverNodesFromFlowInstance(applyNo, "计划员排产", flowInstance);
} else if ("样品确认".equals(nextNodeCode)) {
// 样品确认不需要进行任何操作 // 样品确认不需要进行任何操作
log.info("样品确认节点"); log.info("样品确认节点");
} else { } else {
@ -276,6 +292,22 @@ public class ErfFlowEngineServiceImpl implements ErfFlowEngineService {
for (Long managerId : qualityManagerIds) { for (Long managerId : qualityManagerIds) {
createSingleNodeInstance(applyNo, nodeCode, managerId); createSingleNodeInstance(applyNo, nodeCode, managerId);
} }
} else if ("计划员排产".equals(nodeCode)) {
// 计划员可能有多个任意一人排产即完成
List<Long> plannerIds = extractPlannerIds(remark);
if (plannerIds.isEmpty()) {
// 降级从角色查询
Long fallbackId = getAssigneeUserId("计划员排产", applyNo);
plannerIds = fallbackId != null ? Collections.singletonList(fallbackId) : Collections.emptyList();
}
if (plannerIds.isEmpty()) {
throw new XJException("计划员信息为空");
}
log.info("创建{}个计划员排产节点", plannerIds.size());
for (Long plannerId : plannerIds) {
createSingleNodeInstance(applyNo, nodeCode, plannerId);
}
} }
} catch (Exception e) { } catch (Exception e) {
log.error("解析审批人信息失败: " + e.getMessage(), e); log.error("解析审批人信息失败: " + e.getMessage(), e);
@ -343,7 +375,6 @@ public class ErfFlowEngineServiceImpl implements ErfFlowEngineService {
*/ */
private List<Long> extractQualityManagerIds(String json) { private List<Long> extractQualityManagerIds(String json) {
try { try {
// 简单的JSON解析{"techManagerId":1,"prodManagerIds":[2,3],"qualityManagerIds":[4,5]}
String pattern = "\"qualityManagerIds\":\\[([0-9,]+)\\]"; String pattern = "\"qualityManagerIds\":\\[([0-9,]+)\\]";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern); java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
java.util.regex.Matcher m = p.matcher(json); java.util.regex.Matcher m = p.matcher(json);
@ -360,6 +391,40 @@ public class ErfFlowEngineServiceImpl implements ErfFlowEngineService {
return new ArrayList<>(); return new ArrayList<>();
} }
/**
* 从JSON字符串中提取计划员ID列表
* 支持新字段 plannerIds 和旧字段 plannerId 兼容
*/
private List<Long> extractPlannerIds(String json) {
try {
// 新格式plannerIds 数组
String pattern = "\"plannerIds\":\\[([0-9,]+)\\]";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
java.util.regex.Matcher m = p.matcher(json);
if (m.find()) {
String idsStr = m.group(1);
return Arrays.stream(idsStr.split(","))
.map(s -> Long.valueOf(s.trim()))
.filter(id -> id > 0)
.collect(Collectors.toList());
}
// 旧格式单个 plannerId
int idx = json.indexOf("\"plannerId\":");
if (idx >= 0) {
String sub = json.substring(idx + 12).trim();
int end = sub.indexOf(',');
if (end < 0) end = sub.indexOf('}');
if (end > 0) {
long id = Long.parseLong(sub.substring(0, end).trim());
if (id > 0) return Collections.singletonList(id);
}
}
} catch (Exception e) {
log.error("解析计划员ID失败: " + e.getMessage());
}
return new ArrayList<>();
}
@Override @Override
public void rejectToNode(String applyNo, String targetNodeCode, ErfFlowApprovalData approvalData) { public void rejectToNode(String applyNo, String targetNodeCode, ErfFlowApprovalData approvalData) {
log.info("=== 驳回到指定节点 === 申请单: {}, 目标节点: {}", applyNo, targetNodeCode); log.info("=== 驳回到指定节点 === 申请单: {}, 目标节点: {}", applyNo, targetNodeCode);

Loading…
Cancel
Save