Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
Z
zlmy-cloud
Project
Project
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
zlmy
zlmy-cloud
Commits
9d92e76e
Commit
9d92e76e
authored
Dec 04, 2025
by
鲁鸿波
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
所有作业3天未完成的,需要重新发起申请。
危险作业到交底环节后按固定时间判断是否超时,超时自动取消
parent
b60097f4
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
873 additions
and
68 deletions
+873
-68
上线说明.md
zlmy-modules/zlmy-boot/doc/国产化/上线说明.md
+1
-0
HazardWorkPlanScheduler.java
.../com/testor/common/scheduler/HazardWorkPlanScheduler.java
+422
-13
THazardWorkPlanExpiredLogServiceImpl.java
...rd/service/impl/THazardWorkPlanExpiredLogServiceImpl.java
+434
-46
THazardWorkPlanServiceImpl.java
...odule/hazard/service/impl/THazardWorkPlanServiceImpl.java
+15
-9
DynamicCandidateUsersListener.java
...tor/module/hazard/task/DynamicCandidateUsersListener.java
+1
-0
No files found.
zlmy-modules/zlmy-boot/doc/国产化/上线说明.md
View file @
9d92e76e
...
@@ -453,6 +453,7 @@ COMMENT ON COLUMN t_hazard_work_plan.is_full IS '是否7~9人(1:是,2:否)';
...
@@ -453,6 +453,7 @@ COMMENT ON COLUMN t_hazard_work_plan.is_full IS '是否7~9人(1:是,2:否)';
INSERT INTO "t_sys_dict_type" ("dict_id", "dict_name", "dict_type", "is_sys", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids", "tree_sort", "cascaded") VALUES ('1', '危险作业超时检测是否可以执行', 'plan_expired', '1', '0', NULL, '2025-11-28 15:30:11', NULL, '2025-11-28 15:30:11', NULL, NULL, NULL, NULL, '0');
INSERT INTO "t_sys_dict_type" ("dict_id", "dict_name", "dict_type", "is_sys", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids", "tree_sort", "cascaded") VALUES ('1', '危险作业超时检测是否可以执行', 'plan_expired', '1', '0', NULL, '2025-11-28 15:30:11', NULL, '2025-11-28 15:30:11', NULL, NULL, NULL, NULL, '0');
INSERT INTO t_sys_dict_data" ("dict_data_id", "dict_id", "dict_key", "dict_value", "tree_sort", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids") VALUES ('4', '1', '1', '可以执行', '0', '0', NULL, '2025-11-28 15:31:44', NULL, '2025-11-28 15:31:44', NULL, '0', '0,');
INSERT INTO t_sys_dict_data" ("dict_data_id", "dict_id", "dict_key", "dict_value", "tree_sort", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids") VALUES ('4', '1', '1', '可以执行', '0', '0', NULL, '2025-11-28 15:31:44', NULL, '2025-11-28 15:31:44', NULL, '0', '0,');
INSERT INTO "public"."t_sys_dict_data" ("dict_data_id", "dict_id", "dict_key", "dict_value", "tree_sort", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids") VALUES ('5ba9557a160d4d2290128553c473028b', '60c11c98359b4cd09b7220202abf9e0b', '12', '已超时自动取消', '12', '0', '1242684364872761344', '2025-12-01 16:29:54', '1242684364872761344', '2025-12-01 16:29:54', '', '0', '0,');
添加表t_hazard_work_plan_expired_log
添加表t_hazard_work_plan_expired_log
...
...
zlmy-modules/zlmy-boot/src/main/java/com/testor/common/scheduler/HazardWorkPlanScheduler.java
View file @
9d92e76e
...
@@ -3,7 +3,6 @@ package com.testor.common.scheduler;
...
@@ -3,7 +3,6 @@ package com.testor.common.scheduler;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.baomidou.mybatisplus.core.toolkit.StringUtils
;
import
com.baomidou.mybatisplus.core.toolkit.StringUtils
;
import
com.testor.biz.sys.dict.data.model.domain.SysDictData
;
import
com.testor.biz.sys.dict.data.model.domain.SysDictData
;
import
com.testor.biz.sys.org.model.domain.SysOrg
;
import
com.testor.common.core.constant.Constants
;
import
com.testor.common.core.constant.Constants
;
import
com.testor.module.hazard.dao.THazardWorkPlanDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao
;
...
@@ -14,22 +13,32 @@ import com.testor.module.sys.model.domian.NewSysOrg;
...
@@ -14,22 +13,32 @@ import com.testor.module.sys.model.domian.NewSysOrg;
import
com.testor.module.sys.service.NewSysDictDataService
;
import
com.testor.module.sys.service.NewSysDictDataService
;
import
com.testor.common.util.DangerousOperationValidator
;
import
com.testor.common.util.DangerousOperationValidator
;
import
com.testor.module.sys.service.NewSysOrgService
;
import
com.testor.module.sys.service.NewSysOrgService
;
import
com.tongtech.tfw.backend.common.biz.constants.BizConstants
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
org.flowable.engine.HistoryService
;
import
org.flowable.engine.HistoryService
;
import
org.flowable.engine.RepositoryService
;
import
org.flowable.engine.RuntimeService
;
import
org.flowable.engine.RuntimeService
;
import
org.flowable.engine.TaskService
;
import
org.flowable.engine.runtime.ChangeActivityStateBuilder
;
import
org.flowable.engine.runtime.Execution
;
import
org.flowable.engine.runtime.ProcessInstance
;
import
org.flowable.engine.runtime.ProcessInstance
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
import
org.springframework.transaction.annotation.Transactional
;
import
org.springframework.transaction.annotation.Transactional
;
import
javax.annotation.PostConstruct
;
import
javax.annotation.PostConstruct
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
import
java.util.*
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.function.Function
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
import
org.flowable.bpmn.model.BpmnModel
;
import
org.flowable.bpmn.model.UserTask
;
import
org.flowable.bpmn.model.FlowElement
;
import
org.flowable.bpmn.model.Process
;
import
org.flowable.task.api.Task
;
/**
/**
* HazardWorkPlanScheduler - 优化版(版本 B)
* HazardWorkPlanScheduler - 优化版(版本 B)
...
@@ -46,7 +55,6 @@ import java.util.stream.Collectors;
...
@@ -46,7 +55,6 @@ import java.util.stream.Collectors;
@Component
@Component
@Slf4j
@Slf4j
public
class
HazardWorkPlanScheduler
{
public
class
HazardWorkPlanScheduler
{
private
static
final
String
DICT_ENABLE_ID
=
"1"
;
private
static
final
String
DICT_ENABLE_ID
=
"1"
;
private
static
final
String
DICT_ENABLE_VALUE
=
"可以执行"
;
private
static
final
String
DICT_ENABLE_VALUE
=
"可以执行"
;
...
@@ -69,6 +77,12 @@ public class HazardWorkPlanScheduler {
...
@@ -69,6 +77,12 @@ public class HazardWorkPlanScheduler {
@Autowired
@Autowired
private
HistoryService
historyService
;
private
HistoryService
historyService
;
@Autowired
private
RepositoryService
repositoryService
;
@Autowired
private
TaskService
taskService
;
@Autowired
@Autowired
private
NewSysOrgService
orgService
;
private
NewSysOrgService
orgService
;
...
@@ -93,10 +107,10 @@ public class HazardWorkPlanScheduler {
...
@@ -93,10 +107,10 @@ public class HazardWorkPlanScheduler {
try
{
try
{
Map
<
String
,
SysDictData
>
typeMap
=
dictDataService
.
getDictId
(
DICT_WORK_TYPE
)
Map
<
String
,
SysDictData
>
typeMap
=
dictDataService
.
getDictId
(
DICT_WORK_TYPE
)
.
stream
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
Value
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
DataId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
Map
<
String
,
SysDictData
>
levelMap
=
dictDataService
.
getDictId
(
DICT_WORK_LEVEL
)
Map
<
String
,
SysDictData
>
levelMap
=
dictDataService
.
getDictId
(
DICT_WORK_LEVEL
)
.
stream
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
Value
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
DataId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
workTypeCache
.
clear
();
workTypeCache
.
clear
();
workTypeCache
.
putAll
(
typeMap
);
workTypeCache
.
putAll
(
typeMap
);
...
@@ -114,7 +128,7 @@ public class HazardWorkPlanScheduler {
...
@@ -114,7 +128,7 @@ public class HazardWorkPlanScheduler {
/**
/**
* 每5分钟检查安全许可通过后仍在运行的作业
* 每5分钟检查安全许可通过后仍在运行的作业
*/
*/
@Scheduled
(
cron
=
"0 0/5 * * * ?"
)
//
@Scheduled(cron = "0 0/5 * * * ?")
public
void
refreshSwitch
()
{
public
void
refreshSwitch
()
{
if
(!
isSchedulerEnabled
())
{
if
(!
isSchedulerEnabled
())
{
log
.
debug
(
"refreshSwitch: 调度被禁用(字典值非 {})"
,
DICT_ENABLE_VALUE
);
log
.
debug
(
"refreshSwitch: 调度被禁用(字典值非 {})"
,
DICT_ENABLE_VALUE
);
...
@@ -214,10 +228,10 @@ public class HazardWorkPlanScheduler {
...
@@ -214,10 +228,10 @@ public class HazardWorkPlanScheduler {
*/
*/
@Transactional
@Transactional
public
void
handleExpiredOperation
(
THazardWorkPlan
plan
,
public
void
handleExpiredOperation
(
THazardWorkPlan
plan
,
String
validationResult
,
String
validationResult
,
DangerousOperationValidator
.
OperationType
operationType
,
DangerousOperationValidator
.
OperationType
operationType
,
DangerousOperationValidator
.
DangerLevel
dangerLevel
,
DangerousOperationValidator
.
DangerLevel
dangerLevel
,
double
actualHours
)
{
double
actualHours
)
{
log
.
warn
(
"作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}"
,
log
.
warn
(
"作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}"
,
plan
.
getId
(),
plan
.
getCode
(),
plan
.
getId
(),
plan
.
getCode
(),
...
@@ -245,8 +259,8 @@ public class HazardWorkPlanScheduler {
...
@@ -245,8 +259,8 @@ public class HazardWorkPlanScheduler {
log
.
error
(
"插入过期日志失败,planId={}"
,
plan
.
getId
(),
e
);
log
.
error
(
"插入过期日志失败,planId={}"
,
plan
.
getId
(),
e
);
}
}
//
2) 停止 Flowable 流程
//
跳转到安全许可证关闭最后一个节点
stopFlowableProcess
(
plan
);
jumpToLastNode
(
plan
.
getProcessId
()
);
// 3) 更新业务状态
// 3) 更新业务状态
try
{
try
{
...
@@ -282,6 +296,399 @@ public class HazardWorkPlanScheduler {
...
@@ -282,6 +296,399 @@ public class HazardWorkPlanScheduler {
return
diffMillis
/
(
1000.0
*
60
*
60
);
return
diffMillis
/
(
1000.0
*
60
*
60
);
}
}
@Transactional
public
void
jumpToLastNode
(
String
processInstanceId
)
{
try
{
// 1. 获取流程实例
ProcessInstance
instance
=
runtimeService
.
createProcessInstanceQuery
()
.
processInstanceId
(
processInstanceId
)
.
singleResult
();
if
(
instance
==
null
)
{
log
.
warn
(
"流程实例不存在,无法跳转: {}"
,
processInstanceId
);
return
;
}
// 2. 通过节点名称找到目标节点
String
targetActivityId
=
findUserTaskByName
(
instance
.
getProcessDefinitionId
(),
"安全许可证关闭"
);
if
(
targetActivityId
==
null
)
{
log
.
warn
(
"未找到名称为'安全许可证关闭'的节点"
);
return
;
}
log
.
info
(
"找到目标节点: 安全许可证关闭 (ID: {})"
,
targetActivityId
);
// 3. 获取当前任务的处理人
List
<
Task
>
currentTasks
=
taskService
.
createTaskQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(
currentTasks
.
isEmpty
())
{
log
.
warn
(
"当前没有任务,无法获取处理人: {}"
,
processInstanceId
);
return
;
}
// 获取处理人
Set
<
String
>
assignees
=
getTaskAssignees
(
currentTasks
,
processInstanceId
);
if
(
assignees
.
isEmpty
())
{
log
.
warn
(
"无法确定任务处理人,使用默认处理人"
);
// 设置默认处理人
assignees
.
add
(
getDefaultAssignee
(
processInstanceId
));
}
log
.
info
(
"确定的任务处理人: {}"
,
assignees
);
// 4. 获取当前执行流
List
<
Execution
>
executions
=
runtimeService
.
createExecutionQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(
executions
.
isEmpty
())
{
log
.
warn
(
"流程无执行节点,不跳转: {}"
,
processInstanceId
);
return
;
}
// 5. 准备目标节点所需的流程变量
Map
<
String
,
Object
>
processVariables
=
prepareProcessVariables
(
processInstanceId
,
assignees
);
// 6. 使用变更活动状态API跳转,并设置必要的流程变量
ChangeActivityStateBuilder
builder
=
runtimeService
.
createChangeActivityStateBuilder
()
.
processInstanceId
(
processInstanceId
);
for
(
Execution
execution
:
executions
)
{
if
(
execution
.
getActivityId
()
!=
null
)
{
builder
.
moveExecutionToActivityId
(
execution
.
getId
(),
targetActivityId
);
log
.
info
(
"将执行流 {} 从活动 {} 跳转到 {}"
,
execution
.
getId
(),
execution
.
getActivityId
(),
targetActivityId
);
}
}
// 设置流程变量,特别是checkUser变量
builder
.
processVariables
(
processVariables
);
builder
.
changeState
();
// 7. 检查跳转结果
checkJumpResult
(
processInstanceId
,
assignees
,
targetActivityId
);
log
.
info
(
"流程 {} 已成功跳转至'安全许可证关闭'节点,处理人: {}"
,
processInstanceId
,
assignees
);
}
catch
(
Exception
e
)
{
log
.
error
(
"跳转到'安全许可证关闭'节点失败,流程实例ID: {}"
,
processInstanceId
,
e
);
throw
new
RuntimeException
(
"流程跳转失败"
,
e
);
}
}
/**
* 获取任务处理人(简化版本,不使用IdentityLink)
*/
private
Set
<
String
>
getTaskAssignees
(
List
<
Task
>
tasks
,
String
processInstanceId
)
{
Set
<
String
>
assignees
=
new
HashSet
<>();
for
(
Task
task
:
tasks
)
{
// 1. 首先检查任务是否有直接分配人
if
(
task
.
getAssignee
()
!=
null
&&
!
task
.
getAssignee
().
trim
().
isEmpty
())
{
assignees
.
add
(
task
.
getAssignee
());
log
.
info
(
"任务 {} 有分配人: {}"
,
task
.
getName
(),
task
.
getAssignee
());
continue
;
}
// 2. 如果没有分配人,尝试从任务变量中获取
String
potentialAssignee
=
findAssigneeFromTaskVariables
(
task
.
getId
());
if
(
potentialAssignee
!=
null
)
{
assignees
.
add
(
potentialAssignee
);
log
.
info
(
"从任务变量中找到处理人: {}"
,
potentialAssignee
);
continue
;
}
// 3. 尝试从流程变量中获取
potentialAssignee
=
findAssigneeFromProcessVariables
(
processInstanceId
);
if
(
potentialAssignee
!=
null
)
{
assignees
.
add
(
potentialAssignee
);
log
.
info
(
"从流程变量中找到处理人: {}"
,
potentialAssignee
);
}
// 4. 如果以上都没有,记录警告
if
(
assignees
.
isEmpty
())
{
log
.
warn
(
"任务 {} 没有找到处理人信息"
,
task
.
getName
());
}
}
return
assignees
;
}
/**
* 从任务变量中查找处理人
*/
private
String
findAssigneeFromTaskVariables
(
String
taskId
)
{
try
{
Map
<
String
,
Object
>
taskVariables
=
taskService
.
getVariables
(
taskId
);
return
findAssigneeInVariables
(
taskVariables
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"从任务变量中查找处理人失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
* 从流程变量中查找处理人
*/
private
String
findAssigneeFromProcessVariables
(
String
processInstanceId
)
{
try
{
Map
<
String
,
Object
>
processVariables
=
runtimeService
.
getVariables
(
processInstanceId
);
return
findAssigneeInVariables
(
processVariables
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"从流程变量中查找处理人失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
* 在变量映射中查找处理人
*/
private
String
findAssigneeInVariables
(
Map
<
String
,
Object
>
variables
)
{
if
(
variables
==
null
)
{
return
null
;
}
// 常见的处理人变量名
String
[]
potentialAssigneeKeys
=
{
"assignee"
,
"userId"
,
"currentUser"
,
"starter"
,
"initiator"
,
"checkUser"
,
"approver"
,
"handler"
};
for
(
String
key
:
potentialAssigneeKeys
)
{
Object
value
=
variables
.
get
(
key
);
if
(
value
!=
null
&&
!
value
.
toString
().
trim
().
isEmpty
())
{
return
value
.
toString
();
}
}
return
null
;
}
/**
* 获取默认处理人
*/
private
String
getDefaultAssignee
(
String
processInstanceId
)
{
try
{
// 尝试获取流程发起人
ProcessInstance
instance
=
runtimeService
.
createProcessInstanceQuery
()
.
processInstanceId
(
processInstanceId
)
.
singleResult
();
if
(
instance
!=
null
&&
instance
.
getStartUserId
()
!=
null
)
{
return
instance
.
getStartUserId
();
}
// 尝试从流程变量中获取initiator
String
initiator
=
(
String
)
runtimeService
.
getVariable
(
processInstanceId
,
"initiator"
);
if
(
initiator
!=
null
&&
!
initiator
.
trim
().
isEmpty
())
{
return
initiator
;
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"获取默认处理人失败: {}"
,
e
.
getMessage
());
}
// 返回系统默认用户(请根据实际情况修改)
return
"system_admin"
;
}
/**
* 准备流程变量,特别是目标节点需要的变量
*/
private
Map
<
String
,
Object
>
prepareProcessVariables
(
String
processInstanceId
,
Set
<
String
>
assignees
)
{
Map
<
String
,
Object
>
variables
=
new
HashMap
<>();
// 1. 获取现有的流程变量
try
{
Map
<
String
,
Object
>
existingVariables
=
runtimeService
.
getVariables
(
processInstanceId
);
if
(
existingVariables
!=
null
)
{
variables
.
putAll
(
existingVariables
);
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"获取现有流程变量失败: {}"
,
e
.
getMessage
());
}
// 2. 设置目标节点需要的checkUser变量
if
(!
assignees
.
isEmpty
())
{
String
checkUser
=
assignees
.
iterator
().
next
();
variables
.
put
(
"checkUser"
,
checkUser
);
log
.
info
(
"设置checkUser变量: {}"
,
checkUser
);
}
else
{
// 如果没有处理人,设置一个默认值
String
defaultUser
=
getDefaultAssignee
(
processInstanceId
);
variables
.
put
(
"checkUser"
,
defaultUser
);
log
.
warn
(
"没有找到处理人,使用默认值设置checkUser: {}"
,
defaultUser
);
}
// 3. 确保其他可能需要的变量存在
ensureRequiredVariables
(
variables
,
processInstanceId
);
log
.
info
(
"准备的流程变量: {}"
,
variables
.
keySet
());
return
variables
;
}
/**
* 确保其他必要变量存在
*/
private
void
ensureRequiredVariables
(
Map
<
String
,
Object
>
variables
,
String
processInstanceId
)
{
// 添加其他可能需要的变量
if
(!
variables
.
containsKey
(
"approveResult"
))
{
variables
.
put
(
"approveResult"
,
"agree"
);
// 默认同意
}
// 根据业务需求添加其他必要变量
if
(!
variables
.
containsKey
(
"jumpSource"
))
{
variables
.
put
(
"jumpSource"
,
"auto_jump_to_last_node"
);
}
}
/**
* 检查跳转结果
*/
private
void
checkJumpResult
(
String
processInstanceId
,
Set
<
String
>
assignees
,
String
expectedActivityId
)
{
// 等待流程引擎处理
try
{
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
Thread
.
sleep
(
1000
);
List
<
Task
>
newTasks
=
taskService
.
createTaskQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(!
newTasks
.
isEmpty
())
{
processNewTasks
(
newTasks
,
assignees
,
expectedActivityId
);
return
;
}
log
.
info
(
"第 {} 次检查,尚未发现新任务"
,
i
+
1
);
}
log
.
warn
(
"跳转后长时间未发现新任务"
);
checkExecutionState
(
processInstanceId
,
expectedActivityId
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
}
}
/**
* 处理新生成的任务
*/
private
void
processNewTasks
(
List
<
Task
>
newTasks
,
Set
<
String
>
assignees
,
String
expectedActivityId
)
{
boolean
foundTarget
=
false
;
for
(
Task
task
:
newTasks
)
{
String
taskDefinitionKey
=
getTaskDefinitionKey
(
task
);
if
(
expectedActivityId
.
equals
(
taskDefinitionKey
))
{
foundTarget
=
true
;
log
.
info
(
"成功跳转到目标节点: {} ({})"
,
task
.
getName
(),
taskDefinitionKey
);
// 设置任务分配人(如果任务还没有分配人)
if
(
task
.
getAssignee
()
==
null
&&
!
assignees
.
isEmpty
())
{
String
assignee
=
assignees
.
iterator
().
next
();
taskService
.
setAssignee
(
task
.
getId
(),
assignee
);
log
.
info
(
"为任务 '{}' 设置处理人: {}"
,
task
.
getName
(),
assignee
);
}
else
if
(
task
.
getAssignee
()
!=
null
)
{
log
.
info
(
"任务 '{}' 已有处理人: {}"
,
task
.
getName
(),
task
.
getAssignee
());
}
break
;
}
}
if
(!
foundTarget
)
{
log
.
warn
(
"跳转未到达预期节点。预期: {},实际任务: {}"
,
expectedActivityId
,
newTasks
.
stream
()
.
map
(
t
->
t
.
getName
()
+
"("
+
getTaskDefinitionKey
(
t
)
+
")"
)
.
collect
(
Collectors
.
toList
()));
}
}
/**
* 检查执行流状态
*/
private
void
checkExecutionState
(
String
processInstanceId
,
String
expectedActivityId
)
{
try
{
List
<
Execution
>
executions
=
runtimeService
.
createExecutionQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
log
.
info
(
"当前执行流状态:"
);
for
(
Execution
exec
:
executions
)
{
log
.
info
(
"执行流: {}, 活动ID: {}"
,
exec
.
getId
(),
exec
.
getActivityId
());
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查执行流状态失败: {}"
,
e
.
getMessage
());
}
}
/**
* 获取任务定义Key
*/
private
String
getTaskDefinitionKey
(
Task
task
)
{
try
{
// 尝试通过反射获取taskDefinitionKey
java
.
lang
.
reflect
.
Method
method
=
task
.
getClass
().
getMethod
(
"getTaskDefinitionKey"
);
Object
result
=
method
.
invoke
(
task
);
return
result
!=
null
?
result
.
toString
()
:
"unknown"
;
}
catch
(
Exception
e
)
{
// 如果获取失败,返回任务名称作为标识
return
task
.
getName
()
!=
null
?
task
.
getName
()
:
"unknown"
;
}
}
/**
* 通过节点名称查找用户任务
*/
private
String
findUserTaskByName
(
String
processDefinitionId
,
String
taskName
)
{
try
{
BpmnModel
model
=
repositoryService
.
getBpmnModel
(
processDefinitionId
);
if
(
model
==
null
)
{
log
.
error
(
"无法加载 BPMN 模型: {}"
,
processDefinitionId
);
return
null
;
}
Process
process
=
model
.
getMainProcess
();
List
<
UserTask
>
matchingTasks
=
process
.
getFlowElements
().
stream
()
.
filter
(
element
->
element
instanceof
UserTask
)
.
map
(
element
->
(
UserTask
)
element
)
.
filter
(
task
->
taskName
.
equals
(
task
.
getName
()))
.
collect
(
Collectors
.
toList
());
if
(
matchingTasks
.
isEmpty
())
{
log
.
warn
(
"未找到名称为'{}'的用户任务"
,
taskName
);
// 输出所有用户任务名称,用于调试
List
<
UserTask
>
allUserTasks
=
process
.
getFlowElements
().
stream
()
.
filter
(
element
->
element
instanceof
UserTask
)
.
map
(
element
->
(
UserTask
)
element
)
.
collect
(
Collectors
.
toList
());
if
(!
allUserTasks
.
isEmpty
())
{
log
.
info
(
"流程中所有用户任务: {}"
,
allUserTasks
.
stream
().
map
(
UserTask:
:
getName
).
collect
(
Collectors
.
toList
()));
}
return
null
;
}
if
(
matchingTasks
.
size
()
>
1
)
{
log
.
warn
(
"找到多个名称为'{}'的用户任务,将使用第一个"
,
taskName
);
matchingTasks
.
sort
(
Comparator
.
comparing
(
UserTask:
:
getId
));
}
UserTask
targetTask
=
matchingTasks
.
get
(
0
);
log
.
info
(
"找到目标任务: {} (ID: {})"
,
targetTask
.
getName
(),
targetTask
.
getId
());
return
targetTask
.
getId
();
}
catch
(
Exception
e
)
{
log
.
error
(
"查找用户任务失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
/**
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 内部捕获异常,保证调度器稳定运行。
* 内部捕获异常,保证调度器稳定运行。
...
@@ -351,4 +758,6 @@ public class HazardWorkPlanScheduler {
...
@@ -351,4 +758,6 @@ public class HazardWorkPlanScheduler {
throw
e
;
throw
e
;
}
}
}
}
}
}
zlmy-modules/zlmy-boot/src/main/java/com/testor/module/hazard/service/impl/THazardWorkPlanExpiredLogServiceImpl.java
View file @
9d92e76e
package
com
.
testor
.
module
.
hazard
.
service
.
impl
;
package
com
.
testor
.
module
.
hazard
.
service
.
impl
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.baomidou.mybatisplus.core.toolkit.StringUtils
;
import
com.baomidou.mybatisplus.core.toolkit.StringUtils
;
import
com.testor.biz.sys.dict.data.model.domain.SysDictData
;
import
com.testor.biz.sys.dict.data.model.domain.SysDictData
;
import
com.testor.common.core.constant.Constants
;
import
com.testor.common.util.DangerousOperationValidator
;
import
com.testor.common.util.DangerousOperationValidator
;
import
com.testor.module.hazard.dao.THazardWorkPlanDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao
;
import
com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao
;
...
@@ -10,12 +12,23 @@ import com.testor.module.hazard.model.domain.THazardWorkPlan;
...
@@ -10,12 +12,23 @@ import com.testor.module.hazard.model.domain.THazardWorkPlan;
import
com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog
;
import
com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog
;
import
com.testor.module.hazard.model.enums.WorkPlanStatusEnum
;
import
com.testor.module.hazard.model.enums.WorkPlanStatusEnum
;
import
com.testor.module.hazard.service.THazardWorkPlanExpiredLogService
;
import
com.testor.module.hazard.service.THazardWorkPlanExpiredLogService
;
import
com.testor.module.sys.model.domian.NewSysOrg
;
import
com.testor.module.sys.service.NewSysDictDataService
;
import
com.testor.module.sys.service.NewSysDictDataService
;
import
com.testor.module.sys.service.NewSysOrgService
;
import
com.tongtech.tfw.backend.common.models.supers.SuperServiceImpl
;
import
com.tongtech.tfw.backend.common.models.supers.SuperServiceImpl
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
org.flowable.bpmn.model.BpmnModel
;
import
org.flowable.bpmn.model.FlowElement
;
import
org.flowable.bpmn.model.Process
;
import
org.flowable.bpmn.model.UserTask
;
import
org.flowable.engine.HistoryService
;
import
org.flowable.engine.HistoryService
;
import
org.flowable.engine.RepositoryService
;
import
org.flowable.engine.RuntimeService
;
import
org.flowable.engine.RuntimeService
;
import
org.flowable.engine.TaskService
;
import
org.flowable.engine.runtime.ChangeActivityStateBuilder
;
import
org.flowable.engine.runtime.Execution
;
import
org.flowable.engine.runtime.ProcessInstance
;
import
org.flowable.engine.runtime.ProcessInstance
;
import
org.flowable.task.api.Task
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
...
@@ -23,9 +36,7 @@ import org.springframework.transaction.annotation.Transactional;
...
@@ -23,9 +36,7 @@ import org.springframework.transaction.annotation.Transactional;
import
javax.annotation.PostConstruct
;
import
javax.annotation.PostConstruct
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.*
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.function.Function
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
...
@@ -56,6 +67,15 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -56,6 +67,15 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
@Autowired
@Autowired
private
HistoryService
historyService
;
private
HistoryService
historyService
;
@Autowired
private
RepositoryService
repositoryService
;
@Autowired
private
TaskService
taskService
;
@Autowired
private
NewSysOrgService
orgService
;
// 缓存:key = dictValue, value = SysDictData
// 缓存:key = dictValue, value = SysDictData
private
final
Map
<
String
,
SysDictData
>
workTypeCache
=
new
ConcurrentHashMap
<>();
private
final
Map
<
String
,
SysDictData
>
workTypeCache
=
new
ConcurrentHashMap
<>();
private
final
Map
<
String
,
SysDictData
>
workLevelCache
=
new
ConcurrentHashMap
<>();
private
final
Map
<
String
,
SysDictData
>
workLevelCache
=
new
ConcurrentHashMap
<>();
...
@@ -68,6 +88,7 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -68,6 +88,7 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
/**
/**
* 定时刷新字典缓存(每 10 分钟刷新一次,保证字典变更能生效)
* 定时刷新字典缓存(每 10 分钟刷新一次,保证字典变更能生效)
*/
*/
//@Scheduled(cron = "0 0/10 * * * ?")
public
void
refreshDictCacheScheduled
()
{
public
void
refreshDictCacheScheduled
()
{
refreshDictCache
();
refreshDictCache
();
}
}
...
@@ -76,10 +97,10 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -76,10 +97,10 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
try
{
try
{
Map
<
String
,
SysDictData
>
typeMap
=
dictDataService
.
getDictId
(
DICT_WORK_TYPE
)
Map
<
String
,
SysDictData
>
typeMap
=
dictDataService
.
getDictId
(
DICT_WORK_TYPE
)
.
stream
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
Value
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
DataId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
Map
<
String
,
SysDictData
>
levelMap
=
dictDataService
.
getDictId
(
DICT_WORK_LEVEL
)
Map
<
String
,
SysDictData
>
levelMap
=
dictDataService
.
getDictId
(
DICT_WORK_LEVEL
)
.
stream
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
Value
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
.
collect
(
Collectors
.
toMap
(
SysDictData:
:
getDict
DataId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
workTypeCache
.
clear
();
workTypeCache
.
clear
();
workTypeCache
.
putAll
(
typeMap
);
workTypeCache
.
putAll
(
typeMap
);
...
@@ -97,6 +118,7 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -97,6 +118,7 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
/**
/**
* 每5分钟检查安全许可通过后仍在运行的作业
* 每5分钟检查安全许可通过后仍在运行的作业
*/
*/
//@Scheduled(cron = "0 0/5 * * * ?")
@Override
@Override
public
void
refreshSwitch
()
{
public
void
refreshSwitch
()
{
if
(!
isSchedulerEnabled
())
{
if
(!
isSchedulerEnabled
())
{
...
@@ -132,9 +154,6 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -132,9 +154,6 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
log
.
info
(
"结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业"
);
log
.
info
(
"结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业"
);
}
}
/****************************
/****************************
* 核心处理函数
* 核心处理函数
****************************/
****************************/
...
@@ -175,6 +194,12 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -175,6 +194,12 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
return
;
return
;
}
}
NewSysOrg
org
=
orgService
.
getOne
(
new
QueryWrapper
<
NewSysOrg
>().
eq
(
"org_id"
,
plan
.
getOrgId
()));
if
(
org
.
getParentIds
().
contains
(
Constants
.
DBGK_ORG_ID
)
||
org
.
getOrgId
().
equals
(
Constants
.
DBGK_ORG_ID
)){
plan
.
setBl
(
true
);
}
else
{
plan
.
setBl
(
false
);
}
// 计算从审批通过到现在的小时数
// 计算从审批通过到现在的小时数
double
actualHours
=
calculateHoursBetween
(
plan
.
getHazardLicensePassTime
());
double
actualHours
=
calculateHoursBetween
(
plan
.
getHazardLicensePassTime
());
...
@@ -225,8 +250,8 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -225,8 +250,8 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
log
.
error
(
"插入过期日志失败,planId={}"
,
plan
.
getId
(),
e
);
log
.
error
(
"插入过期日志失败,planId={}"
,
plan
.
getId
(),
e
);
}
}
//
2) 停止 Flowable 流程
//
跳转到安全许可证关闭最后一个节点
stopFlowableProcess
(
plan
);
jumpToLastNode
(
plan
.
getProcessId
()
);
// 3) 更新业务状态
// 3) 更新业务状态
try
{
try
{
...
@@ -236,41 +261,6 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -236,41 +261,6 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
}
}
}
}
private
void
handleThreeDaysExpired
(
THazardWorkPlan
plan
,
double
actualHours
)
{
log
.
warn
(
"3天过期 - planId={}, code={}, hoursSinceCreation={}"
,
plan
.
getId
(),
plan
.
getCode
(),
actualHours
);
// 记录日志
try
{
THazardWorkPlanExpiredLog
expiredLog
=
new
THazardWorkPlanExpiredLog
();
expiredLog
.
setPlanId
(
plan
.
getId
());
expiredLog
.
setCode
(
plan
.
getCode
());
expiredLog
.
setWorkType
(
plan
.
getWorkType
());
expiredLog
.
setDangerLevel
(
plan
.
getWorkLevel
());
expiredLog
.
setExpiredReason
(
"作业创建后3天内未完成,需要重新发起申请"
);
expiredLog
.
setBeforeExpiration
(
plan
.
getWorkStatus
());
expiredLog
.
setHazardLicensePassTime
(
plan
.
getHazardLicensePassTime
());
expiredLog
.
setActualDurationHours
(
BigDecimal
.
valueOf
(
actualHours
));
expiredLog
.
setPlanCreateDate
(
plan
.
getCreateDate
());
expiredLog
.
setStatus
(
"0"
);
expiredLog
.
setCreateBy
(
"system-relaxed"
);
expiredLog
.
setCreateDate
(
new
Date
());
hazardWorkPlanExpiredLogDao
.
insert
(
expiredLog
);
}
catch
(
Exception
e
)
{
log
.
error
(
"记录3天过期日志失败,planId={}"
,
plan
.
getId
(),
e
);
}
// 停止流程并更新状态
stopFlowableProcess
(
plan
);
try
{
// 更新状态
updateOperationStatus
(
plan
,
WorkPlanStatusEnum
.
TIMEOUT_CANCELED
.
getValue
());
}
catch
(
Exception
e
)
{
log
.
error
(
"3天过期后更新作业状态失败,planId={}"
,
plan
.
getId
(),
e
);
}
}
/**
/**
* 检查调度是否启用(通过字典开关)
* 检查调度是否启用(通过字典开关)
*/
*/
...
@@ -297,6 +287,399 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -297,6 +287,399 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
return
diffMillis
/
(
1000.0
*
60
*
60
);
return
diffMillis
/
(
1000.0
*
60
*
60
);
}
}
@Transactional
public
void
jumpToLastNode
(
String
processInstanceId
)
{
try
{
// 1. 获取流程实例
ProcessInstance
instance
=
runtimeService
.
createProcessInstanceQuery
()
.
processInstanceId
(
processInstanceId
)
.
singleResult
();
if
(
instance
==
null
)
{
log
.
warn
(
"流程实例不存在,无法跳转: {}"
,
processInstanceId
);
return
;
}
// 2. 通过节点名称找到目标节点
String
targetActivityId
=
findUserTaskByName
(
instance
.
getProcessDefinitionId
(),
"安全许可证关闭"
);
if
(
targetActivityId
==
null
)
{
log
.
warn
(
"未找到名称为'安全许可证关闭'的节点"
);
return
;
}
log
.
info
(
"找到目标节点: 安全许可证关闭 (ID: {})"
,
targetActivityId
);
// 3. 获取当前任务的处理人
List
<
Task
>
currentTasks
=
taskService
.
createTaskQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(
currentTasks
.
isEmpty
())
{
log
.
warn
(
"当前没有任务,无法获取处理人: {}"
,
processInstanceId
);
return
;
}
// 获取处理人
Set
<
String
>
assignees
=
getTaskAssignees
(
currentTasks
,
processInstanceId
);
if
(
assignees
.
isEmpty
())
{
log
.
warn
(
"无法确定任务处理人,使用默认处理人"
);
// 设置默认处理人
assignees
.
add
(
getDefaultAssignee
(
processInstanceId
));
}
log
.
info
(
"确定的任务处理人: {}"
,
assignees
);
// 4. 获取当前执行流
List
<
Execution
>
executions
=
runtimeService
.
createExecutionQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(
executions
.
isEmpty
())
{
log
.
warn
(
"流程无执行节点,不跳转: {}"
,
processInstanceId
);
return
;
}
// 5. 准备目标节点所需的流程变量
Map
<
String
,
Object
>
processVariables
=
prepareProcessVariables
(
processInstanceId
,
assignees
);
// 6. 使用变更活动状态API跳转,并设置必要的流程变量
ChangeActivityStateBuilder
builder
=
runtimeService
.
createChangeActivityStateBuilder
()
.
processInstanceId
(
processInstanceId
);
for
(
Execution
execution
:
executions
)
{
if
(
execution
.
getActivityId
()
!=
null
)
{
builder
.
moveExecutionToActivityId
(
execution
.
getId
(),
targetActivityId
);
log
.
info
(
"将执行流 {} 从活动 {} 跳转到 {}"
,
execution
.
getId
(),
execution
.
getActivityId
(),
targetActivityId
);
}
}
// 设置流程变量,特别是checkUser变量
builder
.
processVariables
(
processVariables
);
builder
.
changeState
();
// 7. 检查跳转结果
checkJumpResult
(
processInstanceId
,
assignees
,
targetActivityId
);
log
.
info
(
"流程 {} 已成功跳转至'安全许可证关闭'节点,处理人: {}"
,
processInstanceId
,
assignees
);
}
catch
(
Exception
e
)
{
log
.
error
(
"跳转到'安全许可证关闭'节点失败,流程实例ID: {}"
,
processInstanceId
,
e
);
throw
new
RuntimeException
(
"流程跳转失败"
,
e
);
}
}
/**
* 获取任务处理人(简化版本,不使用IdentityLink)
*/
private
Set
<
String
>
getTaskAssignees
(
List
<
Task
>
tasks
,
String
processInstanceId
)
{
Set
<
String
>
assignees
=
new
HashSet
<>();
for
(
Task
task
:
tasks
)
{
// 1. 首先检查任务是否有直接分配人
if
(
task
.
getAssignee
()
!=
null
&&
!
task
.
getAssignee
().
trim
().
isEmpty
())
{
assignees
.
add
(
task
.
getAssignee
());
log
.
info
(
"任务 {} 有分配人: {}"
,
task
.
getName
(),
task
.
getAssignee
());
continue
;
}
// 2. 如果没有分配人,尝试从任务变量中获取
String
potentialAssignee
=
findAssigneeFromTaskVariables
(
task
.
getId
());
if
(
potentialAssignee
!=
null
)
{
assignees
.
add
(
potentialAssignee
);
log
.
info
(
"从任务变量中找到处理人: {}"
,
potentialAssignee
);
continue
;
}
// 3. 尝试从流程变量中获取
potentialAssignee
=
findAssigneeFromProcessVariables
(
processInstanceId
);
if
(
potentialAssignee
!=
null
)
{
assignees
.
add
(
potentialAssignee
);
log
.
info
(
"从流程变量中找到处理人: {}"
,
potentialAssignee
);
}
// 4. 如果以上都没有,记录警告
if
(
assignees
.
isEmpty
())
{
log
.
warn
(
"任务 {} 没有找到处理人信息"
,
task
.
getName
());
}
}
return
assignees
;
}
/**
* 从任务变量中查找处理人
*/
private
String
findAssigneeFromTaskVariables
(
String
taskId
)
{
try
{
Map
<
String
,
Object
>
taskVariables
=
taskService
.
getVariables
(
taskId
);
return
findAssigneeInVariables
(
taskVariables
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"从任务变量中查找处理人失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
* 从流程变量中查找处理人
*/
private
String
findAssigneeFromProcessVariables
(
String
processInstanceId
)
{
try
{
Map
<
String
,
Object
>
processVariables
=
runtimeService
.
getVariables
(
processInstanceId
);
return
findAssigneeInVariables
(
processVariables
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"从流程变量中查找处理人失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
* 在变量映射中查找处理人
*/
private
String
findAssigneeInVariables
(
Map
<
String
,
Object
>
variables
)
{
if
(
variables
==
null
)
{
return
null
;
}
// 常见的处理人变量名
String
[]
potentialAssigneeKeys
=
{
"assignee"
,
"userId"
,
"currentUser"
,
"starter"
,
"initiator"
,
"checkUser"
,
"approver"
,
"handler"
};
for
(
String
key
:
potentialAssigneeKeys
)
{
Object
value
=
variables
.
get
(
key
);
if
(
value
!=
null
&&
!
value
.
toString
().
trim
().
isEmpty
())
{
return
value
.
toString
();
}
}
return
null
;
}
/**
* 获取默认处理人
*/
private
String
getDefaultAssignee
(
String
processInstanceId
)
{
try
{
// 尝试获取流程发起人
ProcessInstance
instance
=
runtimeService
.
createProcessInstanceQuery
()
.
processInstanceId
(
processInstanceId
)
.
singleResult
();
if
(
instance
!=
null
&&
instance
.
getStartUserId
()
!=
null
)
{
return
instance
.
getStartUserId
();
}
// 尝试从流程变量中获取initiator
String
initiator
=
(
String
)
runtimeService
.
getVariable
(
processInstanceId
,
"initiator"
);
if
(
initiator
!=
null
&&
!
initiator
.
trim
().
isEmpty
())
{
return
initiator
;
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"获取默认处理人失败: {}"
,
e
.
getMessage
());
}
// 返回系统默认用户(请根据实际情况修改)
return
"system_admin"
;
}
/**
* 准备流程变量,特别是目标节点需要的变量
*/
private
Map
<
String
,
Object
>
prepareProcessVariables
(
String
processInstanceId
,
Set
<
String
>
assignees
)
{
Map
<
String
,
Object
>
variables
=
new
HashMap
<>();
// 1. 获取现有的流程变量
try
{
Map
<
String
,
Object
>
existingVariables
=
runtimeService
.
getVariables
(
processInstanceId
);
if
(
existingVariables
!=
null
)
{
variables
.
putAll
(
existingVariables
);
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"获取现有流程变量失败: {}"
,
e
.
getMessage
());
}
// 2. 设置目标节点需要的checkUser变量
if
(!
assignees
.
isEmpty
())
{
String
checkUser
=
assignees
.
iterator
().
next
();
variables
.
put
(
"checkUser"
,
checkUser
);
log
.
info
(
"设置checkUser变量: {}"
,
checkUser
);
}
else
{
// 如果没有处理人,设置一个默认值
String
defaultUser
=
getDefaultAssignee
(
processInstanceId
);
variables
.
put
(
"checkUser"
,
defaultUser
);
log
.
warn
(
"没有找到处理人,使用默认值设置checkUser: {}"
,
defaultUser
);
}
// 3. 确保其他可能需要的变量存在
ensureRequiredVariables
(
variables
,
processInstanceId
);
log
.
info
(
"准备的流程变量: {}"
,
variables
.
keySet
());
return
variables
;
}
/**
* 确保其他必要变量存在
*/
private
void
ensureRequiredVariables
(
Map
<
String
,
Object
>
variables
,
String
processInstanceId
)
{
// 添加其他可能需要的变量
if
(!
variables
.
containsKey
(
"approveResult"
))
{
variables
.
put
(
"approveResult"
,
"agree"
);
// 默认同意
}
// 根据业务需求添加其他必要变量
if
(!
variables
.
containsKey
(
"jumpSource"
))
{
variables
.
put
(
"jumpSource"
,
"auto_jump_to_last_node"
);
}
}
/**
* 检查跳转结果
*/
private
void
checkJumpResult
(
String
processInstanceId
,
Set
<
String
>
assignees
,
String
expectedActivityId
)
{
// 等待流程引擎处理
try
{
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
Thread
.
sleep
(
1000
);
List
<
Task
>
newTasks
=
taskService
.
createTaskQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
if
(!
newTasks
.
isEmpty
())
{
processNewTasks
(
newTasks
,
assignees
,
expectedActivityId
);
return
;
}
log
.
info
(
"第 {} 次检查,尚未发现新任务"
,
i
+
1
);
}
log
.
warn
(
"跳转后长时间未发现新任务"
);
checkExecutionState
(
processInstanceId
,
expectedActivityId
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
}
}
/**
* 处理新生成的任务
*/
private
void
processNewTasks
(
List
<
Task
>
newTasks
,
Set
<
String
>
assignees
,
String
expectedActivityId
)
{
boolean
foundTarget
=
false
;
for
(
Task
task
:
newTasks
)
{
String
taskDefinitionKey
=
getTaskDefinitionKey
(
task
);
if
(
expectedActivityId
.
equals
(
taskDefinitionKey
))
{
foundTarget
=
true
;
log
.
info
(
"成功跳转到目标节点: {} ({})"
,
task
.
getName
(),
taskDefinitionKey
);
// 设置任务分配人(如果任务还没有分配人)
if
(
task
.
getAssignee
()
==
null
&&
!
assignees
.
isEmpty
())
{
String
assignee
=
assignees
.
iterator
().
next
();
taskService
.
setAssignee
(
task
.
getId
(),
assignee
);
log
.
info
(
"为任务 '{}' 设置处理人: {}"
,
task
.
getName
(),
assignee
);
}
else
if
(
task
.
getAssignee
()
!=
null
)
{
log
.
info
(
"任务 '{}' 已有处理人: {}"
,
task
.
getName
(),
task
.
getAssignee
());
}
break
;
}
}
if
(!
foundTarget
)
{
log
.
warn
(
"跳转未到达预期节点。预期: {},实际任务: {}"
,
expectedActivityId
,
newTasks
.
stream
()
.
map
(
t
->
t
.
getName
()
+
"("
+
getTaskDefinitionKey
(
t
)
+
")"
)
.
collect
(
Collectors
.
toList
()));
}
}
/**
* 检查执行流状态
*/
private
void
checkExecutionState
(
String
processInstanceId
,
String
expectedActivityId
)
{
try
{
List
<
Execution
>
executions
=
runtimeService
.
createExecutionQuery
()
.
processInstanceId
(
processInstanceId
)
.
list
();
log
.
info
(
"当前执行流状态:"
);
for
(
Execution
exec
:
executions
)
{
log
.
info
(
"执行流: {}, 活动ID: {}"
,
exec
.
getId
(),
exec
.
getActivityId
());
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查执行流状态失败: {}"
,
e
.
getMessage
());
}
}
/**
* 获取任务定义Key
*/
private
String
getTaskDefinitionKey
(
Task
task
)
{
try
{
// 尝试通过反射获取taskDefinitionKey
java
.
lang
.
reflect
.
Method
method
=
task
.
getClass
().
getMethod
(
"getTaskDefinitionKey"
);
Object
result
=
method
.
invoke
(
task
);
return
result
!=
null
?
result
.
toString
()
:
"unknown"
;
}
catch
(
Exception
e
)
{
// 如果获取失败,返回任务名称作为标识
return
task
.
getName
()
!=
null
?
task
.
getName
()
:
"unknown"
;
}
}
/**
* 通过节点名称查找用户任务
*/
private
String
findUserTaskByName
(
String
processDefinitionId
,
String
taskName
)
{
try
{
BpmnModel
model
=
repositoryService
.
getBpmnModel
(
processDefinitionId
);
if
(
model
==
null
)
{
log
.
error
(
"无法加载 BPMN 模型: {}"
,
processDefinitionId
);
return
null
;
}
Process
process
=
model
.
getMainProcess
();
List
<
UserTask
>
matchingTasks
=
process
.
getFlowElements
().
stream
()
.
filter
(
element
->
element
instanceof
UserTask
)
.
map
(
element
->
(
UserTask
)
element
)
.
filter
(
task
->
taskName
.
equals
(
task
.
getName
()))
.
collect
(
Collectors
.
toList
());
if
(
matchingTasks
.
isEmpty
())
{
log
.
warn
(
"未找到名称为'{}'的用户任务"
,
taskName
);
// 输出所有用户任务名称,用于调试
List
<
UserTask
>
allUserTasks
=
process
.
getFlowElements
().
stream
()
.
filter
(
element
->
element
instanceof
UserTask
)
.
map
(
element
->
(
UserTask
)
element
)
.
collect
(
Collectors
.
toList
());
if
(!
allUserTasks
.
isEmpty
())
{
log
.
info
(
"流程中所有用户任务: {}"
,
allUserTasks
.
stream
().
map
(
UserTask:
:
getName
).
collect
(
Collectors
.
toList
()));
}
return
null
;
}
if
(
matchingTasks
.
size
()
>
1
)
{
log
.
warn
(
"找到多个名称为'{}'的用户任务,将使用第一个"
,
taskName
);
matchingTasks
.
sort
(
Comparator
.
comparing
(
UserTask:
:
getId
));
}
UserTask
targetTask
=
matchingTasks
.
get
(
0
);
log
.
info
(
"找到目标任务: {} (ID: {})"
,
targetTask
.
getName
(),
targetTask
.
getId
());
return
targetTask
.
getId
();
}
catch
(
Exception
e
)
{
log
.
error
(
"查找用户任务失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
/**
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 内部捕获异常,保证调度器稳定运行。
* 内部捕获异常,保证调度器稳定运行。
...
@@ -370,8 +753,13 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
...
@@ -370,8 +753,13 @@ public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THaza
@Override
@Override
public
THazardWorkPlanExpiredLog
selectByPlanId
(
String
planId
)
{
public
THazardWorkPlanExpiredLog
selectByPlanId
(
String
planId
)
{
return
hazardWorkPlanExpiredLogDao
.
selectOne
(
new
LambdaQueryWrapper
<
THazardWorkPlanExpiredLog
>()
List
<
THazardWorkPlanExpiredLog
>
tHazardWorkPlanExpiredLogs
=
hazardWorkPlanExpiredLogDao
.
selectList
(
new
LambdaQueryWrapper
<
THazardWorkPlanExpiredLog
>()
.
eq
(
THazardWorkPlanExpiredLog:
:
getPlanId
,
planId
));
.
eq
(
THazardWorkPlanExpiredLog:
:
getPlanId
,
planId
));
if
(
null
!=
tHazardWorkPlanExpiredLogs
&&
tHazardWorkPlanExpiredLogs
.
size
()
>
0
){
return
tHazardWorkPlanExpiredLogs
.
get
(
0
);
}
else
{
return
null
;
}
}
}
}
}
zlmy-modules/zlmy-boot/src/main/java/com/testor/module/hazard/service/impl/THazardWorkPlanServiceImpl.java
View file @
9d92e76e
...
@@ -627,26 +627,32 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
...
@@ -627,26 +627,32 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
THazardWorkPlan
updWorkPlan
=
new
THazardWorkPlan
();
THazardWorkPlan
updWorkPlan
=
new
THazardWorkPlan
();
updWorkPlan
.
setId
(
id
);
updWorkPlan
.
setId
(
id
);
updWorkPlan
.
setHazardLicensePassTime
(
new
Date
());
//
updWorkPlan.setHazardLicensePassTime(new Date());
this
.
updateById
(
updWorkPlan
);
this
.
updateById
(
updWorkPlan
);
}
}
//如果为安全许可证关闭
//如果为安全许可证关闭
if
(
WorkPlanStatusEnum
.
TO_BE_CLOSED
.
getValue
().
equals
(
workStatus
))
{
if
(
WorkPlanStatusEnum
.
TO_BE_CLOSED
.
getValue
().
equals
(
workStatus
)
||
WorkPlanStatusEnum
.
TIMEOUT_CANCELED
.
getValue
().
equals
(
workStatus
)
)
{
//关闭流程
//关闭流程
executionWorkProcess
(
id
,
tHazardWorkPlanDTO
.
getTaskId
(),
WorkPlanStatusEnum
.
CLOSED
.
getValue
(),
tHazardWorkPlanDTO
.
getMap
(),
tHazardWorkPlan
);
executionWorkProcess
(
id
,
tHazardWorkPlanDTO
.
getTaskId
(),
WorkPlanStatusEnum
.
CLOSED
.
getValue
(),
tHazardWorkPlanDTO
.
getMap
(),
tHazardWorkPlan
);
THazardWorkPlan
updWorkPlan
=
new
THazardWorkPlan
();
THazardWorkPlan
updWorkPlan
=
new
THazardWorkPlan
();
updWorkPlan
.
setId
(
id
);
updWorkPlan
.
setId
(
id
);
Date
scheduledEndTime
=
tHazardWorkPlan
.
getScheduledEndTime
();
Date
scheduledEndTime
=
tHazardWorkPlan
.
getScheduledEndTime
();
//如果计划结束时间在当前时间之后
if
(
WorkPlanStatusEnum
.
TIMEOUT_CANCELED
.
getValue
().
equals
(
workStatus
)){
if
(
scheduledEndTime
.
after
(
new
Date
()))
{
updWorkPlan
.
setWorkStatus
(
WorkPlanStatusEnum
.
TIMEOUT_CANCELED
.
getValue
());
//更新状态 取消状态
}
else
{
updWorkPlan
.
setWorkStatus
(
WorkPlanStatusEnum
.
CLOSED
.
getValue
());
//如果计划结束时间在当前时间之后
}
else
{
if
(
scheduledEndTime
.
after
(
new
Date
()))
{
//更新状态 超时
//更新状态 取消状态
updWorkPlan
.
setWorkStatus
(
WorkPlanStatusEnum
.
OVERDUE
.
getValue
());
updWorkPlan
.
setWorkStatus
(
WorkPlanStatusEnum
.
CLOSED
.
getValue
());
}
else
{
//更新状态 超时
updWorkPlan
.
setWorkStatus
(
WorkPlanStatusEnum
.
OVERDUE
.
getValue
());
}
}
}
//更新结束时间
//更新结束时间
updWorkPlan
.
setActualEndTime
(
new
Date
());
updWorkPlan
.
setActualEndTime
(
new
Date
());
this
.
updateById
(
updWorkPlan
);
this
.
updateById
(
updWorkPlan
);
...
...
zlmy-modules/zlmy-boot/src/main/java/com/testor/module/hazard/task/DynamicCandidateUsersListener.java
View file @
9d92e76e
...
@@ -88,6 +88,7 @@ public class DynamicCandidateUsersListener implements TaskListener {
...
@@ -88,6 +88,7 @@ public class DynamicCandidateUsersListener implements TaskListener {
String
bizId
=
delegateTask
.
getVariable
(
"bizKeyId"
).
toString
();
String
bizId
=
delegateTask
.
getVariable
(
"bizKeyId"
).
toString
();
THazardWorkPlan
byId
=
tHazardWorkPlanService
.
getById
(
bizId
);
THazardWorkPlan
byId
=
tHazardWorkPlanService
.
getById
(
bizId
);
if
(
byId
!=
null
){
if
(
byId
!=
null
){
byId
.
setHazardLicensePassTime
(
new
Date
());
byId
.
setWorkStatus
(
WorkPlanStatusEnum
.
TECHNICAL_EXPLANATION
.
getValue
());
byId
.
setWorkStatus
(
WorkPlanStatusEnum
.
TECHNICAL_EXPLANATION
.
getValue
());
tHazardWorkPlanService
.
updateById
(
byId
);
tHazardWorkPlanService
.
updateById
(
byId
);
}
}
...
...
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