去掉冲突(意外的文件内容)

Signed-off-by: daixingdeng <2283268848@qq.com>
This commit is contained in:
daixingdeng 2025-02-28 07:25:45 +00:00 committed by Gitee
commit 7398394829
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
41 changed files with 513 additions and 123 deletions

View File

@ -14,12 +14,12 @@
<properties>
<revision>5.3.0</revision>
<spring-boot.version>3.4.2</spring-boot.version>
<spring-boot.version>3.4.3</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<mybatis.version>3.5.16</mybatis.version>
<springdoc.version>2.8.4</springdoc.version>
<springdoc.version>2.8.5</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<easyexcel.version>4.0.3</easyexcel.version>
<velocity.version>2.3</velocity.version>
@ -27,7 +27,7 @@
<mybatis-plus.version>3.5.10</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.35</hutool.version>
<spring-boot-admin.version>3.4.1</spring-boot-admin.version>
<spring-boot-admin.version>3.4.2</spring-boot-admin.version>
<redisson.version>3.44.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
@ -50,7 +50,7 @@
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250101</anyline.version>
<!--工作流配置-->
<warm-flow.version>1.6.6</warm-flow.version>
<warm-flow.version>1.6.7-M2</warm-flow.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>

View File

@ -1,9 +1,9 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.config.RuoYiConfig;
import org.dromara.common.core.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@ -17,16 +17,12 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
/**
* 系统基础配置
*/
private final RuoYiConfig ruoyiConfig;
/**
* 访问首页提示语
*/
@GetMapping("/")
public String index() {
return StringUtils.format("欢迎使用{}后台管理框架,当前版本v{}请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
}
}

View File

@ -120,8 +120,8 @@ redisson:
nettyThreads: 8
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 客户端名称 不能用中文
clientName: RuoYi-Vue-Plus
# 最小空闲连接数
connectionMinimumIdleSize: 8
# 连接池大小

View File

@ -123,8 +123,8 @@ redisson:
nettyThreads: 32
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 客户端名称 不能用中文
clientName: RuoYi-Vue-Plus
# 最小空闲连接数
connectionMinimumIdleSize: 32
# 连接池大小

View File

@ -1,24 +1,3 @@
# 项目相关配置
ruoyi:
# 名称
name: RuoYi-Vue-Plus
# 版本
version: ${revision}
# 版权年份
copyrightYear: 2024
captcha:
enable: true
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
@ -41,6 +20,18 @@ server:
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 256
captcha:
enable: true
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 日志配置
logging:
level:
@ -61,7 +52,7 @@ user:
# Spring配置
spring:
application:
name: ${ruoyi.name}
name: RuoYi-Vue-Plus
threads:
# 开启虚拟线程 仅jdk21可用
virtual:
@ -191,7 +182,7 @@ springdoc:
# persistAuthorization: true
info:
# 标题
title: '标题:${ruoyi.name}多租户管理系统_接口文档'
title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
@ -228,7 +219,6 @@ xss:
# 排除链接(多个用逗号分隔)
excludeUrls:
- /system/notice
- /warm-flow/save-xml
# 全局线程池相关配置
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
@ -281,3 +271,11 @@ warm-flow:
ui: true
# 默认Authorization如果有多个token用逗号分隔
token-name: ${sa-token.token-name},clientid
# 流程状态对应的三元色
chart-status-color:
## 未办理
- 157,255,0
## 待办理
- 0,0,0
## 已办理
- 255,200,0

View File

@ -1,6 +1,6 @@
package org.dromara.test;
import org.dromara.common.core.config.RuoYiConfig;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@ -17,19 +17,19 @@ import java.util.concurrent.TimeUnit;
public class DemoUnitTest {
@Autowired
private RuoYiConfig ruoYiConfig;
private CaptchaProperties captchaProperties;
@DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
@Test
public void testTest() {
System.out.println(ruoYiConfig);
System.out.println(captchaProperties);
}
@Disabled
@DisplayName("测试 @Disabled 注解")
@Test
public void testDisabled() {
System.out.println(ruoYiConfig);
System.out.println(captchaProperties);
}
@Timeout(value = 2L, unit = TimeUnit.SECONDS)
@ -37,7 +37,7 @@ public class DemoUnitTest {
@Test
public void testTimeout() throws InterruptedException {
Thread.sleep(3000);
System.out.println(ruoYiConfig);
System.out.println(captchaProperties);
}

View File

@ -1,33 +0,0 @@
package org.dromara.common.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author Lion Li
*/
@Data
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig {
/**
* 项目名称
*/
private String name;
/**
* 版本
*/
private String version;
/**
* 版权年份
*/
private String copyrightYear;
}

View File

@ -30,6 +30,11 @@ public interface CacheNames {
*/
String SYS_DICT = "sys_dict";
/**
* 数据字典类型
*/
String SYS_DICT_TYPE = "sys_dict_type";
/**
* 租户
*/

View File

@ -72,4 +72,9 @@ public interface SystemConstants {
*/
Long SUPER_ADMIN_ID = 1L;
/**
* 根部门祖级列表
*/
String ROOT_DEPT_ANCESTORS = "0";
}

View File

@ -11,7 +11,6 @@ import java.io.Serializable;
*
* @author AprilWind
*/
@Data
@NoArgsConstructor
public class DeptDTO implements Serializable {

View File

@ -0,0 +1,41 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 字典数据DTO
*
* @author AprilWind
*/
@Data
@NoArgsConstructor
public class DictDataDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 字典标签
*/
private String dictLabel;
/**
* 字典键值
*/
private String dictValue;
/**
* 是否默认Y是 N否
*/
private String isDefault;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,41 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 字典类型DTO
*
* @author AprilWind
*/
@Data
@NoArgsConstructor
public class DictTypeDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 字典主键
*/
private Long dictId;
/**
* 字典名称
*/
private String dictName;
/**
* 字典类型
*/
private String dictType;
/**
* 备注
*/
private String remark;
}

View File

@ -35,7 +35,7 @@ public class RoleDTO implements Serializable {
private String roleKey;
/**
* 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限6=部门及以下或本人数据权限
* 数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 5仅本人数据权限 6部门及以下或本人数据权限
*/
private String dataScope;

View File

@ -6,12 +6,12 @@ import java.io.Serial;
import java.io.Serializable;
/**
* 流程办理监听
* 流程创建任务监听
*
* @author may
*/
@Data
public class ProcessTaskEvent implements Serializable {
public class ProcessCreateTaskEvent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.DictDataDTO;
import org.dromara.common.core.domain.dto.DictTypeDTO;
import java.util.List;
import java.util.Map;
/**
@ -64,4 +68,20 @@ public interface DictService {
*/
Map<String, String> getAllDictByDictType(String dictType);
/**
* 根据字典类型查询详细信息
*
* @param dictType 字典类型
* @return 字典类型详细信息
*/
DictTypeDTO getDictTypeDto(String dictType);
/**
* 根据字典类型查询字典数据列表
*
* @param dictType 字典类型
* @return 字典数据列表
*/
List<DictDataDTO> getDictDataDto(String dictType);
}

View File

@ -1,6 +1,5 @@
org.dromara.common.core.config.ApplicationConfig
org.dromara.common.core.config.AsyncConfig
org.dromara.common.core.config.RuoYiConfig
org.dromara.common.core.config.ThreadPoolConfig
org.dromara.common.core.config.ValidatorConfig
org.dromara.common.core.utils.SpringUtils

View File

@ -76,10 +76,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
// 设置响应头
servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
servletResponse.setHeader(headerFlag, encryptPassword);
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
// 获取原始内容

View File

@ -78,6 +78,7 @@ public class CaffeineCacheDecorator implements Cache {
@Override
public void clear() {
CAFFEINE.invalidateAll();
cache.clear();
}

View File

@ -42,7 +42,7 @@ public class SysRole extends TenantEntity {
private Integer roleSort;
/**
* 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限6=部门及以下或本人数据权限
* 数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 5仅本人数据权限 6部门及以下或本人数据权限
*/
private String dataScope;

View File

@ -54,7 +54,7 @@ public class SysRoleVo implements Serializable {
* 数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 5仅本人数据权限 6部门及以下或本人数据权限
*/
@ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限,6=部门及以下或本人数据权限")
@ExcelDictFormat(readConverterExp = "1=全部数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限,6=部门及以下或本人数据权限")
private String dataScope;
/**

View File

@ -323,8 +323,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
dept.setAncestors(oldDept.getAncestors());
}
int result = baseMapper.updateById(dept);
if (SystemConstants.NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
&& !StringUtils.equals(SystemConstants.NORMAL, dept.getAncestors())) {
// 如果部门状态为启用且部门祖级列表不为空且部门祖级列表不等于根部门祖级列表如果部门祖级列表不等于根部门祖级列表则说明存在上级部门
if (SystemConstants.NORMAL.equals(dept.getStatus())
&& StringUtils.isNotEmpty(dept.getAncestors())
&& !StringUtils.equals(SystemConstants.ROOT_DEPT_ANCESTORS, dept.getAncestors())) {
// 如果该部门是启用状态则启用该部门的所有上级部门
updateParentDeptStatusNormal(dept);
}

View File

@ -1,5 +1,6 @@
package org.dromara.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -8,6 +9,8 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.dto.DictDataDTO;
import org.dromara.common.core.domain.dto.DictTypeDTO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
@ -121,6 +124,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
* @param dictType 字典类型
* @return 字典类型
*/
@Cacheable(cacheNames = CacheNames.SYS_DICT_TYPE, key = "#dictType")
@Override
public SysDictTypeVo selectDictTypeByType(String dictType) {
return baseMapper.selectVoOne(new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getDictType, dictType));
@ -140,6 +144,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
}
CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType());
CacheUtils.evict(CacheNames.SYS_DICT_TYPE, dictType.getDictType());
}
baseMapper.deleteByIds(Arrays.asList(dictIds));
}
@ -150,6 +155,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public void resetDictCache() {
CacheUtils.clear(CacheNames.SYS_DICT);
CacheUtils.clear(CacheNames.SYS_DICT_TYPE);
}
/**
@ -188,6 +194,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
int row = baseMapper.updateById(dict);
if (row > 0) {
CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType());
CacheUtils.evict(CacheNames.SYS_DICT_TYPE, oldDict.getDictType());
return dictDataMapper.selectDictDataByType(dict.getDictType());
}
throw new ServiceException("操作失败");
@ -249,10 +256,40 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
}
}
/**
* 获取字典下所有的字典值与标签
*
* @param dictType 字典类型
* @return dictValue为keydictLabel为值组成的Map
*/
@Override
public Map<String, String> getAllDictByDictType(String dictType) {
List<SysDictDataVo> list = selectDictDataByType(dictType);
List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
return StreamUtils.toMap(list, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
}
/**
* 根据字典类型查询详细信息
*
* @param dictType 字典类型
* @return 字典类型详细信息
*/
@Override
public DictTypeDTO getDictTypeDto(String dictType) {
SysDictTypeVo vo = SpringUtils.getAopProxy(this).selectDictTypeByType(dictType);
return BeanUtil.toBean(vo, DictTypeDTO.class);
}
/**
* 根据字典类型查询字典数据列表
*
* @param dictType 字典类型
* @return 字典数据列表
*/
@Override
public List<DictDataDTO> getDictDataDto(String dictType) {
List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
return BeanUtil.copyToList(list, DictDataDTO.class);
}
}

View File

@ -154,6 +154,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService {
public boolean checkPostNameUnique(SysPostBo post) {
boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysPost>()
.eq(SysPost::getPostName, post.getPostName())
.eq(SysPost::getDeptId, post.getDeptId())
.ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId()));
return !exist;
}

View File

@ -53,6 +53,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum());
QueryWrapper<SysRole> wrapper = Wrappers.query();
wrapper.eq("r.del_flag", SystemConstants.NORMAL)
.eq("r.status", SystemConstants.NORMAL)
.like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "r.role_name", taskQuery.getHandlerCode())
.like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "r.role_key", taskQuery.getHandlerName())
.between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()),
@ -75,6 +76,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
public TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery) {
PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum());
LambdaQueryWrapper<SysPost> wrapper = Wrappers.<SysPost>lambdaQuery()
.eq(SysPost::getStatus, SystemConstants.NORMAL)
.like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysPost::getPostCategory, taskQuery.getHandlerCode())
.like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysPost::getPostName, taskQuery.getHandlerName())
.between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()),
@ -106,6 +108,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum());
LambdaQueryWrapper<SysDept> wrapper = Wrappers.<SysDept>lambdaQuery()
.eq(SysDept::getDelFlag, SystemConstants.NORMAL)
.eq(SysDept::getStatus, SystemConstants.NORMAL)
.like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysDept::getDeptCategory, taskQuery.getHandlerCode())
.like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysDept::getDeptName, taskQuery.getHandlerName())
.between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()),
@ -143,6 +146,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum());
QueryWrapper<SysUser> wrapper = Wrappers.query();
wrapper.eq("u.del_flag", SystemConstants.NORMAL)
.eq("u.status", SystemConstants.NORMAL)
.like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "u.user_name", taskQuery.getHandlerCode())
.like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "u.nick_name", taskQuery.getHandlerName())
.between(taskQuery.getBeginTime() != null && taskQuery.getEndTime() != null,

View File

@ -179,10 +179,20 @@ public class SysTenantServiceImpl implements ISysTenantService {
for (SysDictType dictType : dictTypeList) {
dictType.setDictId(null);
dictType.setTenantId(tenantId);
dictType.setCreateDept(null);
dictType.setCreateBy(null);
dictType.setCreateTime(null);
dictType.setUpdateBy(null);
dictType.setUpdateTime(null);
}
for (SysDictData dictData : dictDataList) {
dictData.setDictCode(null);
dictData.setTenantId(tenantId);
dictData.setCreateDept(null);
dictData.setCreateBy(null);
dictData.setCreateTime(null);
dictData.setUpdateBy(null);
dictData.setUpdateTime(null);
}
dictTypeMapper.insertBatch(dictTypeList);
dictDataMapper.insertBatch(dictDataList);
@ -192,6 +202,11 @@ public class SysTenantServiceImpl implements ISysTenantService {
for (SysConfig config : sysConfigList) {
config.setConfigId(null);
config.setTenantId(tenantId);
config.setCreateDept(null);
config.setCreateBy(null);
config.setCreateTime(null);
config.setUpdateBy(null);
config.setUpdateTime(null);
}
configMapper.insertBatch(sysConfigList);
@ -433,6 +448,9 @@ public class SysTenantServiceImpl implements ISysTenantService {
data.setTenantId(tenantId);
data.setCreateTime(null);
data.setUpdateTime(null);
data.setCreateDept(null);
data.setCreateBy(null);
data.setUpdateBy(null);
set.add(tenantId);
saveDataList.add(data);
}
@ -454,6 +472,9 @@ public class SysTenantServiceImpl implements ISysTenantService {
data.setTenantId(tenantId);
data.setCreateTime(null);
data.setUpdateTime(null);
data.setCreateDept(null);
data.setCreateBy(null);
data.setUpdateBy(null);
set.add(tenantId);
saveDataList.add(data);
}

View File

@ -1,3 +0,0 @@
# 工作流说明
工作流目前在未成熟阶段 后续仍会经历重构 甚至重写(生产使用前请慎重考虑后续是否要更新维护)

View File

@ -0,0 +1,30 @@
package org.dromara.workflow.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 按钮权限枚举
*
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum ButtonPermissionEnum implements NodeExtEnum {
/**
* 是否弹窗选人
*/
APPROVE("是否弹窗选人", "1", false),
/**
* 是否能委托
*/
REJECT("是否能委托", "2", false);
private final String label;
private final String value;
private final boolean selected;
}

View File

@ -0,0 +1,26 @@
package org.dromara.workflow.common.enums;
/**
* 节点扩展属性枚举
*
* @author AprilWind
*/
public interface NodeExtEnum {
/**
* 选项label
*/
String getLabel();
/**
* 选项值
*/
String getValue();
/**
* 是否默认选中
*/
boolean isSelected();
}

View File

@ -1,9 +1,9 @@
package org.dromara.workflow.handler;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.event.ProcessCreateTaskEvent;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.ConditionalOnEnable;
@ -23,7 +23,7 @@ import java.util.Map;
public class FlowProcessEventHandler {
/**
* 总体流程监听(例如: 草稿撤销退回作废终止已完成)
* 总体流程监听(例如: 草稿撤销退回作废终止已完成单任务完成)
*
* @param flowCode 流程定义编码
* @param businessId 业务id
@ -44,23 +44,23 @@ public class FlowProcessEventHandler {
}
/**
* 执行办理任务监听
* 执行创建任务监听
*
* @param flowCode 流程定义编码
* @param nodeCode 审批节点编码
* @param taskId 任务id
* @param businessId 业务id
*/
public void processTaskHandler(String flowCode, String nodeCode, Long taskId, String businessId) {
public void processCreateTaskHandler(String flowCode, String nodeCode, Long taskId, String businessId) {
String tenantId = TenantHelper.getTenantId();
log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 节点编码: {}, 任务ID: {}, 业务ID: {}", tenantId, flowCode, nodeCode, taskId, businessId);
ProcessTaskEvent processTaskEvent = new ProcessTaskEvent();
processTaskEvent.setTenantId(tenantId);
processTaskEvent.setFlowCode(flowCode);
processTaskEvent.setNodeCode(nodeCode);
processTaskEvent.setTaskId(taskId);
processTaskEvent.setBusinessId(businessId);
SpringUtils.context().publishEvent(processTaskEvent);
ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent();
processCreateTaskEvent.setTenantId(tenantId);
processCreateTaskEvent.setFlowCode(flowCode);
processCreateTaskEvent.setNodeCode(nodeCode);
processCreateTaskEvent.setTaskId(taskId);
processCreateTaskEvent.setBusinessId(businessId);
SpringUtils.context().publishEvent(processCreateTaskEvent);
}
/**

View File

@ -52,7 +52,7 @@ public class WorkflowGlobalListener implements GlobalListener {
Task task = listenerVariable.getTask();
if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(flowStatus)) {
// 判断流程状态发布审批中事件
flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), task.getNodeCode(), task.getId(), businessId);
flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), task.getNodeCode(), task.getId(), businessId);
}
}

View File

@ -219,6 +219,11 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
.eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID).eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID));
flowCategory.setCategoryId(null);
flowCategory.setTenantId(tenantId);
flowCategory.setCreateDept(null);
flowCategory.setCreateBy(null);
flowCategory.setCreateTime(null);
flowCategory.setUpdateBy(null);
flowCategory.setUpdateTime(null);
flwCategoryMapper.insert(flowCategory);
List<Long> defIds = StreamUtils.toList(flowDefinitions, FlowDefinition::getId);
List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().in(FlowNode::getDefinitionId, defIds));

View File

@ -0,0 +1,190 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.DictTypeDTO;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.ui.service.NodeExtService;
import org.dromara.warm.flow.ui.vo.NodeExt;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.enums.ButtonPermissionEnum;
import org.dromara.workflow.common.enums.NodeExtEnum;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* 流程设计器-节点扩展属性
*
* @author AprilWind
*/
@ConditionalOnEnable
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwNodeExtServiceImpl implements NodeExtService {
/**
* 权限页code
*/
private static final String PERMISSION_TAB = "wf_button_tab";
/**
* 权限页名称
*/
private static final String PERMISSION_TAB_NAME = "权限";
/**
* 枚举类型标识
*/
private static final String ENUM_TYPE_PREFIX = "enum:";
/**
* 基础设置
*/
private static final int TYPE_BASE_SETTING = 1;
/**
* 新页签
*/
private static final int TYPE_NEW_TAB = 2;
/**
* 存储不同 dictType 对应的配置信息
*/
private static final Map<String, Map<String, Object>> CHILD_NODE_MAP = new HashMap<>();
static {
CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getName(),
Map.of("label", "权限按钮", "type", 4, "must", false, "multiple", true));
}
private final DictService dictService;
/**
* 获取节点扩展属性
*
* @return 结果
*/
@Override
public List<NodeExt> getNodeExt() {
List<NodeExt> nodeExtList = new ArrayList<>();
// 构建按钮权限页面
nodeExtList.add(buildNodeExt(PERMISSION_TAB, PERMISSION_TAB_NAME, TYPE_NEW_TAB,
ENUM_TYPE_PREFIX + ButtonPermissionEnum.class.getName()));
return nodeExtList;
}
/**
* 构建一个 NodeExt 对象
*
* @param code 编码此json中唯一
* @param name 名称如果type为新页签时作为页签名称
* @param type 节点类型1基础设置2新页签
* @param sourceTypes 字典/枚举类型来源逗号分隔
* @return 返回构建好的 NodeExt 对象
*/
private NodeExt buildNodeExt(String code, String name, int type, String sourceTypes) {
NodeExt nodeExt = new NodeExt();
nodeExt.setCode(code);
nodeExt.setType(type);
nodeExt.setName(name);
nodeExt.setChilds(StringUtils.splitList(sourceTypes)
.stream().map(this::buildChildNode)
.filter(ObjectUtil::isNotNull)
.toList()
);
return nodeExt;
}
/**
* 构建一个 ChildNode 对象
*
* @param sourceType 字典类型
* @return 返回构建好的 ChildNode 对象
*/
private NodeExt.ChildNode buildChildNode(String sourceType) {
return sourceType.startsWith(ENUM_TYPE_PREFIX) ?
buildChildNodeFromEnum(sourceType.substring(ENUM_TYPE_PREFIX.length())) : buildChildNodeFromDict(sourceType);
}
/**
* 根据枚举构建一个 ChildNode 对象
*
* @param enumClassName 枚举名称
* @return 返回构建好的 ChildNode 对象
*/
private NodeExt.ChildNode buildChildNodeFromEnum(String enumClassName) {
try {
Class<?> enumClass = Class.forName(enumClassName);
if (!enumClass.isEnum()) {
return null;
}
NodeExt.ChildNode childNode = buildChildNodeMap(enumClassName);
// 编码此json中唯
childNode.setCode(ENUM_TYPE_PREFIX + enumClassName);
// 字典下拉框和复选框时用到
childNode.setDict(Arrays.stream(enumClass.getEnumConstants())
.filter(NodeExtEnum.class::isInstance)
.map(NodeExtEnum.class::cast)
.map(x ->
new NodeExt.DictItem(x.getLabel(), x.getValue(), x.isSelected())
).toList());
return childNode;
} catch (ClassNotFoundException e) {
log.error("Enum class not found: {}", enumClassName, e);
}
return null;
}
/**
* 根据字典构建一个 ChildNode 对象
*
* @param dictType 字典类型
* @return 返回构建好的 ChildNode 对象
*/
private NodeExt.ChildNode buildChildNodeFromDict(String dictType) {
DictTypeDTO dictTypeDTO = dictService.getDictTypeDto(dictType);
if (ObjectUtil.isNull(dictTypeDTO)) {
return null;
}
NodeExt.ChildNode childNode = buildChildNodeMap(dictType);
// 编码此json中唯一
childNode.setCode(dictType);
// label名称
childNode.setLabel(dictTypeDTO.getDictName());
// 描述
childNode.setDesc(dictTypeDTO.getRemark());
// 字典下拉框和复选框时用到
childNode.setDict(dictService.getDictDataDto(dictType)
.stream().map(x ->
new NodeExt.DictItem(x.getDictLabel(), x.getDictValue(), Convert.toBool(x.getIsDefault(), false))
).toList());
return childNode;
}
/**
* 根据 CHILD_NODE_MAP 中的配置信息构建一个基本的 ChildNode 对象
* 该方法用于设置 ChildNode 的常规属性例如 labeltype是否必填是否多选等
*
* @param key CHILD_NODE_MAP key
* @return 返回构建好的 ChildNode 对象
*/
private NodeExt.ChildNode buildChildNodeMap(String key) {
NodeExt.ChildNode childNode = new NodeExt.ChildNode();
Map<String, Object> map = CHILD_NODE_MAP.get(key);
// label名称
childNode.setLabel((String) map.get("label"));
// 1输入框 2输入框 3下拉框 4选择框
childNode.setType(Convert.toInt(map.get("type"), 1));
// 是否必填
childNode.setMust(Convert.toBool(map.get("must"), false));
// 是否多选
childNode.setMultiple(Convert.toBool(map.get("multiple"), true));
return childNode;
}
}

View File

@ -9,9 +9,9 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.event.ProcessCreateTaskEvent;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.service.WorkflowService;
import org.dromara.common.core.utils.MapstructUtils;
@ -123,7 +123,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
}
/**
* 总体流程监听(例如: 草稿撤销退回作废终止已完成)
* 总体流程监听(例如: 草稿撤销退回作废终止已完成单任务完成)
* 正常使用只需#processEvent.flowCode=='leave1'
* 示例为了方便则使用startsWith匹配了全部示例key
*
@ -151,19 +151,19 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
}
/**
* 执行办理任务监听
* 示例也可通过 @EventListener(condition = "#processTaskEvent.flowCode=='leave1'")进行判断
* 执行任务创建监听
* 示例也可通过 @EventListener(condition = "#processCreateTaskEvent.flowCode=='leave1'")进行判断
* 在方法中判断流程节点key
* if ("xxx".equals(processTaskEvent.getNodeCode())) {
* if ("xxx".equals(processCreateTaskEvent.getNodeCode())) {
* //执行业务逻辑
* }
*
* @param processTaskEvent 参数
* @param processCreateTaskEvent 参数
*/
@EventListener(condition = "#processTaskEvent.flowCode.startsWith('leave')")
public void processTaskHandler(ProcessTaskEvent processTaskEvent) {
log.info("当前任务执行了{}", processTaskEvent.toString());
TestLeave testLeave = baseMapper.selectById(Long.valueOf(processTaskEvent.getBusinessId()));
@EventListener(condition = "#processCreateTaskEvent.flowCode.startsWith('leave')")
public void processCreateTaskHandler(ProcessCreateTaskEvent processCreateTaskEvent) {
log.info("当前任务创建了{}", processCreateTaskEvent.toString());
TestLeave testLeave = baseMapper.selectById(Long.valueOf(processCreateTaskEvent.getBusinessId()));
testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus());
baseMapper.updateById(testLeave);
}

View File

@ -31,7 +31,7 @@
d.version,
uu.processed_by,
uu.type
from flow_task as t
from flow_task t
left join flow_user uu on uu.associated = t.id
left join flow_definition d on t.definition_id = d.id
left join flow_instance i on t.instance_id = i.id

View File

@ -14,6 +14,8 @@ http {
keepalive_timeout 65;
# 限制body大小
client_max_body_size 100m;
# 开启静态资源压缩
gzip_static on;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '

View File

@ -75,7 +75,7 @@ create table sys_tenant (
tenant_id varchar2(20) not null,
contact_user_name varchar2(20) default '',
contact_phone varchar2(20) default '',
company_name varchar2(50) default '',
company_name varchar2(30) default '',
license_number varchar2(30) default '',
address varchar2(200) default '',
intro varchar2(200) default '',

View File

@ -75,7 +75,7 @@ create table if not exists sys_tenant
tenant_id varchar(20) not null,
contact_user_name varchar(20) default null::varchar,
contact_phone varchar(20) default null::varchar,
company_name varchar(50) default null::varchar,
company_name varchar(30) default null::varchar,
license_number varchar(30) default null::varchar,
address varchar(200) default null::varchar,
intro varchar(200) default null::varchar,

View File

@ -45,7 +45,7 @@ create table sys_tenant
tenant_id varchar(20) not null comment '租户编号',
contact_user_name varchar(20) comment '联系人',
contact_phone varchar(20) comment '联系电话',
company_name varchar(50) comment '企业名称',
company_name varchar(30) comment '企业名称',
license_number varchar(30) comment '统一社会信用代码',
address varchar(200) comment '地址',
intro varchar(200) comment '企业简介',

View File

@ -24,7 +24,7 @@ CREATE TABLE `flow_definition`
CREATE TABLE `flow_node`
(
`id` bigint unsigned NOT NULL COMMENT '主键id',
`id` bigint NOT NULL COMMENT '主键id',
`node_type` tinyint(1) NOT NULL COMMENT '节点类型0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关',
`definition_id` bigint NOT NULL COMMENT '流程定义id',
`node_code` varchar(100) NOT NULL COMMENT '流程节点编码',
@ -32,7 +32,6 @@ CREATE TABLE `flow_node`
`permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用逗号隔开)',
`node_ratio` decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值',
`coordinate` varchar(100) DEFAULT NULL COMMENT '坐标',
`skip_any_node` varchar(100) DEFAULT 'N' COMMENT '是否可以退回任意节点Y是 N否即将删除',
`any_node_skip` varchar(100) DEFAULT NULL COMMENT '任意结点跳转',
`listener_type` varchar(100) DEFAULT NULL COMMENT '监听器类型',
`listener_path` varchar(400) DEFAULT NULL COMMENT '监听器路径',
@ -43,6 +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 '扩展属性',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',
`tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id',
PRIMARY KEY (`id`) USING BTREE

View File

@ -222,7 +222,7 @@ CREATE TABLE sys_tenant
tenant_id nvarchar(20) NOT NULL,
contact_user_name nvarchar(20) NULL,
contact_phone nvarchar(20) NULL,
company_name nvarchar(50) NULL,
company_name nvarchar(30) NULL,
license_number nvarchar(30) NULL,
address nvarchar(200) NULL,
intro nvarchar(200) NULL,

View File

@ -0,0 +1,6 @@
ALTER TABLE `flow_node` DROP COLUMN `skip_any_node`;
ALTER TABLE `flow_node`
ADD COLUMN `ext` text NULL COMMENT '扩展属性' AFTER `update_time`;
INSERT INTO sys_dict_type VALUES (16, '000000', '按钮权限', 'wf_button_permission', 103, 1, sysdate(), NULL, NULL, '按钮权限列表');
INSERT INTO sys_dict_data VALUES (60, '000000', 1, '是否弹窗选人', '1', 'wf_button_permission', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL,'是否弹窗选人');