OA流程相关

自定义规则添加和修改
This commit is contained in:
furongxin 2024-03-12 16:48:24 +08:00
parent eec371dd2e
commit ca64e7d916
16 changed files with 267 additions and 32 deletions

View File

@ -18,7 +18,8 @@ public enum BpmTaskRuleScriptEnum {
LEADER_X1(20L, "流程发起人的一级领导"),
LEADER_X2(21L, "流程发起人的二级领导"),
LEADER_X3(22L, "流程发起人的三级领导"),
LEADER_X4(23L, "审批人的一级领导");
LEADER_X4(23L, "审批人的一级领导"),
LEADER_X5(24L, "调岗部门领导");
/**

View File

@ -42,7 +42,7 @@ public class BpmProcessDefinitionController {
@GetMapping ("/list")
@Operation(summary = "获得流程定义列表")
@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
// @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
BpmProcessDefinitionListReqVO listReqVO) {
return success(bpmDefinitionService.getProcessDefinitionList(listReqVO));

View File

@ -39,7 +39,7 @@ public class BpmOACashCreateReqVO {
private LocalDate applicationDate;
@Schema(description = "备注说明", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal notes;
private String notes;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems;

View File

@ -38,7 +38,7 @@ public class BpmOACashRespVO extends BpmOABaseRespVO {
private LocalDate applicationDate;
@Schema(description = "备注说明", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal notes;
private String notes;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems;

View File

@ -11,4 +11,8 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface BpmOAShiftjobsMapper extends BaseMapperX<BpmOAShiftjobsDO> {
default BpmOAShiftjobsDO selectByProcessInstanceId(String processInstanceId) {
return selectOne(BpmOAShiftjobsDO::getProcessInstanceId, processInstanceId);
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
@ -52,19 +53,61 @@ public class BpmTaskCurrentAssignLeaderScript extends BpmTaskAssignLeaderAbstrac
if (CollUtil.isEmpty(bpmTaskRespVOs)) {
return emptySet();
}
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
//获取最后一个Task任务相关数据
BpmTaskRespVO bpmTaskRespVO = bpmTaskRespVOs.get(0) ;
Long assigneeUserId = bpmTaskRespVO.getAssigneeUser().getId() ;
// 获取当前审批用户的部门信息
DeptRespDTO dept = getUserDept(assigneeUserId);
// 获得对应 发起人 的部门
DeptRespDTO dept = getUserDept(startUserId);
if (dept == null) { // 找不到发起人的部门所以无法使用该规则
return emptySet();
}
int count = 0;
for (int i = 0; i < dept.getLevel(); i++) {
if (i == 0) { //第一次查找 判断审批人是否为发起人部门负责人
if (!dept.getLeaderUserId().toString().equals(assigneeUserId.toString())){ //如果发起人上级部门负责人不为当前审批人时
count = 1;
break;
}
}
//获得发起人的上级部门
DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData();
if (parentDept == null) { // 找不到父级部门原因是人的所属部门配在了最高节点了
if (parentDept == null) { // 找不到父级部门所以只好结束寻找原因是例如说级别比较高的人所在部门层级比较少
return emptySet();
}
return parentDept.getLeaderUserId() != null ? asSet(parentDept.getLeaderUserId()) : emptySet();
if (!parentDept.getLeaderUserId().toString().equals(assigneeUserId.toString())){ //如果发起人上级部门负责人不为当前审批人时
dept = parentDept;
count = 1;
break;
}
}
if (count == 0){// 找不到父级部门原因是人的所属部门配在了最高节点了
return emptySet();
}
return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
// //获取最后一个Task任务相关数据
// BpmTaskRespVO bpmTaskRespVO = bpmTaskRespVOs.get(0) ;
// Long assigneeUserId = bpmTaskRespVO.getAssigneeUser().getId() ;
// // 获取当前审批用户的部门信息
// DeptRespDTO dept = getUserDept(assigneeUserId);
// if (dept == null) { // 找不到发起人的部门所以无法使用该规则
// return emptySet();
// }
// DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData();
// if (parentDept == null) { // 找不到父级部门原因是人的所属部门配在了最高节点了
// return emptySet();
// }
// return parentDept.getLeaderUserId() != null ? asSet(parentDept.getLeaderUserId()) : emptySet();
}
@Override

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAShiftjobsDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAShiftjobsMapper;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static java.util.Collections.emptySet;
@Component
public class BpmTaskShiftjobsLeaderScript implements BpmTaskAssignScript {
@Resource
private DeptApi deptApi;
@Resource
private BpmOAShiftjobsMapper bpmOAShiftjobsMapper;
@Resource
@Lazy // 解决循环依赖
private BpmProcessInstanceService bpmProcessInstanceService;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService bpmTaskService ;
@Override
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
// 获得发起人
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
List<BpmTaskRespVO> bpmTaskRespVOs = bpmTaskService.getTaskListByProcessInstanceId(processInstance.getProcessInstanceId());
if (CollUtil.isEmpty(bpmTaskRespVOs)) {
return emptySet();
}
//根据流程实例ID 取到调岗流程表单
BpmOAShiftjobsDO shiftjobsDO = bpmOAShiftjobsMapper.selectByProcessInstanceId(processInstance.getProcessInstanceId());
//获取调岗部门ID
Long deptId = shiftjobsDO.getNewDeptId();
//根据部门ID 获取部门信息
DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData();
if (dept.getLevel() > 3) { //判断部门层级
String [] flag = dept.getFlag().split("-");
dept = deptApi.getDept(Long.valueOf(flag[3])).getCheckedData();
}
return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
}
@Override
public BpmTaskRuleScriptEnum getEnum() {
return BpmTaskRuleScriptEnum.LEADER_X5;
}
}

View File

@ -12,9 +12,9 @@ import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
@ -31,13 +31,12 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
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.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
@ -47,7 +46,6 @@ import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
@ -64,9 +62,6 @@ import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_TARGET_NODE_NOT_EXISTS;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageReqVO;
/**
* 流程任务实例 Service 实现类
@ -375,29 +370,57 @@ public class BpmTaskServiceImpl implements BpmTaskService {
* 根据当前审核人的岗位判定流程走向
*/
Map<String, Object> paramMap = instance.getProcessVariables() ;
// 获得 User
AdminUserRespDTO examineUser = adminUserApi.getUser(userId).getCheckedData();
// // 获得 User
// AdminUserRespDTO examineUser = adminUserApi.getUser(userId).getCheckedData();
// // 获取部门信息
// Long deptId = examineUser.getDeptId();
//获取流程发起人User
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
//获取流程发起人部门信息
DeptRespDTO startDeptInfo = deptApi.getDept(startUser.getDeptId()).getCheckedData();
// 获取岗位信息
Set<Long> postIds = examineUser.getPostIds();
// 获取部门信息
Long deptId = examineUser.getDeptId();
Set<Long> postIds = startUser.getPostIds();
//根据审批人用户ID获取审批人 担任的负责人的部门信息
List<DeptRespDTO> deptRespDTOs = deptApi.getDeptByLeaderId(userId).getCheckedData();
//遍历查找 发起人的上级部门
for (DeptRespDTO deptRespDTO : deptRespDTOs) {
if (startDeptInfo.getFlag().contains(deptRespDTO.getFlag())) {
paramMap.put("level", deptRespDTO.getLevel().toString());
break;
}
}
//如果审核人是发起人的时候
if (userId.toString().equals(instance.getStartUserId())) {
//获取发起人部门负责人编号和部门层级
paramMap.put("leader_id", startDeptInfo.getLeaderUserId().toString());
paramMap.put("level", startDeptInfo.getLevel().toString());
}
if( postIds == null || postIds.isEmpty() ) {
// 当前审批用户未配置岗位
// 操作失败原因您未配置岗位请联系管理员操作
throw exception(TASK_OPERATE_FAIL_USER_NO_POST);
}
if( deptId == null ) {
// 当前审批用户未配置部门
// 操作失败原因您未配置部门请联系管理员操作
throw exception(TASK_OPERATE_FAIL_USER_NO_DEPT);
}
// if( deptId == null ) {
// // 当前审批用户未配置部门
// // 操作失败原因您未配置部门请联系管理员操作
// throw exception(TASK_OPERATE_FAIL_USER_NO_DEPT);
// }
ArrayList<Long> list = new ArrayList<>(postIds);
// 只获配置的首个岗位
Long postId = list.get(0);
paramMap.put("post_id", postId.toString());
paramMap.put("dept_id", deptId.toString());
paramMap.put("user_id", userId.toString());
paramMap.put("dept_id", startUser.getDeptId().toString()); //配置发起人部门id
paramMap.put("approve_reason",reqVO.getReason()) ; //通过原因---因为Task任务的通过是通过监听事件处理的reason数据无法传递给监听函数所以通过此中方法传递
taskService.complete(task.getId(), paramMap);
// 更新任务拓展表为通过

View File

@ -37,6 +37,11 @@ public interface DeptApi {
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/getByLeaderId")
@Operation(summary = "获取特定部门信息")
@Parameter(name = "leaderUserId", description = "部门负责人", example = "12", required = true)
CommonResult<List<DeptRespDTO>> getDeptByLeaderId(@RequestParam("leaderUserId")Long leaderUserId);
/**
* 获得指定编号的部门 Map
*
@ -47,5 +52,4 @@ public interface DeptApi {
List<DeptRespDTO> list = getDeptList(ids).getCheckedData();
return CollectionUtils.convertMap(list, DeptRespDTO::getId);
}
}

View File

@ -16,9 +16,15 @@ public class DeptRespDTO {
@Schema(description = "父部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long parentId;
@Schema(description = "部门层级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer level;
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long leaderUserId;
@Schema(description = "部门标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String flag;
@Schema(description = "部门状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举

View File

@ -42,4 +42,11 @@ public class DeptApiImpl implements DeptApi {
return success(true);
}
@Override
@DataPermission(enable = false)
public CommonResult<List<DeptRespDTO>> getDeptByLeaderId(Long leaderUserId) {
List<DeptDO> deptDO = deptService.getDeptByLeaderId(leaderUserId);
return success(BeanUtils.toBean(deptDO, DeptRespDTO.class));
}
}

View File

@ -38,6 +38,10 @@ public class DeptDO extends TenantBaseDO {
* 关联 {@link #id}
*/
private Long parentId;
/**
* 部门层级
*/
private Integer level;
/**
* 显示顺序
*/
@ -56,6 +60,10 @@ public class DeptDO extends TenantBaseDO {
* 邮箱
*/
private String email;
/**
* 标识符
*/
private String flag;
/**
* 部门状态
*

View File

@ -30,4 +30,15 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
return selectList(DeptDO::getParentId, parentIds);
}
default List<DeptDO> selectByLeaderId(Long leaderUserId) {
return selectList(new LambdaQueryWrapperX<DeptDO>()
.eq(DeptDO::getLeaderUserId, leaderUserId)
.orderByAsc(DeptDO::getLevel));
}
default List<DeptDO> selectLikeFlag(String flag) {
return selectList(new LambdaQueryWrapperX<DeptDO>().like(DeptDO::getFlag, flag));
}
}

View File

@ -99,4 +99,8 @@ public interface DeptService {
*/
void validateDeptList(Collection<Long> ids);
/**
* 根据用户ID 检索该用户所担任负责人的部门list
*/
List<DeptDO> getDeptByLeaderId(Long leaderUserId);
}

View File

@ -51,6 +51,22 @@ public class DeptServiceImpl implements DeptService {
// 插入部门
DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
if (dept.getParentId().toString().equals("0")) {
dept.setLevel(0);
dept.setFlag(dept.getId().toString());
}else {
//获取上级部门信息
DeptDO patentDeptDO = getDept(dept.getParentId());
//新建部门层级为 上级部门层级+1
dept.setLevel(patentDeptDO.getLevel() + 1);
//新建部门标识符为 上级部门标识符+本部门ID
dept.setFlag(patentDeptDO.getFlag() + "-" + dept.getId().toString());
}
deptMapper.insert(dept);
return dept.getId();
}
@ -69,11 +85,43 @@ public class DeptServiceImpl implements DeptService {
// 校验部门名的唯一性
validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
//更新前部门 dept
DeptDO oldDeptDO = getDept(updateReqVO.getId());
// 更新部门
DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
if(updateObj.getLeaderUserId() == null)
updateObj.setLeaderUserId(new Long(0)) ;
// 如果变换上级部门
if (!updateObj.getParentId().toString().equals(oldDeptDO.getParentId().toString())) {
//获取更新后上级部门信息
DeptDO patentDeptDO = getDept(updateObj.getParentId());
//获取所有下级部门
List<DeptDO> deptDOList = deptMapper.selectLikeFlag(oldDeptDO.getFlag());
String oldFlag = oldDeptDO.getFlag();
String newFlag = patentDeptDO.getFlag() + "-" + updateObj.getId().toString();
//遍历下级部门设置部门层级和标识符
for (DeptDO deptDO : deptDOList) {
if (deptDO.getId().toString().equals(updateObj.getId().toString())) {
continue;
}
String flag = deptDO.getFlag();
deptDO.setFlag(flag.replace(oldFlag, newFlag));
deptDO.setLevel(deptDO.getFlag().split("-").length - 1);
deptMapper.updateById(deptDO);
}
updateObj.setLevel(patentDeptDO.getLevel() + 1);
updateObj.setFlag(newFlag);
}
deptMapper.updateById(updateObj);
}
@ -218,4 +266,9 @@ public class DeptServiceImpl implements DeptService {
});
}
@Override
public List<DeptDO> getDeptByLeaderId(Long leaderUserId) {
return deptMapper.selectByLeaderId(leaderUserId);
}
}

View File

@ -200,7 +200,8 @@ public class PermissionServiceImpl implements PermissionService {
@Override
@DSTransactional // 多数据源使用 @DSTransactional 保证本地事务以及数据源的切换
@CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
@CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId"
,allEntries = true)
public void assignUserRole(Long userId, Set<Long> roleIds) {
// 获得角色拥有角色编号
Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId),