Compare commits

...

4 Commits

7 changed files with 279 additions and 20 deletions

View File

@ -49,7 +49,7 @@
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250101</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.7.3</warm-flow.version>
<warm-flow.version>1.7.4</warm-flow.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>

View File

@ -52,6 +52,14 @@ public interface UserService {
*/
String selectEmailById(Long userId);
/**
* 通过用户ID查询用户详细信息
*
* @param userId 用户id
* @return 用户详细信息
*/
UserDTO selectUserDtoById(Long userId);
/**
* 通过用户ID查询用户列表
*

View File

@ -623,6 +623,23 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return ObjectUtils.notNullGetter(sysUser, SysUser::getEmail);
}
/**
* 通过用户ID查询用户详细信息
*
* @param userId 用户id
* @return 用户详细信息
*/
@Override
public UserDTO selectUserDtoById(Long userId) {
SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName,
SysUser::getNickName, SysUser::getUserType, SysUser::getEmail,
SysUser::getPhonenumber, SysUser::getSex, SysUser::getStatus,
SysUser::getCreateTime)
.eq(SysUser::getUserId, userId));
return BeanUtil.toBean(sysUser, UserDTO.class);
}
/**
* 通过用户ID查询用户列表
*
@ -635,7 +652,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return List.of();
}
List<SysUserVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber)
.select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName,
SysUser::getNickName, SysUser::getUserType, SysUser::getEmail,
SysUser::getPhonenumber, SysUser::getSex, SysUser::getStatus,
SysUser::getCreateTime)
.eq(SysUser::getStatus, SystemConstants.NORMAL)
.in(SysUser::getUserId, userIds));
return BeanUtil.copyToList(list, UserDTO.class);
@ -676,7 +696,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
// 获取用户ID列表
Set<Long> userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId);
return selectListByIds(new ArrayList<>(userIds));
return this.selectListByIds(new ArrayList<>(userIds));
}
/**
@ -716,7 +736,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
// 获取用户ID列表
Set<Long> userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId);
return selectListByIds(new ArrayList<>(userIds));
return this.selectListByIds(new ArrayList<>(userIds));
}
/**

View File

@ -0,0 +1,228 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.service.DeptService;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.core.dto.DefJson;
import org.dromara.warm.flow.core.dto.NodeJson;
import org.dromara.warm.flow.core.dto.PromptContent;
import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.orm.entity.FlowHisTask;
import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
import org.dromara.warm.flow.ui.service.ChartExtService;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 流程图提示信息
*
* @author AprilWind
*/
@ConditionalOnEnable
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwChartExtServiceImpl implements ChartExtService {
private final UserService userService;
private final DeptService deptService;
private final FlowHisTaskMapper flowHisTaskMapper;
/**
* 设置流程图提示信息
*
* @param defJson 流程定义json对象
*/
@Override
public void execute(DefJson defJson) {
// 临时修复 后续版本将通过defjson获取流程实例ID
String[] parts = ServletUtils.getRequest().getRequestURI().split("/");
Long instanceId = Long.valueOf(parts[parts.length - 1]);
// 根据流程实例ID查询所有相关的历史任务列表
List<FlowHisTask> flowHisTasks = this.getHisTaskGroupedByNode(instanceId);
if (CollUtil.isEmpty(flowHisTasks)) {
return;
}
// 按节点编号nodeCode对历史任务进行分组
Map<String, List<FlowHisTask>> groupedByNode = flowHisTasks.stream()
.collect(Collectors.groupingBy(FlowHisTask::getNodeCode));
// 批量查询所有审批人的用户信息
List<UserDTO> userDTOList = userService.selectListByIds(
flowHisTasks.stream()
.map(task -> Long.valueOf(task.getApprover()))
.distinct()
.collect(Collectors.toList())
);
// 将查询到的用户列表转换为以用户ID为key的映射
Map<Long, UserDTO> userMap = userDTOList.stream()
.collect(Collectors.toMap(UserDTO::getUserId, user -> user));
// 遍历流程定义中的每个节点调用处理方法将对应节点的任务列表及用户信息传入生成扩展提示内容
for (NodeJson nodeJson : defJson.getNodeList()) {
// 获取当前节点对应的历史任务列表如果没有则返回空列表避免空指针
List<FlowHisTask> taskList = groupedByNode.getOrDefault(nodeJson.getNodeCode(), Collections.emptyList());
// 处理当前节点的扩展信息包括构建审批人提示内容等
this.processNodeExtInfo(nodeJson, taskList, userMap);
}
}
/**
* 初始化流程图提示信息
*
* @param defJson 流程定义json对象
*/
@Override
public void initPromptContent(DefJson defJson) {
ChartExtService.super.initPromptContent(defJson);
// 为每个节点设置统一的提示框样式
defJson.getNodeList().forEach(nodeJson ->
nodeJson.getPromptContent()
.setDialogStyle(
Map.ofEntries(
Map.entry("position", "absolute"),
Map.entry("backgroundColor", "#fff"),
Map.entry("border", "1px solid #ccc"),
Map.entry("borderRadius", "4px"),
Map.entry("boxShadow", "0 2px 8px rgba(0, 0, 0, 0.15)"),
Map.entry("padding", "8px 12px"),
Map.entry("fontSize", "14px"),
Map.entry("zIndex", 1000),
Map.entry("maxWidth", "500px"),
Map.entry("overflowY", "visible"),
Map.entry("overflowX", "hidden"),
Map.entry("color", "#333"),
Map.entry("pointerEvents", "auto"),
Map.entry("scrollbarWidth", "thin")
)
)
);
}
/**
* 处理节点的扩展信息构建用于流程图悬浮提示的内容
*
* @param nodeJson 当前节点对象
* @param taskList 当前节点对应的历史审批任务列表
*/
private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, UserDTO> userMap) {
if (CollUtil.isEmpty(taskList)) {
return;
}
// 获取节点提示内容对象中的 info 列表用于追加提示项
List<PromptContent.InfoItem> info = nodeJson.getPromptContent().getInfo();
// 遍历所有任务记录构建提示内容
for (FlowHisTask task : taskList) {
UserDTO userDTO = userMap.get(Long.valueOf(task.getApprover()));
if (ObjectUtil.isEmpty(userDTO)) {
return;
}
// 查询用户所属部门名称
String deptName = deptService.selectDeptNameByIds(String.valueOf(userDTO.getDeptId()));
// 添加标题项👤 张三市场部
info.add(new PromptContent.InfoItem()
.setPrefix(StringUtils.format("👥 {}{}", userDTO.getNickName(), deptName))
.setPrefixStyle(Map.of(
"fontWeight", "bold",
"fontSize", "15px",
"color", "#333"
))
.setRowStyle(Map.of(
"margin", "8px 0",
"borderBottom", "1px dashed #ccc"
))
);
// 添加具体信息项账号耗时时间
info.add(buildInfoItem("用户账号", userDTO.getUserName()));
info.add(buildInfoItem("审批耗时", DateUtils.getTimeDifference(task.getUpdateTime(), task.getCreateTime())));
info.add(buildInfoItem("办理时间", DateUtils.formatDateTime(task.getUpdateTime())));
}
}
/**
* 构建单条提示内容对象 InfoItem用于悬浮窗显示key: value
*
* @param key 字段名作为前缀
* @param value 字段值
* @return 提示项对象
*/
private PromptContent.InfoItem buildInfoItem(String key, String value) {
return new PromptContent.InfoItem()
// 前缀
.setPrefix(key + ": ")
// 前缀样式
.setPrefixStyle(Map.of(
"textAlign", "right",
"color", "#444",
"userSelect", "none",
"display", "inline-block",
"width", "100px",
"paddingRight", "8px",
"fontWeight", "500",
"fontSize", "14px",
"lineHeight", "24px",
"verticalAlign", "middle"
))
// 内容
.setContent(value)
// 内容样式
.setContentStyle(Map.of(
"backgroundColor", "#f7faff",
"color", "#005cbf",
"padding", "4px 8px",
"fontSize", "14px",
"borderRadius", "4px",
"whiteSpace", "normal",
"border", "1px solid #d0e5ff",
"userSelect", "text",
"lineHeight", "20px"
))
// 行样式
.setRowStyle(Map.of(
"color", "#222",
"alignItems", "center",
"display", "flex",
"marginBottom", "6px",
"fontWeight", "400",
"fontSize", "14px"
));
}
/**
* 根据流程实例ID获取历史任务列表
*
* @param instanceId 流程实例ID
* @return 历史任务列表
*/
public List<FlowHisTask> getHisTaskGroupedByNode(Long instanceId) {
LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
wrapper.eq(FlowHisTask::getInstanceId, instanceId)
.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
.orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime);
return flowHisTaskMapper.selectList(wrapper);
}
}

View File

@ -304,14 +304,14 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
}
//历史任务
LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
wrapper.eq(FlowHisTask::getInstanceId, instanceId);
wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey());
wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime);
wrapper.eq(FlowHisTask::getInstanceId, instanceId)
.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
.orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime);
List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(wrapper);
if (CollUtil.isNotEmpty(flowHisTasks)) {
list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class));
}
return Map.of("list", list,"instanceId",instanceId);
return Map.of("list", list, "instanceId", instanceId);
}
/**

View File

@ -530,7 +530,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
// 构建以下节点数据
List<Task> buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build()));
// 办理人变量替换
ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable);
ExpressionUtil.evalVariable(buildNextTaskList,
FlowParams.build()
.variable(mergeVariable)
);
for (FlowNode flowNode : nextFlowNodes) {
buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> {
if (CollUtil.isNotEmpty(t.getPermissionList())) {

View File

@ -29,7 +29,7 @@ CREATE TABLE `flow_node`
`definition_id` bigint NOT NULL COMMENT '流程定义id',
`node_code` varchar(100) NOT NULL COMMENT '流程节点编码',
`node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称',
`permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用逗号隔开)',
`permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用@@隔开)',
`node_ratio` decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值',
`coordinate` varchar(100) DEFAULT NULL COMMENT '坐标',
`any_node_skip` varchar(100) DEFAULT NULL COMMENT '任意结点跳转',
@ -42,7 +42,7 @@ CREATE TABLE `flow_node`
`version` varchar(20) NOT NULL COMMENT '版本',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`ext` text COMMENT '扩展属性',
`ext` text COMMENT '节点扩展属性',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',
`tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id',
PRIMARY KEY (`id`) USING BTREE
@ -96,7 +96,7 @@ CREATE TABLE `flow_task`
`node_code` varchar(100) NOT NULL COMMENT '节点编码',
`node_name` varchar(100) DEFAULT NULL COMMENT '节点名称',
`node_type` tinyint(1) NOT NULL COMMENT '节点类型0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关',
`flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回',
`flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回',
`form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义Y是 N否',
`form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
@ -108,25 +108,25 @@ CREATE TABLE `flow_task`
CREATE TABLE `flow_his_task`
(
`id` bigint(20) NOT NULL COMMENT '主键id',
`definition_id` bigint(20) NOT NULL COMMENT '对应flow_definition表的id',
`instance_id` bigint(20) NOT NULL COMMENT '对应flow_instance表的id',
`task_id` bigint(20) NOT NULL COMMENT '对应flow_task表的id',
`id` bigint(20) NOT NULL COMMENT '主键id',
`definition_id` bigint(20) NOT NULL COMMENT '对应flow_definition表的id',
`instance_id` bigint(20) NOT NULL COMMENT '对应flow_instance表的id',
`task_id` bigint(20) NOT NULL COMMENT '对应flow_task表的id',
`node_code` varchar(100) DEFAULT NULL COMMENT '开始节点编码',
`node_name` varchar(100) DEFAULT NULL COMMENT '开始节点名称',
`node_type` tinyint(1) DEFAULT NULL COMMENT '开始节点类型0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关',
`target_node_code` varchar(200) DEFAULT NULL COMMENT '目标节点编码',
`target_node_name` varchar(200) DEFAULT NULL COMMENT '结束节点名称',
`approver` varchar(40) DEFAULT NULL COMMENT '审批者',
`cooperate_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)',
`cooperate_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)',
`collaborator` varchar(40) DEFAULT NULL COMMENT '协作人',
`skip_type` varchar(10) NOT NULL COMMENT '流转类型PASS通过 REJECT退回 NONE无动作',
`flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回',
`skip_type` varchar(10) NOT NULL COMMENT '流转类型PASS通过 REJECT退回 NONE无动作',
`flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回',
`form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义Y是 N否',
`form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径',
`message` varchar(500) DEFAULT NULL COMMENT '审批意见',
`variable` TEXT DEFAULT NULL COMMENT '任务变量',
`ext` varchar(500) DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串',
`ext` TEXT DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串',
`create_time` datetime DEFAULT NULL COMMENT '任务开始时间',
`update_time` datetime DEFAULT NULL COMMENT '审批完成时间',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',