|
|
@ -3,6 +3,8 @@ package com.xujie.sys.modules.erf.service.impl; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode; |
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper; |
|
|
import com.github.pagehelper.PageHelper; |
|
|
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; |
|
|
@ -58,6 +60,7 @@ import static com.xujie.sys.modules.sys.controller.AbstractController.getUser; |
|
|
public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfExpApply> implements ErfExpApplyService { |
|
|
public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfExpApply> implements ErfExpApplyService { |
|
|
|
|
|
|
|
|
private static final String APPROVAL_PAGE_URL = "http://172.26.68.20:9001/#/erf-expApplyApproval"; |
|
|
private static final String APPROVAL_PAGE_URL = "http://172.26.68.20:9001/#/erf-expApplyApproval"; |
|
|
|
|
|
private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); |
|
|
|
|
|
|
|
|
@Autowired |
|
|
@Autowired |
|
|
private ErfExpApplyMapper erfExpApplyMapper; |
|
|
private ErfExpApplyMapper erfExpApplyMapper; |
|
|
@ -88,6 +91,9 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
@Autowired |
|
|
@Autowired |
|
|
private QcMapper qcMapper; |
|
|
private QcMapper qcMapper; |
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
|
private ErfExpTriConfirmMapper erfExpTriConfirmMapper; |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public PageUtils searchExpApplyList(ErfExpApplyData data) { |
|
|
public PageUtils searchExpApplyList(ErfExpApplyData data) { |
|
|
PageHelper.startPage(data.getPage(), data.getLimit()); |
|
|
PageHelper.startPage(data.getPage(), data.getLimit()); |
|
|
@ -1481,13 +1487,310 @@ public class ErfExpApplyServiceImpl extends ServiceImpl<ErfExpApplyMapper, ErfEx |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 6. 记录操作日志 |
|
|
// 6. 记录操作日志 |
|
|
String operatorName = getUser() != null ? getUser().getUsername() : "系统"; |
|
|
|
|
|
|
|
|
String operatorName = "系统"; |
|
|
|
|
|
if (getUser() != null) { |
|
|
|
|
|
operatorName = StringUtils.isNotBlank(getUser().getUserDisplay()) |
|
|
|
|
|
? getUser().getUserDisplay() |
|
|
|
|
|
: getUser().getUsername(); |
|
|
|
|
|
} |
|
|
saveApprovalLog(applyNo, "取消", "取消试验单", |
|
|
saveApprovalLog(applyNo, "取消", "取消试验单", |
|
|
"用户主动取消试验单,所有待审批流程已终止", userId, operatorName); |
|
|
"用户主动取消试验单,所有待审批流程已终止", userId, operatorName); |
|
|
|
|
|
|
|
|
|
|
|
// 7. 发送取消通知邮件(覆盖申请人、负责人、经理、计划员及High Risk三方确认人员) |
|
|
|
|
|
sendCancelNotification(entity, userId, operatorName); |
|
|
|
|
|
|
|
|
log.info("=== 取消试验单完成 === 试验单号: {}", applyNo); |
|
|
log.info("=== 取消试验单完成 === 试验单号: {}", applyNo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 取消后发送通知邮件 |
|
|
|
|
|
* 通知范围:实验申请人、试验负责人、技术/生产/质量经理、计划员、PJM负责人、 |
|
|
|
|
|
* High Risk 三方确认人员、流程历史相关审批人 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void sendCancelNotification(ErfExpApply entity, Long operatorUserId, String operatorName) { |
|
|
|
|
|
try { |
|
|
|
|
|
MailSendAddressData mailSendData = qcMapper.getSendMailFromAddress(); |
|
|
|
|
|
if (mailSendData == null) { |
|
|
|
|
|
log.error("邮件发送配置未设置,无法发送试验单取消通知,applyNo={}", entity.getApplyNo()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Map<Long, Set<String>> recipientRoleMap = collectCancelNotificationRecipients(entity); |
|
|
|
|
|
if (operatorUserId != null && operatorUserId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, operatorUserId, "取消操作人"); |
|
|
|
|
|
} |
|
|
|
|
|
if (recipientRoleMap.isEmpty()) { |
|
|
|
|
|
log.warn("未收集到取消通知收件人,applyNo={}", entity.getApplyNo()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LinkedHashSet<String> emailSet = new LinkedHashSet<>(); |
|
|
|
|
|
LinkedHashSet<String> roleSummarySet = new LinkedHashSet<>(); |
|
|
|
|
|
List<String> recipientSummary = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
for (Map.Entry<Long, Set<String>> entry : recipientRoleMap.entrySet()) { |
|
|
|
|
|
Long userId = entry.getKey(); |
|
|
|
|
|
UserEmailInfoDto userInfo = sysUserDao.getUserEmailInfoById(userId); |
|
|
|
|
|
if (userInfo == null) { |
|
|
|
|
|
log.warn("取消通知收件人不存在,userId={}, applyNo={}", userId, entity.getApplyNo()); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
String email = userInfo.getEmail() != null ? userInfo.getEmail().trim() : ""; |
|
|
|
|
|
if (email.isEmpty()) { |
|
|
|
|
|
log.warn("取消通知收件人未配置邮箱,userId={}, username={}, applyNo={}", |
|
|
|
|
|
userId, userInfo.getUsername(), entity.getApplyNo()); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
emailSet.add(email); |
|
|
|
|
|
roleSummarySet.addAll(entry.getValue()); |
|
|
|
|
|
recipientSummary.add(String.format("%s(%s)", |
|
|
|
|
|
userInfo.getUsername(), String.join("、", entry.getValue()))); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (emailSet.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.setSendDate(new Date()); |
|
|
|
|
|
qcMapper.saveSendMailRecord(mailRecord); |
|
|
|
|
|
|
|
|
|
|
|
log.info("取消通知邮件发送完成,applyNo={}, 收件人数量={}, 收件人={}", |
|
|
|
|
|
entity.getApplyNo(), emailSet.size(), recipientSummary); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("发送试验单取消通知失败,applyNo={}, error={}", entity.getApplyNo(), e.getMessage(), e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Map<Long, Set<String>> collectCancelNotificationRecipients(ErfExpApply entity) { |
|
|
|
|
|
Map<Long, Set<String>> recipientRoleMap = new LinkedHashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 申请人 |
|
|
|
|
|
addRecipientRole(recipientRoleMap, entity.getCreatorUserId(), "实验申请人"); |
|
|
|
|
|
|
|
|
|
|
|
// 2. 试验负责人 |
|
|
|
|
|
UserEmailInfoDto projectLeader = resolveProjectLeaderEmailInfo(entity); |
|
|
|
|
|
if (projectLeader != null) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, projectLeader.getUserId(), "试验负责人"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3. PJM负责人 |
|
|
|
|
|
UserEmailInfoDto pjmLeader = resolvePjmLeaderEmailInfo(entity); |
|
|
|
|
|
if (pjmLeader != null) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, pjmLeader.getUserId(), "PJM负责人"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 4. 下达时配置的技术/生产/质量经理与计划员 |
|
|
|
|
|
collectApproversFromFlowRemark(entity.getApplyNo(), recipientRoleMap); |
|
|
|
|
|
|
|
|
|
|
|
// 5. 流程节点中出现过的相关审批人(兜底覆盖) |
|
|
|
|
|
collectFlowNodeAssignees(entity.getApplyNo(), recipientRoleMap); |
|
|
|
|
|
|
|
|
|
|
|
// 6. High Risk 额外包含三方确认人员 |
|
|
|
|
|
if ("High Risk".equalsIgnoreCase(entity.getExperimentType())) { |
|
|
|
|
|
collectTriConfirmApprovers(entity.getApplyNo(), recipientRoleMap); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return recipientRoleMap; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void collectApproversFromFlowRemark(String applyNo, Map<Long, Set<String>> recipientRoleMap) { |
|
|
|
|
|
QueryWrapper<ErfFlowInstance> flowQuery = new QueryWrapper<>(); |
|
|
|
|
|
flowQuery.eq("apply_no", applyNo); |
|
|
|
|
|
ErfFlowInstance flowInstance = erfFlowInstanceMapper.selectOne(flowQuery); |
|
|
|
|
|
if (flowInstance == null || StringUtils.isBlank(flowInstance.getRemark())) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
JsonNode root = JSON_MAPPER.readTree(flowInstance.getRemark()); |
|
|
|
|
|
|
|
|
|
|
|
long techManagerId = root.path("techManagerId").asLong(0); |
|
|
|
|
|
if (techManagerId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, techManagerId, resolveTechManagerRoleLabel(techManagerId)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
long prodManagerId = root.path("prodManagerId").asLong(0); |
|
|
|
|
|
if (prodManagerId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, prodManagerId, "生产经理"); |
|
|
|
|
|
} |
|
|
|
|
|
addRecipientsFromJsonArray(root.path("prodManagerIds"), "生产经理", recipientRoleMap); |
|
|
|
|
|
|
|
|
|
|
|
long qualityManagerId = root.path("qualityManagerId").asLong(0); |
|
|
|
|
|
if (qualityManagerId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, qualityManagerId, "质量经理"); |
|
|
|
|
|
} |
|
|
|
|
|
addRecipientsFromJsonArray(root.path("qualityManagerIds"), "质量经理", recipientRoleMap); |
|
|
|
|
|
|
|
|
|
|
|
long plannerId = root.path("plannerId").asLong(0); |
|
|
|
|
|
if (plannerId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, plannerId, "计划员"); |
|
|
|
|
|
} |
|
|
|
|
|
addRecipientsFromJsonArray(root.path("plannerIds"), "计划员", recipientRoleMap); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.warn("解析流程审批人信息失败,applyNo={}, error={}", applyNo, e.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addRecipientsFromJsonArray(JsonNode arrayNode, String roleName, |
|
|
|
|
|
Map<Long, Set<String>> recipientRoleMap) { |
|
|
|
|
|
if (arrayNode == null || !arrayNode.isArray()) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
for (JsonNode node : arrayNode) { |
|
|
|
|
|
long userId = node.asLong(0); |
|
|
|
|
|
if (userId > 0) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, userId, roleName); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void collectFlowNodeAssignees(String applyNo, Map<Long, Set<String>> recipientRoleMap) { |
|
|
|
|
|
QueryWrapper<ErfFlowNodeInstance> nodeQuery = new QueryWrapper<>(); |
|
|
|
|
|
nodeQuery.eq("apply_no", applyNo).isNotNull("assignee_user_id"); |
|
|
|
|
|
List<ErfFlowNodeInstance> nodeInstances = erfFlowNodeInstanceMapper.selectList(nodeQuery); |
|
|
|
|
|
if (nodeInstances == null || nodeInstances.isEmpty()) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (ErfFlowNodeInstance node : nodeInstances) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, node.getAssigneeUserId(), |
|
|
|
|
|
mapNodeCodeToCancelNotifyRole(node.getNodeCode(), node.getAssigneeUserId())); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String mapNodeCodeToCancelNotifyRole(String nodeCode, Long assigneeUserId) { |
|
|
|
|
|
if (StringUtils.isBlank(nodeCode)) { |
|
|
|
|
|
return "流程相关人员"; |
|
|
|
|
|
} |
|
|
|
|
|
if (nodeCode.contains("技术经理")) { |
|
|
|
|
|
return resolveTechManagerRoleLabel(assigneeUserId); |
|
|
|
|
|
} |
|
|
|
|
|
if (nodeCode.contains("生产经理")) { |
|
|
|
|
|
return "生产经理"; |
|
|
|
|
|
} |
|
|
|
|
|
if (nodeCode.contains("质量经理")) { |
|
|
|
|
|
return "质量经理"; |
|
|
|
|
|
} |
|
|
|
|
|
if (nodeCode.contains("计划员")) { |
|
|
|
|
|
return "计划员"; |
|
|
|
|
|
} |
|
|
|
|
|
if (nodeCode.contains("三方确认")) { |
|
|
|
|
|
return "三方确认人员"; |
|
|
|
|
|
} |
|
|
|
|
|
return "流程相关人员"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void collectTriConfirmApprovers(String applyNo, Map<Long, Set<String>> recipientRoleMap) { |
|
|
|
|
|
QueryWrapper<ErfExpTriConfirm> triQuery = new QueryWrapper<>(); |
|
|
|
|
|
triQuery.eq("apply_no", applyNo); |
|
|
|
|
|
List<ErfExpTriConfirm> triConfirmList = erfExpTriConfirmMapper.selectList(triQuery); |
|
|
|
|
|
if (triConfirmList == null || triConfirmList.isEmpty()) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (ErfExpTriConfirm tri : triConfirmList) { |
|
|
|
|
|
addRecipientRole(recipientRoleMap, tri.getProdApproverUserId(), "生产负责人"); |
|
|
|
|
|
addRecipientRole(recipientRoleMap, tri.getQaApproverUserId(), "质量负责人"); |
|
|
|
|
|
addRecipientRole(recipientRoleMap, tri.getTechApproverUserId(), "技术负责人"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 解析技术经理的真实角色名称(R&D经理 / MFG经理) |
|
|
|
|
|
* 若无法识别则回退为“技术经理” |
|
|
|
|
|
*/ |
|
|
|
|
|
private String resolveTechManagerRoleLabel(Long userId) { |
|
|
|
|
|
if (userId == null || userId <= 0) { |
|
|
|
|
|
return "技术经理"; |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
List<UserRoleDto> roles = sysUserDao.getUserRolesByRoleNames( |
|
|
|
|
|
userId, Arrays.asList("R&D经理", "MFG经理")); |
|
|
|
|
|
if (roles == null || roles.isEmpty()) { |
|
|
|
|
|
return "技术经理"; |
|
|
|
|
|
} |
|
|
|
|
|
LinkedHashSet<String> roleNames = roles.stream() |
|
|
|
|
|
.map(UserRoleDto::getRoleName) |
|
|
|
|
|
.filter(StringUtils::isNotBlank) |
|
|
|
|
|
.collect(Collectors.toCollection(LinkedHashSet::new)); |
|
|
|
|
|
if (roleNames.isEmpty()) { |
|
|
|
|
|
return "技术经理"; |
|
|
|
|
|
} |
|
|
|
|
|
return String.join("、", roleNames); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.warn("解析技术经理角色失败,userId={}, error={}", userId, e.getMessage()); |
|
|
|
|
|
return "技术经理"; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addRecipientRole(Map<Long, Set<String>> recipientRoleMap, Long userId, String roleName) { |
|
|
|
|
|
if (userId == null || userId <= 0 || StringUtils.isBlank(roleName)) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
recipientRoleMap |
|
|
|
|
|
.computeIfAbsent(userId, key -> new LinkedHashSet<>()) |
|
|
|
|
|
.add(roleName); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String buildCancelNotificationBody(ErfExpApply entity, String operatorName, Set<String> roleSummarySet) { |
|
|
|
|
|
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.join("、", roleSummarySet); |
|
|
|
|
|
|
|
|
|
|
|
StringBuilder body = new StringBuilder(); |
|
|
|
|
|
body.append("<html><body>") |
|
|
|
|
|
.append("<h3>工程实验申请取消通知</h3>") |
|
|
|
|
|
.append("<p>您好!以下试验单已被取消,相关审批流程已终止,请知悉。</p>") |
|
|
|
|
|
.append("<p><b>取消人:</b>") |
|
|
|
|
|
.append(StringUtils.isNotBlank(operatorName) ? operatorName : "系统") |
|
|
|
|
|
.append("</p>") |
|
|
|
|
|
.append("<p><b>取消时间:</b>") |
|
|
|
|
|
.append(cancelTime) |
|
|
|
|
|
.append("</p>") |
|
|
|
|
|
.append("<p><b>通知范围:</b>") |
|
|
|
|
|
.append(roleSummary) |
|
|
|
|
|
.append("</p>") |
|
|
|
|
|
.append("<hr/>") |
|
|
|
|
|
.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse:collapse; width:100%; font-size:13px;'>") |
|
|
|
|
|
.append("<thead style='background-color:#909399; color:white;'><tr>") |
|
|
|
|
|
.append("<th>试验单号</th><th>事业部</th><th>试验名称</th><th>试验类型</th><th>试验负责人</th><th>需求日期</th><th>状态</th>") |
|
|
|
|
|
.append("</tr></thead><tbody><tr>") |
|
|
|
|
|
.append("<td>").append(entity.getApplyNo() != null ? entity.getApplyNo() : "").append("</td>") |
|
|
|
|
|
.append("<td>").append(entity.getBuNo() != null ? entity.getBuNo() : "").append("</td>") |
|
|
|
|
|
.append("<td>").append(entity.getTitle() != null ? entity.getTitle() : "").append("</td>") |
|
|
|
|
|
.append("<td>").append(entity.getExperimentType() != null ? entity.getExperimentType() : "").append("</td>") |
|
|
|
|
|
.append("<td>").append(entity.getProjectLeader() != null ? entity.getProjectLeader() : "").append("</td>") |
|
|
|
|
|
.append("<td>").append(expectedFinishDateStr).append("</td>") |
|
|
|
|
|
.append("<td>已取消</td>") |
|
|
|
|
|
.append("</tr></tbody></table>"); |
|
|
|
|
|
|
|
|
|
|
|
if ("High Risk".equalsIgnoreCase(entity.getExperimentType())) { |
|
|
|
|
|
body.append("<p><b>说明:</b>该试验单为 High Risk,三方确认相关人员已纳入本次通知范围。</p>"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
body.append("<p style='color:#888;'>发送时间:") |
|
|
|
|
|
.append(cancelTime) |
|
|
|
|
|
.append("</p>") |
|
|
|
|
|
.append("</body></html>"); |
|
|
|
|
|
return body.toString(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public String copyExpApply(String sourceApplyNo,String applyNo, Boolean copyTriConfirm, Boolean copyAttachment,Boolean copyRawMaterialList, Long currentUserId) { |
|
|
public String copyExpApply(String sourceApplyNo,String applyNo, Boolean copyTriConfirm, Boolean copyAttachment,Boolean copyRawMaterialList, Long currentUserId) { |
|
|
log.info("=== 开始复制试验单 === 源试验单号: {}, 复制三方确认: {}, 复制附件: {}", |
|
|
log.info("=== 开始复制试验单 === 源试验单号: {}, 复制三方确认: {}, 复制附件: {}", |
|
|
|