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
37df8b1d
Commit
37df8b1d
authored
Dec 16, 2025
by
王志超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 催办
parent
6b6c23d8
Pipeline
#86999
passed with stages
in 1 minute 40 seconds
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
208 additions
and
2 deletions
+208
-2
ChangeFlowBiz.java
...om/netease/mail/yanxuan/change/biz/biz/ChangeFlowBiz.java
+164
-0
ChangeFlowUrgeReq.java
.../yanxuan/change/dal/meta/model/req/ChangeFlowUrgeReq.java
+28
-0
EmailTemplateEnum.java
...uan/change/integration/email/enums/EmailTemplateEnum.java
+3
-1
ChangeFlowController.java
...l/yanxuan/change/web/controller/ChangeFlowController.java
+13
-1
No files found.
yanxuan-qc-change-system-biz/src/main/java/com/netease/mail/yanxuan/change/biz/biz/ChangeFlowBiz.java
View file @
37df8b1d
...
...
@@ -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
);
}
}
yanxuan-qc-change-system-dal/src/main/java/com/netease/mail/yanxuan/change/dal/meta/model/req/ChangeFlowUrgeReq.java
0 → 100644
View file @
37df8b1d
/**
* @(#)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
;
}
yanxuan-qc-change-system-integration/src/main/java/com/netease/mail/yanxuan/change/integration/email/enums/EmailTemplateEnum.java
View file @
37df8b1d
...
...
@@ -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
*/
...
...
yanxuan-qc-change-system-web/src/main/java/com/netease/mail/yanxuan/change/web/controller/ChangeFlowController.java
View file @
37df8b1d
...
...
@@ -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
...
...
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