Commit 37df8b1d by 王志超

feat: 催办

parent 6b6c23d8
Pipeline #86999 passed with stages
in 1 minute 40 seconds
......@@ -2261,4 +2261,168 @@ public class ChangeFlowBiz {
return calendar.getTimeInMillis();
}
/**
* 主工单催办
* 直接调用列表接口判断是否超期,如果超期则对具体的行动工单执行人进行提醒
*
* @param flowId 主工单ID
*/
public void urge(Long flowId) {
log.info("[urge] 开始催办,flowId:{}", flowId);
// 1. 通过列表接口查询工单,复用已有的超期判断逻辑
ChangeFlowListQueryReq queryReq = new ChangeFlowListQueryReq();
queryReq.setFlowId(flowId);
ChangeFlowListVO listResult = query(1, 1, queryReq);
if (listResult == null || CollectionUtils.isEmpty(listResult.getChangeFlowList())) {
throw ExceptionFactory.createBiz(ResponseCode.DETAIL_FLOW_ERROR, "工单不存在");
}
ChangeFlowVO changeFlowVO = listResult.getChangeFlowList().get(0);
// 2. 判断是否超期
if (changeFlowVO.getIsOverdue() == null || !changeFlowVO.getIsOverdue()) {
log.info("[urge] 工单未超期,无需催办,flowId:{}", flowId);
return;
}
// 3. 查询主工单信息
ChangeRecord changeRecord = changeFlowService.getByFlowId(flowId);
if (changeRecord == null) {
throw ExceptionFactory.createBiz(ResponseCode.DETAIL_FLOW_ERROR, "工单不存在");
}
// 4. 查询该主单下的所有行动工单
List<ChangeSubFlowRecord> allSubFlows = changeSubFlowRecordService.getByChangeRecordIds(
Collections.singletonList(changeRecord.getId()));
if (CollectionUtils.isEmpty(allSubFlows)) {
log.info("[urge] 该主单下没有行动工单,flowId:{}", flowId);
return;
}
// 5. 批量查询所有行动工单对应的行动项
Set<Long> allSubFlowRecordIds = allSubFlows.stream()
.map(ChangeSubFlowRecord::getId)
.collect(Collectors.toSet());
List<ChangeExecRecord> execRecords = changeExecRecordMapper.selectBySubFlowRecordIds(
new ArrayList<>(allSubFlowRecordIds));
// 按行动工单分组
Map<Long, List<ChangeExecRecord>> execRecordsBySubFlow = execRecords.stream()
.filter(exec -> exec.getSubFlowRecordId() != null)
.collect(Collectors.groupingBy(ChangeExecRecord::getSubFlowRecordId));
// 6. 过滤出未完结、未取消且超期的行动工单
Long currentTime = DateUtils.getCurrentTime();
List<ChangeSubFlowRecord> overdueSubFlows = allSubFlows.stream()
.filter(subFlow -> {
// 过滤:只保留未完结或未取消的行动工单
Integer status = subFlow.getStatus();
if (status == null
|| ChangeSubFlowStatusEnum.FINISHED.getStatus().equals(status)
|| ChangeSubFlowStatusEnum.CANCELLED.getStatus().equals(status)) {
return false;
}
// 判断该行动工单对应的行动项是否超期
List<ChangeExecRecord> subFlowExecRecords = execRecordsBySubFlow.getOrDefault(
subFlow.getId(), Collections.emptyList());
// 如果该行动工单没有行动项,不判断超期
if (CollectionUtils.isEmpty(subFlowExecRecords)) {
return false;
}
// 判断行动项的 changeExecFinishTime 是否超过两个工作日
for (ChangeExecRecord execRecord : subFlowExecRecords) {
Long changeExecFinishTime = execRecord.getChangeExecFinishTime();
if (changeExecFinishTime != null && isOverdueByWorkdays(changeExecFinishTime, currentTime, 2)) {
return true; // 只要有一个行动项超期,该行动工单就需要催办
}
}
return false;
})
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(overdueSubFlows)) {
log.info("[urge] 没有超期的行动工单,flowId:{}", flowId);
return;
}
// 7. 对每个超期的行动工单单独发送催办邮件
int sentCount = 0;
for (ChangeSubFlowRecord overdueSubFlow : overdueSubFlows) {
String execUserEmail = overdueSubFlow.getChangeExecUserEmail();
if (StringUtils.isBlank(execUserEmail)) {
log.warn("[urge] 行动工单没有执行人邮箱,跳过,subFlowId:{}", overdueSubFlow.getSubFlowId());
continue;
}
// 获取该行动工单对应的行动项
List<ChangeExecRecord> subFlowExecRecords = execRecordsBySubFlow.getOrDefault(
overdueSubFlow.getId(), Collections.emptyList());
// 构建邮件参数(行动工单相关信息)
Map<String, Object> param = new HashMap<>();
// 行动工单ID
param.put("changeId", overdueSubFlow.getSubFlowId());
// 主工单ID(用于跳转)
param.put("mainFlowId", changeRecord.getFlowId());
param.put("changeSubject", ChangeSubjectEnum.getChangeSubjectEnum(changeRecord.getChangeSubject()).getDesc());
param.put("changeContent", changeRecord.getChangeContent());
param.put("changeReason", changeRecord.getChangeReason());
// 行动工单执行人信息
IusUserInfoRsp execUser = iusService.queryUserInfo(execUserEmail);
param.put("changeExecUser", execUser == null ? execUserEmail : execUser.getName());
param.put("changeExecUserEmail", execUserEmail);
param.put("changeExecDepartment", overdueSubFlow.getChangeExecDepartment());
// 获取变更负责人信息
IusUserInfoRsp commanderUser = iusService.queryUserInfo(changeRecord.getChangeCommander());
param.put("changeCommander", commanderUser == null ? changeRecord.getChangeCommander() : commanderUser.getName());
param.put("changeCommanderEmail", changeRecord.getChangeCommander());
// 行动项列表
List<Map<String, Object>> execProjectList = new ArrayList<>();
for (ChangeExecRecord execRecord : subFlowExecRecords) {
Map<String, Object> execProject = new HashMap<>();
execProject.put("changeExecProject", execRecord.getChangeExecProject());
execProject.put("changeExecFinishTime", execRecord.getChangeExecFinishTime());
execProject.put("changeRiskDesc", execRecord.getChangeRiskDesc());
execProject.put("changeChecking", execRecord.getChangeChecking());
execProjectList.add(execProject);
}
param.put("execProjectList", execProjectList);
// 行动工单URL(使用行动工单ID)
param.put("flowUrl", overdueSubFlow.getSubFlowId());
// 邮件标题:使用行动工单ID
String subjectParam = ChangeSubjectEnum.getChangeSubjectEnum(changeRecord.getChangeSubject()).getDesc()
+ overdueSubFlow.getSubFlowId();
// 收件人:该行动工单的执行人(只发送给行动人,不抄送)
List<String> receiver = Collections.singletonList(execUserEmail);
List<String> ccList = new ArrayList<>(); // 不抄送任何人
// 发送催办邮件
try {
qcSendEmail(receiver, ccList, subjectParam, EmailTemplateEnum.YX_QC_CHANGE_URGE, param);
sentCount++;
log.info("[urge] 行动工单催办邮件已发送,subFlowId:{}, execUserEmail:{}",
overdueSubFlow.getSubFlowId(), execUserEmail);
} catch (Exception e) {
log.error("[urge] 发送行动工单催办邮件失败,subFlowId:{}, execUserEmail:{}, error:{}",
overdueSubFlow.getSubFlowId(), execUserEmail, e.getMessage(), e);
}
}
log.info("[urge] 催办邮件发送完成,flowId:{}, 超期行动工单数:{}, 成功发送数:{}",
flowId, overdueSubFlows.size(), sentCount);
}
}
/**
* @(#)ChangeFlowUrgeReq.java, 2024/12/16.
* <p/>
* Copyright 2024 Netease, Inc. All rights reserved.
* NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.netease.mail.yanxuan.change.dal.meta.model.req;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* 主工单催办请求
*
* @Author system
* @Date 2024/12/16
*/
@Data
public class ChangeFlowUrgeReq {
/**
* 工单id
*/
@NotNull(message = "工单id不能为空")
private Long flowId;
}
......@@ -21,7 +21,9 @@ public enum EmailTemplateEnum {
YX_QC_CHANGE_FINISH("yx_qc_change_finish","工单完结","【已完结】%s 变更风险行动项已完结,请开始执行变更。"),
YX_QC_CHANGE_PENDING("yx_qc_change_pending","待处理","【待处理】执行标准号 执行标准名称已变更 请排查对应的商品变更");
YX_QC_CHANGE_PENDING("yx_qc_change_pending","待处理","【待处理】执行标准号 执行标准名称已变更 请排查对应的商品变更"),
YX_QC_CHANGE_URGE("yx_qc_change_urge","变更催办","【催办提醒】%s 变更工单超期催办通知");
/**
* 模版code
*/
......
......@@ -32,13 +32,13 @@ import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowCancelReq;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowCreateReq;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowDeliverReq;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowListQueryReq;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowUrgeReq;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowRequest;
import com.netease.mail.yanxuan.change.dal.meta.model.req.ChangeFlowSubmitReq;
import com.netease.mail.yanxuan.change.dal.meta.model.rpc.CommanderResponse;
import com.netease.mail.yanxuan.change.dal.meta.model.vo.BasicChangeFlowVO;
import com.netease.mail.yanxuan.change.dal.meta.model.vo.ChangeFlowListVO;
import com.netease.mail.yanxuan.change.dal.meta.model.vo.ChangeFlowVO;
import com.netease.yanxuan.flowx.sdk.meta.dto.base.FlowDataDTO;
import com.netease.yanxuan.flowx.sdk.meta.dto.exec.InterfaceInputDTO;
import com.netease.yanxuan.flowx.sdk.meta.dto.exec.UserBaseContainerDTO;
......@@ -182,6 +182,18 @@ public class ChangeFlowController {
}
/**
* 主工单催办
* 找出超期的行动工单,对具体的行动工单执行人进行提醒
*
* @return
*/
@PostMapping("/urge")
public AjaxResult<Void> urge(@RequestBody @Valid ChangeFlowUrgeReq req) {
changeFlowBiz.urge(req.getFlowId());
return AjaxResult.success();
}
/**
* 引用变更工单
*
* @return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment