feat(bpm): 添加流程映射配置功能

- 新增流程映射配置表和相关 SQL
- 实现流程映射配置的 CRUD 接口和服务
- 添加流程数据动态查询功能
- 采用二级缓存架构(内存 + Redis)提高查询性能
This commit is contained in:
aikai 2025-07-01 14:09:50 +08:00
parent 64baec0471
commit e09b8c6b9d
14 changed files with 729 additions and 0 deletions

View File

@ -0,0 +1,26 @@
-- 流程映射配置表
CREATE TABLE `bpm_process_mapping_config` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`process_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程唯一标识(如:ORDER,PAYMENT)',
`process_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程显示名称',
`table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '物理表名(如:order_info)',
`entity_class` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实体类全限定名(如:com.example.order.OrderDO)',
`mapper_class` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Mapper接口全限定名(如:com.example.order.OrderMapper)',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '流程描述',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_process_code` (`process_code`, `deleted`) COMMENT '流程代码唯一索引',
KEY `idx_tenant_id` (`tenant_id`) COMMENT '租户ID索引'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='流程映射配置表';
-- 插入示例数据
INSERT INTO `bpm_process_mapping_config`
(`process_code`, `process_name`, `table_name`, `entity_class`, `mapper_class`, `description`, `creator`, `tenant_id`)
VALUES
('LEAVE', '请假流程', 'bpm_oa_leave', 'cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO', 'cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper', '员工请假申请流程配置', 'system', 0),
('REIMBURSEMENT', '报销流程', 'bpm_oa_reimbursement', 'cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO', 'cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReimbursementMapper', '员工报销申请流程配置', 'system', 0);

View File

@ -129,4 +129,8 @@ public interface ErrorCodeConstants {
ErrorCode BPM_SYSTEM_BUG = new ErrorCode(1_009_012_001, "系统问题,请联系管理员");
// ========== 流程映射配置模块 1-009-014-000 ==========
ErrorCode PROCESS_MAPPING_CONFIG_NOT_EXISTS = new ErrorCode(1_009_014_001, "流程映射配置不存在");
ErrorCode PROCESS_MAPPING_CONFIG_CODE_DUPLICATE = new ErrorCode(1_009_014_002, "流程代码已存在,不允许重复");
}

View File

@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigUpdateReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import cn.iocoder.yudao.module.bpm.service.processmapping.ProcessMappingConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 流程映射配置
*
* @author 系统
*/
@Tag(name = "管理后台 - 流程映射配置")
@RestController
@RequestMapping("/bpm/process-mapping-config")
@Validated
@Slf4j
public class BpmProcessMappingConfigController {
@Resource
private ProcessMappingConfigService processMappingConfigService;
@PostMapping("/create")
@Operation(summary = "创建流程映射配置")
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:create')")
public CommonResult<Long> createProcessMappingConfig(@Valid @RequestBody BpmProcessMappingConfigCreateReqVO createReqVO) {
return success(processMappingConfigService.createProcessMappingConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新流程映射配置")
@PreAuthorize("@ss.hasPermission('bmp:process-mapping-config:update')")
public CommonResult<Boolean> updateProcessMappingConfig(@Valid @RequestBody BpmProcessMappingConfigUpdateReqVO updateReqVO) {
processMappingConfigService.updateProcessMappingConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除流程映射配置")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:delete')")
public CommonResult<Boolean> deleteProcessMappingConfig(@RequestParam("id") Long id) {
processMappingConfigService.deleteProcessMappingConfig(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得流程映射配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:query')")
public CommonResult<BpmProcessMappingConfigRespVO> getProcessMappingConfig(@RequestParam("id") Long id) {
BpmProcessMappingConfigDO config = processMappingConfigService.getProcessMappingConfig(id);
return success(BeanUtils.toBean(config, BpmProcessMappingConfigRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得流程映射配置分页")
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:query')")
public CommonResult<PageResult<BpmProcessMappingConfigRespVO>> getProcessMappingConfigPage(@Valid BpmProcessMappingConfigPageReqVO pageReqVO) {
PageResult<BpmProcessMappingConfigDO> pageResult = processMappingConfigService.getProcessMappingConfigPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, BpmProcessMappingConfigRespVO.class));
}
@PostMapping("/refresh-cache")
@Operation(summary = "刷新流程映射配置缓存")
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:update')")
public CommonResult<Boolean> refreshCache() {
processMappingConfigService.refreshCache();
return success(true);
}
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.service.processmapping.ProcessQueryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.Serializable;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 流程数据查询
*
* @author 系统
*/
@Tag(name = "管理后台 - 流程数据查询")
@RestController
@RequestMapping("/bpm/process-query")
@Validated
@Slf4j
public class BpmProcessQueryController {
@Resource
private ProcessQueryService processQueryService;
@GetMapping("/data")
@Operation(summary = "根据流程代码和ID查询数据")
@Parameter(name = "processCode", description = "流程代码", required = true, example = "ORDER")
@Parameter(name = "id", description = "数据ID", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:process-query:data')")
public CommonResult<Object> getProcessData(@RequestParam("processCode") String processCode,
@RequestParam("id") Serializable id) {
Object result = processQueryService.queryById(processCode, id);
return success(result);
}
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* 流程映射配置创建 Request VO
*
* @author 系统
*/
@Schema(description = "管理后台 - 流程映射配置创建 Request VO")
@Data
public class BpmProcessMappingConfigCreateReqVO {
@Schema(description = "流程唯一标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "ORDER")
@NotBlank(message = "流程代码不能为空")
@Size(max = 64, message = "流程代码长度不能超过64个字符")
private String processCode;
@Schema(description = "流程显示名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单流程")
@NotBlank(message = "流程名称不能为空")
@Size(max = 128, message = "流程名称长度不能超过128个字符")
private String processName;
@Schema(description = "物理表名", requiredMode = Schema.RequiredMode.REQUIRED, example = "order_info")
@NotBlank(message = "表名不能为空")
@Size(max = 64, message = "表名长度不能超过64个字符")
private String tableName;
@Schema(description = "实体类全限定名", requiredMode = Schema.RequiredMode.REQUIRED, example = "com.example.order.OrderDO")
@NotBlank(message = "实体类不能为空")
@Size(max = 255, message = "实体类长度不能超过255个字符")
private String entityClass;
@Schema(description = "Mapper接口全限定名", requiredMode = Schema.RequiredMode.REQUIRED, example = "com.example.order.OrderMapper")
@NotBlank(message = "Mapper类不能为空")
@Size(max = 255, message = "Mapper类长度不能超过255个字符")
private String mapperClass;
@Schema(description = "流程描述", example = "订单相关的业务流程")
@Size(max = 255, message = "描述长度不能超过255个字符")
private String description;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 流程映射配置分页 Request VO
*
* @author 系统
*/
@Schema(description = "管理后台 - 流程映射配置分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessMappingConfigPageReqVO extends PageParam {
@Schema(description = "流程代码", example = "ORDER")
private String processCode;
@Schema(description = "流程名称", example = "订单流程")
private String processName;
@Schema(description = "表名", example = "order_info")
private String tableName;
@Schema(description = "实体类", example = "com.example.order.OrderDO")
private String entityClass;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 流程映射配置 Response VO
*
* @author 系统
*/
@Schema(description = "管理后台 - 流程映射配置 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessMappingConfigRespVO extends BpmProcessMappingConfigCreateReqVO {
@Schema(description = "配置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
/**
* 流程映射配置更新 Request VO
*
* @author 系统
*/
@Schema(description = "管理后台 - 流程映射配置更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessMappingConfigUpdateReqVO extends BpmProcessMappingConfigCreateReqVO {
@Schema(description = "配置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "配置ID不能为空")
private Long id;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.Map;
/**
* 流程数据查询 Request VO
*
* @author 系统
*/
@Schema(description = "管理后台 - 流程数据查询 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProcessDataQueryReqVO extends PageParam {
@Schema(description = "流程代码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ORDER")
@NotBlank(message = "流程代码不能为空")
private String processCode;
@Schema(description = "数据ID", example = "1024")
private Serializable id;
@Schema(description = "查询条件", example = "{\"status\": 1, \"name\": \"测试\"}")
private Map<String, Object> queryParams;
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.processmapping;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 流程映射配置 Mapper
*
* @author 系统
*/
@Mapper
public interface BpmProcessMappingConfigMapper extends BaseMapperX<BpmProcessMappingConfigDO> {
default PageResult<BpmProcessMappingConfigDO> selectPage(BpmProcessMappingConfigPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessMappingConfigDO>()
.likeIfPresent(BpmProcessMappingConfigDO::getProcessCode, reqVO.getProcessCode())
.likeIfPresent(BpmProcessMappingConfigDO::getProcessName, reqVO.getProcessName())
.likeIfPresent(BpmProcessMappingConfigDO::getTableName, reqVO.getTableName())
.likeIfPresent(BpmProcessMappingConfigDO::getEntityClass, reqVO.getEntityClass())
.betweenIfPresent(BpmProcessMappingConfigDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmProcessMappingConfigDO::getId));
}
default BpmProcessMappingConfigDO selectByProcessCode(String processCode) {
return selectOne(BpmProcessMappingConfigDO::getProcessCode, processCode);
}
}

View File

@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.bpm.service.processmapping;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigUpdateReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import javax.validation.Valid;
/**
* 流程映射配置 Service 接口
*
* @author 系统
*/
public interface ProcessMappingConfigService {
/**
* 创建流程映射配置
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createProcessMappingConfig(@Valid BpmProcessMappingConfigCreateReqVO createReqVO);
/**
* 更新流程映射配置
*
* @param updateReqVO 更新信息
*/
void updateProcessMappingConfig(@Valid BpmProcessMappingConfigUpdateReqVO updateReqVO);
/**
* 删除流程映射配置
*
* @param id 编号
*/
void deleteProcessMappingConfig(Long id);
/**
* 获得流程映射配置
*
* @param id 编号
* @return 流程映射配置
*/
BpmProcessMappingConfigDO getProcessMappingConfig(Long id);
/**
* 根据流程代码获取配置
*
* @param processCode 流程代码
* @return 流程映射配置
*/
BpmProcessMappingConfigDO getProcessMappingConfigByCode(String processCode);
/**
* 获得流程映射配置分页
*
* @param pageReqVO 分页查询
* @return 流程映射配置分页
*/
PageResult<BpmProcessMappingConfigDO> getProcessMappingConfigPage(BpmProcessMappingConfigPageReqVO pageReqVO);
/**
* 刷新缓存
*/
void refreshCache();
}

View File

@ -0,0 +1,200 @@
package cn.iocoder.yudao.module.bpm.service.processmapping;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.processmapping.vo.BpmProcessMappingConfigUpdateReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.processmapping.BpmProcessMappingConfigMapper;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.Duration;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_MAPPING_CONFIG_NOT_EXISTS;
/**
* 流程映射配置 Service 实现类
*
* 使用二级缓存架构
* 1. 一级缓存内存Google Guava LoadingCache10分钟过期
* 2. 二级缓存RedisSpring Cache1小时过期
* 3. 数据库MySQL
*
* @author 系统
*/
@Service
@Validated
@Slf4j
public class ProcessMappingConfigServiceImpl implements ProcessMappingConfigService {
private static final String CACHE_KEY_PREFIX = "process_mapping_config";
@Resource
private BpmProcessMappingConfigMapper processMappingConfigMapper;
/**
* 一级缓存内存- Google Guava LoadingCache
* 缓存key为processCode缓存时间10分钟
*/
@Getter
private final LoadingCache<String, BpmProcessMappingConfigDO> localCache = CacheUtils.buildAsyncReloadingCache(
Duration.ofMinutes(10L),
new CacheLoader<String, BpmProcessMappingConfigDO>() {
@Override
public BpmProcessMappingConfigDO load(String processCode) {
log.info("[load][一级缓存miss从二级缓存加载] processCode={}", processCode);
// 从二级缓存Redis加载
BpmProcessMappingConfigDO config = getFromRedisCache(processCode);
return config != null ? config : new BpmProcessMappingConfigDO(); // 避免缓存穿透
}
});
@Override
@Transactional(rollbackFor = Exception.class)
public Long createProcessMappingConfig(BpmProcessMappingConfigCreateReqVO createReqVO) {
// 校验processCode唯一性
validateProcessCodeUnique(null, createReqVO.getProcessCode());
// 插入
BpmProcessMappingConfigDO config = BeanUtil.toBean(createReqVO, BpmProcessMappingConfigDO.class);
processMappingConfigMapper.insert(config);
// 清除缓存
evictCache(createReqVO.getProcessCode());
return config.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessMappingConfig(BpmProcessMappingConfigUpdateReqVO updateReqVO) {
// 校验存在
BpmProcessMappingConfigDO oldConfig = validateProcessMappingConfigExists(updateReqVO.getId());
// 校验processCode唯一性
if (!oldConfig.getProcessCode().equals(updateReqVO.getProcessCode())) {
validateProcessCodeUnique(updateReqVO.getId(), updateReqVO.getProcessCode());
}
// 更新
BpmProcessMappingConfigDO updateObj = BeanUtil.toBean(updateReqVO, BpmProcessMappingConfigDO.class);
processMappingConfigMapper.updateById(updateObj);
// 清除缓存
evictCache(oldConfig.getProcessCode());
if (!oldConfig.getProcessCode().equals(updateReqVO.getProcessCode())) {
evictCache(updateReqVO.getProcessCode());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteProcessMappingConfig(Long id) {
// 校验存在
BpmProcessMappingConfigDO config = validateProcessMappingConfigExists(id);
// 删除
processMappingConfigMapper.deleteById(id);
// 清除缓存
evictCache(config.getProcessCode());
}
@Override
public BpmProcessMappingConfigDO getProcessMappingConfig(Long id) {
return processMappingConfigMapper.selectById(id);
}
@Override
public BpmProcessMappingConfigDO getProcessMappingConfigByCode(String processCode) {
try {
// 先从一级缓存内存获取
BpmProcessMappingConfigDO config = localCache.get(processCode);
if (config != null && config.getId() != null) {
log.debug("[getProcessMappingConfigByCode][一级缓存命中] processCode={}", processCode);
return config;
}
return null;
} catch (Exception e) {
log.error("[getProcessMappingConfigByCode][缓存异常] processCode={}", processCode, e);
// 缓存异常时直接查询数据库
return processMappingConfigMapper.selectByProcessCode(processCode);
}
}
/**
* 从二级缓存Redis获取配置
* 使用Spring Cache注解自动管理Redis缓存
*/
@Cacheable(value = CACHE_KEY_PREFIX + "#1h", key = "#processCode", unless = "#result == null")
public BpmProcessMappingConfigDO getFromRedisCache(String processCode) {
log.info("[getFromRedisCache][二级缓存miss从数据库加载] processCode={}", processCode);
// 从数据库查询
return processMappingConfigMapper.selectByProcessCode(processCode);
}
@Override
public PageResult<BpmProcessMappingConfigDO> getProcessMappingConfigPage(BpmProcessMappingConfigPageReqVO pageReqVO) {
return processMappingConfigMapper.selectPage(pageReqVO);
}
@Override
public void refreshCache() {
// 清空一级缓存
localCache.invalidateAll();
log.info("[refreshCache][已清空一级缓存]");
// 清空二级缓存通过Spring Cache管理
// 注意这里需要具体实现清空所有Redis缓存的逻辑
}
/**
* 清除指定processCode的缓存
*/
@CacheEvict(value = CACHE_KEY_PREFIX + "#1h", key = "#processCode")
public void evictCache(String processCode) {
// 清除一级缓存
localCache.invalidate(processCode);
log.info("[evictCache][已清除缓存] processCode={}", processCode);
// 清除二级缓存由@CacheEvict注解自动处理
}
private void validateProcessCodeUnique(Long id, String processCode) {
BpmProcessMappingConfigDO config = processMappingConfigMapper.selectByProcessCode(processCode);
if (config == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的配置
if (id == null) {
throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
}
if (!config.getId().equals(id)) {
throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
}
}
private BpmProcessMappingConfigDO validateProcessMappingConfigExists(Long id) {
if (id == null) {
return null;
}
BpmProcessMappingConfigDO config = processMappingConfigMapper.selectById(id);
if (config == null) {
throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
}
return config;
}
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.service.processmapping;
import java.io.Serializable;
/**
* 流程动态查询 Service 接口
*
* @author 系统
*/
public interface ProcessQueryService {
/**
* 根据流程代码和ID查询数据
*
* @param processCode 流程标识("ORDER")
* @param id 数据主键
* @return 对应的实体对象
*/
Object queryById(String processCode, Serializable id);
}

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.bpm.service.processmapping;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import cn.iocoder.yudao.module.bpm.framework.core.util.ReflectionInvoker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.io.Serializable;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_MAPPING_CONFIG_NOT_EXISTS;
/**
* 流程动态查询 Service 实现类
*
* @author 系统
*/
@Service
@Validated
@Slf4j
public class ProcessQueryServiceImpl implements ProcessQueryService {
@Resource
private ProcessMappingConfigService processMappingConfigService;
@Override
public Object queryById(String processCode, Serializable id) {
// 1. 获取流程配置
BpmProcessMappingConfigDO config = getProcessMappingConfig(processCode);
// 2. 动态获取Mapper实例
Object mapperBean = getMapperBean(config.getMapperClass());
// 3. 调用selectById方法
return ReflectionInvoker.invokeMapperMethod(mapperBean, "selectById", Serializable.class, id);
}
/**
* 获取流程映射配置
*/
private BpmProcessMappingConfigDO getProcessMappingConfig(String processCode) {
BpmProcessMappingConfigDO config = processMappingConfigService.getProcessMappingConfigByCode(processCode);
if (config == null) {
throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
}
return config;
}
/**
* 动态获取Mapper Bean实例
*/
private Object getMapperBean(String mapperClassName) {
try {
Class<?> mapperClass = ClassUtil.loadClass(mapperClassName);
return SpringUtil.getBean(mapperClass);
} catch (Exception e) {
log.error("[getMapperBean][获取Mapper实例失败] mapperClassName={}", mapperClassName, e);
throw new ServiceException(500, String.format("获取Mapper实例失败: %s", e.getMessage()));
}
}
}