Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yanxuan-qc-change-system
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
yx-qc-change-flow
yanxuan-qc-change-system
Commits
8476e93e
Commit
8476e93e
authored
Dec 05, 2025
by
王志超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 主单审批,驳回选定的行动工单
parent
c8fc33d3
Pipeline
#86574
passed with stages
in 1 minute 28 seconds
Changes
2
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
126 additions
and
76 deletions
+126
-76
ChangeFlowBiz.java
...om/netease/mail/yanxuan/change/biz/biz/ChangeFlowBiz.java
+123
-74
ChangeFlowSubmitReq.java
...anxuan/change/dal/meta/model/req/ChangeFlowSubmitReq.java
+3
-2
No files found.
yanxuan-qc-change-system-biz/src/main/java/com/netease/mail/yanxuan/change/biz/biz/ChangeFlowBiz.java
View file @
8476e93e
...
...
@@ -1656,8 +1656,23 @@ public class ChangeFlowBiz {
log
.
info
(
"[checkUpdateAndSubmit] 主工单流转完成,flowId:{}, 审批节点:{}, 新节点:{}, 审批结果:通过"
,
flowId
,
node
.
getName
(),
nextNodeId
);
}
else
{
// 审批不通过:回退到确认变更方案节点,并
取消所有
子单
// 审批不通过:回退到确认变更方案节点,并
重置指定的
子单
log
.
warn
(
"[{}] 审批不通过,flowId:{}"
,
node
.
getName
(),
flowId
);
// 验证必须选择要驳回的子单
List
<
String
>
rejectSubFlowIds
=
changeFlowSubmitReq
.
getRejectSubFlowIds
();
if
(
CollectionUtils
.
isEmpty
(
rejectSubFlowIds
))
{
throw
ExceptionFactory
.
createBiz
(
ResponseCode
.
BAD_REQUEST
,
"审批不通过时必须选择要驳回的行动工单"
);
}
// 验证驳回的子单归属于当前主工单,并返回要驳回的子单列表
List
<
ChangeSubFlowRecord
>
subFlowsToReject
=
validateAndFilterSubFlowsToReject
(
changeRecord
.
getId
(),
rejectSubFlowIds
);
String
rejectReason
=
changeFlowSubmitReq
.
getRejectReason
();
if
(
StringUtils
.
isBlank
(
rejectReason
))
{
rejectReason
=
"主单审批不通过"
;
}
Map
<
String
,
Object
>
rejectParamMap
=
new
HashMap
<>();
rejectParamMap
.
put
(
"type"
,
FlowTransitionType
.
TYPE_BACK_TO_CONFIRM_PLAN
);
...
...
@@ -1671,23 +1686,14 @@ public class ChangeFlowBiz {
changeRecord
.
setUpdateTime
(
DateUtils
.
getCurrentTime
());
changeFlowService
.
updateRecord
(
changeRecord
);
// 取消指定的子单(审批人选择要取消的行动工单)
String
rejectReason
=
changeFlowSubmitReq
.
getRejectReason
();
if
(
StringUtils
.
isBlank
(
rejectReason
))
{
rejectReason
=
"主单审批不通过"
;
}
List
<
String
>
cancelSubFlowIds
=
changeFlowSubmitReq
.
getCancelSubFlowIds
();
int
cancelledCount
=
0
;
if
(
CollectionUtils
.
isNotEmpty
(
cancelSubFlowIds
))
{
cancelledCount
=
cancelSelectedSubFlows
(
changeRecord
.
getId
(),
cancelSubFlowIds
,
rejectReason
);
}
// 重置被驳回的子单(已过滤,直接重置并流转工单)
int
resetCount
=
resetRejectedSubFlows
(
subFlowsToReject
,
rejectReason
);
// 发送审批不通过邮件通知
sendMainFlowRejectEmail
(
changeRecord
,
node
.
getName
(),
rejectReason
,
cancelled
Count
);
// sendMainFlowRejectEmail(changeRecord, node.getName(), rejectReason, reset
Count);
log
.
info
(
"[checkUpdateAndSubmit] 主工单流转完成,flowId:{}, 审批节点:{}, 新节点:确认变更方案, 审批结果:不通过,已
取消
子单数:{}"
,
flowId
,
node
.
getName
(),
cancelled
Count
);
log
.
info
(
"[checkUpdateAndSubmit] 主工单流转完成,flowId:{}, 审批节点:{}, 新节点:确认变更方案, 审批结果:不通过,已
驳回
子单数:{}"
,
flowId
,
node
.
getName
(),
reset
Count
);
}
return
nextNodeId
;
...
...
@@ -1781,14 +1787,64 @@ public class ChangeFlowBiz {
}
/**
* 验证子单归属于主工单(查询一次,返回所有子单供后续复用)
*
* @param changeRecordId 主工单ID
* @param subFlowIds 子单ID列表
* @return 主工单下所有子单列表
*/
/**
* 验证并过滤出要驳回的子单
*
* @param changeRecordId 主工单ID
* @param rejectSubFlowIds 要驳回的子单ID列表
* @return 过滤后的要驳回的子单列表
*/
private
List
<
ChangeSubFlowRecord
>
validateAndFilterSubFlowsToReject
(
Long
changeRecordId
,
List
<
String
>
rejectSubFlowIds
)
{
// 查询该主工单下所有子单(只查询一次)
List
<
ChangeSubFlowRecord
>
allSubFlows
=
changeSubFlowRecordMapper
.
selectByChangeRecordId
(
changeRecordId
);
if
(
CollectionUtils
.
isEmpty
(
allSubFlows
))
{
throw
ExceptionFactory
.
createBiz
(
ResponseCode
.
BAD_REQUEST
,
"主工单下没有子单,无法驳回"
);
}
// 构建子单ID到记录的映射
Map
<
String
,
ChangeSubFlowRecord
>
subFlowMap
=
allSubFlows
.
stream
()
.
collect
(
Collectors
.
toMap
(
ChangeSubFlowRecord:
:
getSubFlowId
,
subFlow
->
subFlow
));
// 检查要驳回的子单是否都存在
List
<
String
>
notFoundSubFlowIds
=
rejectSubFlowIds
.
stream
()
.
filter
(
subFlowId
->
!
subFlowMap
.
containsKey
(
subFlowId
))
.
collect
(
Collectors
.
toList
());
if
(
CollectionUtils
.
isNotEmpty
(
notFoundSubFlowIds
))
{
String
notFoundIdsStr
=
String
.
join
(
","
,
notFoundSubFlowIds
);
log
.
warn
(
"[validateAndFilterSubFlowsToReject] 部分行动工单不存在或不归属于当前主工单, changeRecordId:{}, notFoundSubFlowIds:{}"
,
changeRecordId
,
notFoundIdsStr
);
throw
ExceptionFactory
.
createBiz
(
ResponseCode
.
BAD_REQUEST
,
"部分行动工单不存在或不归属于当前主工单:"
+
notFoundIdsStr
);
}
// 过滤出要驳回的子单
List
<
ChangeSubFlowRecord
>
subFlowsToReject
=
rejectSubFlowIds
.
stream
()
.
map
(
subFlowMap:
:
get
)
.
collect
(
Collectors
.
toList
());
log
.
info
(
"[validateAndFilterSubFlowsToReject] 子单归属校验通过, changeRecordId:{}, 总子单数:{}, 待驳回数:{}"
,
changeRecordId
,
allSubFlows
.
size
(),
subFlowsToReject
.
size
());
return
subFlowsToReject
;
}
/**
* 发送主单审批不通过邮件通知
*
* @param changeRecord 主工单记录
* @param rejectNodeName 审批不通过的节点名称
* @param rejectReason 拒绝原因
* @param
cancelledCount 取消
的子单数量
* @param
resetCount 重置
的子单数量
*/
private
void
sendMainFlowRejectEmail
(
ChangeRecord
changeRecord
,
String
rejectNodeName
,
String
rejectReason
,
int
cancelled
Count
)
{
private
void
sendMainFlowRejectEmail
(
ChangeRecord
changeRecord
,
String
rejectNodeName
,
String
rejectReason
,
int
reset
Count
)
{
try
{
// 构建邮件参数
Map
<
String
,
Object
>
emailParam
=
new
HashMap
<>();
...
...
@@ -1805,7 +1861,7 @@ public class ChangeFlowBiz {
emailParam
.
put
(
"changeContent"
,
changeRecord
.
getChangeContent
());
emailParam
.
put
(
"rejectNodeName"
,
rejectNodeName
);
emailParam
.
put
(
"rejectReason"
,
rejectReason
);
emailParam
.
put
(
"
cancelledSubFlowCount"
,
cancelled
Count
);
emailParam
.
put
(
"
resetSubFlowCount"
,
reset
Count
);
emailParam
.
put
(
"flowUrl"
,
changeRecord
.
getFlowId
());
String
subjectParam
=
changeRecord
.
getFlowId
().
toString
();
...
...
@@ -1856,95 +1912,88 @@ public class ChangeFlowBiz {
}
/**
* 取消主工单下指定的子单(主单审批不通过时调用)
* 重置被驳回的子单(主单审批不通过时调用)
* 将子单状态和节点重置到初始状态,保留流程记录
*
* @param changeRecordId 主工单ID
* @param cancelSubFlowIds 要取消的子单ID列表
* @param cancelReason 取消原因
* @return 实际取消的子单数量
* @param subFlowsToReject 要驳回的子单列表(已过滤,直接处理)
* @param rejectReason 驳回原因
* @return 实际重置的子单数量
*/
private
int
cancelSelectedSubFlows
(
Long
changeRecordId
,
List
<
String
>
cancelSubFlowIds
,
String
cancel
Reason
)
{
if
(
CollectionUtils
.
isEmpty
(
cancelSubFlowIds
))
{
log
.
info
(
"[
cancelSelectedSubFlows] 未指定要取消的子单,changeRecordId:{}"
,
changeRecordId
);
private
int
resetRejectedSubFlows
(
List
<
ChangeSubFlowRecord
>
subFlowsToReject
,
String
reject
Reason
)
{
if
(
CollectionUtils
.
isEmpty
(
subFlowsToReject
))
{
log
.
info
(
"[
resetRejectedSubFlows] 未指定要驳回的子单"
);
return
0
;
}
// 查询该主工单下所有子单
List
<
ChangeSubFlowRecord
>
allSubFlows
=
changeSubFlowRecordMapper
.
selectByChangeRecordId
(
changeRecordId
);
if
(
CollectionUtils
.
isEmpty
(
allSubFlows
))
{
log
.
info
(
"[cancelSelectedSubFlows] 主工单没有子单,changeRecordId:{}"
,
changeRecordId
);
return
0
;
}
log
.
info
(
"[resetRejectedSubFlows] 开始重置被驳回的子单,待重置数量:{}, 驳回原因:{}"
,
subFlowsToReject
.
size
(),
rejectReason
);
// 过滤出要取消的子单
List
<
ChangeSubFlowRecord
>
subFlowsToCancel
=
allSubFlows
.
stream
()
.
filter
(
subFlow
->
cancelSubFlowIds
.
contains
(
subFlow
.
getSubFlowId
()))
.
collect
(
Collectors
.
toList
());
if
(
CollectionUtils
.
isEmpty
(
subFlowsToCancel
))
{
log
.
warn
(
"[cancelSelectedSubFlows] 未找到要取消的子单,changeRecordId:{}, cancelSubFlowIds:{}"
,
changeRecordId
,
cancelSubFlowIds
);
return
0
;
}
log
.
info
(
"[cancelSelectedSubFlows] 开始取消指定子单,changeRecordId:{}, 待取消数量:{}, 总数量:{}, 取消原因:{}"
,
changeRecordId
,
subFlowsToCancel
.
size
(),
allSubFlows
.
size
(),
cancelReason
);
int
cancelledCount
=
0
;
int
resetCount
=
0
;
int
skippedCount
=
0
;
for
(
ChangeSubFlowRecord
subFlowRecord
:
subFlowsToCancel
)
{
Integer
currentStatus
=
subFlowRecord
.
getStatus
();
// 直接遍历要驳回的子单并重置(需要流转工单流程)
for
(
ChangeSubFlowRecord
subFlowRecord
:
subFlowsToReject
)
{
String
subFlowId
=
subFlowRecord
.
getSubFlowId
();
String
oldNodeId
=
subFlowRecord
.
getSubFlowNode
();
// 跳过已完成或已取消的子单
if
(
ChangeSubFlowStatusEnum
.
FINISHED
.
getStatus
().
equals
(
currentStatus
)
||
ChangeSubFlowStatusEnum
.
CANCELLED
.
getStatus
().
equals
(
currentStatus
))
{
log
.
info
(
"[cancelAllSubFlows] 子单已完成或已取消,跳过, subFlowId:{}, status:{}"
,
subFlowId
,
currentStatus
);
try
{
Integer
currentStatus
=
subFlowRecord
.
getStatus
();
// 跳过已取消的子单
if
(
ChangeSubFlowStatusEnum
.
CANCELLED
.
getStatus
().
equals
(
currentStatus
))
{
log
.
info
(
"[resetRejectedSubFlows] 子单已取消,跳过, subFlowId:{}, status:{}"
,
subFlowId
,
currentStatus
);
skippedCount
++;
continue
;
}
try
{
// 查询子单流程详情
FlowDataDTO
subFlowDataDTO
=
flowService
.
flowDetail
(
subFlowId
);
if
(
subFlowDataDTO
==
null
)
{
log
.
warn
(
"[
cancelAll
SubFlows] 子单流程查询失败,跳过, subFlowId:{}"
,
subFlowId
);
log
.
warn
(
"[
resetRejected
SubFlows] 子单流程查询失败,跳过, subFlowId:{}"
,
subFlowId
);
skippedCount
++;
continue
;
}
// 构建提交内容
Map
<
String
,
Object
>
cancel
Content
=
new
HashMap
<>(
CommonConstants
.
INIT_HASH_MAP_SIZE
);
cancel
Content
.
put
(
"updateTime"
,
System
.
currentTimeMillis
());
cancelContent
.
put
(
CommonConstants
.
FLOW_OPERATION_KEY
,
FlowOperationTypeEnum
.
REFUSE
.
getValue
());
Map
<
String
,
Object
>
subFlow
Content
=
new
HashMap
<>(
CommonConstants
.
INIT_HASH_MAP_SIZE
);
subFlow
Content
.
put
(
"updateTime"
,
System
.
currentTimeMillis
());
subFlowContent
.
put
(
CommonConstants
.
FLOW_OPERATION_KEY
,
FlowOperationTypeEnum
.
PASS
.
getValue
());
// 提交子单流程到结束节点(取消)
String
cancelNodeId
=
flowService
.
submitFlow
(
subFlowId
,
subFlowDataDTO
,
RequestLocalBean
.
getUid
(),
ChangeFlowEnum
.
CHANGE_SUB_FLOW
.
getTopoId
(),
JSON
.
toJSONString
(
cancelContent
),
false
,
FlowxOperationEnum
.
SUBMIT
.
getName
(),
"主单审批不通过,自动取消"
,
subFlowRecord
.
getCreateTime
());
// 使用 paramMap 流转子单工单(type=2 回到初始节点)
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
paramMap
.
put
(
"type"
,
FlowTransitionType
.
TYPE_REJECTED
);
// 更新子单记录
subFlowRecord
.
setSubFlowNode
(
cancelNodeId
);
subFlowRecord
.
setStatus
(
ChangeSubFlowStatusEnum
.
CANCELLED
.
getStatus
());
subFlowRecord
.
setCancelReason
(
cancelReason
);
String
startNodeId
=
flowService
.
submitFlowWithParamMap
(
subFlowId
,
subFlowDataDTO
,
RequestLocalBean
.
getUid
(),
ChangeFlowEnum
.
CHANGE_SUB_FLOW
.
getTopoId
(),
JSON
.
toJSONString
(
subFlowContent
),
paramMap
,
FlowxOperationEnum
.
SUBMIT
.
getName
(),
"主单审批不通过,驳回行动工单"
,
subFlowRecord
.
getCreateTime
());
// 重置子单记录到初始状态
subFlowRecord
.
setSubFlowNode
(
startNodeId
);
subFlowRecord
.
setStatus
(
ChangeSubFlowStatusEnum
.
WAIT_CONFIRM_ACTION_PLAN
.
getStatus
());
// 重置审批人为行动人自己
subFlowRecord
.
setApprover
(
JSON
.
toJSONString
(
Collections
.
singletonList
(
subFlowRecord
.
getChangeExecUserEmail
())));
// 清空审批相关字段,保存驳回原因
subFlowRecord
.
setRejectReason
(
rejectReason
);
subFlowRecord
.
setChangeResult
(
null
);
subFlowRecord
.
setRemark
(
null
);
subFlowRecord
.
setUpdateTime
(
DateUtils
.
getCurrentTime
());
changeSubFlowRecordService
.
update
(
subFlowRecord
);
cancelledCount
++;
log
.
info
(
"[cancelAllSubFlows] 子单已取消, subFlowId:{}, nodeId:{}"
,
subFlowId
,
cancelNodeId
);
resetCount
++;
log
.
info
(
"[resetRejectedSubFlows] 子单已驳回并重置到初始状态, subFlowId:{}, 原节点:{}, 新节点:{}, status:{}"
,
subFlowId
,
oldNodeId
,
startNodeId
,
ChangeSubFlowStatusEnum
.
WAIT_CONFIRM_ACTION_PLAN
.
getStatus
());
}
catch
(
Exception
e
)
{
log
.
error
(
"[
cancelAllSubFlows] 取消
子单失败, subFlowId:{}, error:{}"
,
subFlowId
,
e
.
getMessage
(),
e
);
// 继续
取消
下一个子单,不中断整个流程
log
.
error
(
"[
resetRejectedSubFlows] 重置
子单失败, subFlowId:{}, error:{}"
,
subFlowId
,
e
.
getMessage
(),
e
);
// 继续
重置
下一个子单,不中断整个流程
}
}
log
.
info
(
"[
cancelSelectedSubFlows] 子单取消完成,changeRecordId:{}, 待取消数:{}, 已取消
:{}, 跳过:{}"
,
changeRecordId
,
subFlowsToCancel
.
size
(),
cancelled
Count
,
skippedCount
);
log
.
info
(
"[
resetRejectedSubFlows] 子单重置完成,待重置数:{}, 已重置
:{}, 跳过:{}"
,
subFlowsToReject
.
size
(),
reset
Count
,
skippedCount
);
return
cancelled
Count
;
return
reset
Count
;
}
}
yanxuan-qc-change-system-dal/src/main/java/com/netease/mail/yanxuan/change/dal/meta/model/req/ChangeFlowSubmitReq.java
View file @
8476e93e
...
...
@@ -132,7 +132,7 @@ public class ChangeFlowSubmitReq {
private
String
rejectReason
;
/**
* 要
取消的子单ID列表(审批不通过时,选择要取消的行动工单
)
* 要
驳回的子单ID列表(审批不通过时,选择要驳回的行动工单,必填
)
*/
private
List
<
String
>
cancel
SubFlowIds
;
private
List
<
String
>
reject
SubFlowIds
;
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment