团队app配置增加绑定工作流

This commit is contained in:
dengqichen 2025-11-02 14:31:56 +08:00
parent 62dec088fd
commit d59d21a062
9 changed files with 55 additions and 129 deletions

View File

@ -72,4 +72,4 @@ public class PermissionApiController extends BaseController<Permission, Permissi
protected void exportData(HttpServletResponse response, List<PermissionDTO> data) { protected void exportData(HttpServletResponse response, List<PermissionDTO> data) {
// TODO: 实现导出功能 // TODO: 实现导出功能
} }
} }

View File

@ -63,8 +63,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
if (!roleIds.isEmpty()) { if (!roleIds.isEmpty()) {
permissionRepository.findByRoleIds(roleIds).stream() permissionRepository.findByRoleIds(roleIds).stream()
.map(permission -> new SimpleGrantedAuthority(permission.getCode())) .map(permission -> new SimpleGrantedAuthority(permission.getCode()))
.distinct() .distinct()
.forEach(authorities::add); .forEach(authorities::add);
} }

View File

@ -32,7 +32,6 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
* @param request 工作流启动请求 * @param request 工作流启动请求
* @param processInstance Flowable 流程实例 * @param processInstance Flowable 流程实例
* @param definition 工作流定义 * @param definition 工作流定义
* @param formDataId 表单数据ID可选
* @return 工作流实例 * @return 工作流实例
*/ */
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
@ -41,10 +40,9 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
@Mapping(target = "workflowDefinitionId", source = "definition.id") @Mapping(target = "workflowDefinitionId", source = "definition.id")
@Mapping(target = "businessKey", source = "request.businessKey") @Mapping(target = "businessKey", source = "request.businessKey")
@Mapping(target = "graphSnapshot", source = "definition.graph") @Mapping(target = "graphSnapshot", source = "definition.graph")
@Mapping(target = "formDataId", source = "formDataId") @Mapping(target = "variables", source = "request.variables")
@Mapping(target = "formData", ignore = true) // @AfterMapping 中处理
@Mapping(target = "status", constant = "NOT_STARTED") @Mapping(target = "status", constant = "NOT_STARTED")
@Mapping(target = "startTime", ignore = true) // @AfterMapping 中设置 @Mapping(target = "startTime", expression = "java(java.time.LocalDateTime.now())")
@Mapping(target = "endTime", ignore = true) @Mapping(target = "endTime", ignore = true)
@Mapping(target = "createTime", ignore = true) @Mapping(target = "createTime", ignore = true)
@Mapping(target = "updateTime", ignore = true) @Mapping(target = "updateTime", ignore = true)
@ -55,25 +53,7 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
WorkflowInstance toEntity( WorkflowInstance toEntity(
WorkflowInstanceStartRequest request, WorkflowInstanceStartRequest request,
ProcessInstance processInstance, ProcessInstance processInstance,
WorkflowDefinition definition, WorkflowDefinition definition
Long formDataId
); );
/**
* 后处理设置 formData startTime
*/
@AfterMapping
default void afterMapping(
WorkflowInstanceStartRequest request,
Long formDataId,
@MappingTarget WorkflowInstance workflowInstance
) {
// 只有在没有 FormData ID 时才保存 JSON
if (formDataId == null && request.getFormData() != null) {
workflowInstance.setFormData(request.getFormData());
}
// 设置开始时间
workflowInstance.setStartTime(LocalDateTime.now());
}
} }

View File

@ -30,9 +30,9 @@ public class WorkflowInstanceDTO extends BaseDTO {
private String businessKey; private String businessKey;
/** /**
* 启动表单数据ID * 工作流定义ID
*/ */
private Long formDataId; private Long workflowDefinitionId;
/** /**
* 实例状态 * 实例状态
@ -40,10 +40,10 @@ public class WorkflowInstanceDTO extends BaseDTO {
private WorkflowInstanceStatusEnums status; private WorkflowInstanceStatusEnums status;
/** /**
* 表单数据(JSON) * 流程变量
* 存储流程启动时传入的表单变量 * 存储所有业务数据
*/ */
private Map<String, Object> formData; private Map<String, Object> variables;
/** /**
* 开始时间 * 开始时间

View File

@ -1,24 +1,29 @@
package com.qqchen.deploy.backend.workflow.dto; package com.qqchen.deploy.backend.workflow.dto;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.util.Map; import java.util.Map;
/**
* 工作流实例启动请求
*
* @author qqchen
* @since 2025-11-02
*/
@Data @Data
@Schema(description = "工作流实例启动入参") @Schema(description = "工作流实例启动请求")
public class WorkflowInstanceStartRequest { public class WorkflowInstanceStartRequest {
@Schema(description = "流程定义标识", example = "APPROVAL-AND-DEPLOYMENT") @Schema(description = "流程定义标识(必填)", required = true, example = "standard-deploy")
@NotBlank(message = "流程定义标识不能为空")
private String processKey; private String processKey;
@Schema(description = "业务键", example = "DEPLOY-2023-001") @Schema(description = "业务标识(必填)", required = true, example = "DEPLOY-user-service-20251102143000")
@NotBlank(message = "业务标识不能为空")
private String businessKey; private String businessKey;
@Schema(description = "表单标识(可选,如果有则创建 FormData 记录)", example = "APPROVAL-AND-DEPLOYMENT") @Schema(description = "流程变量(可选)", example = "{\"applicationId\":1,\"environmentId\":2,\"deployJob\":\"user-service-prod\"}")
private String formKey; private Map<String, Object> variables;
@Schema(description = "表单数据")
private Map<String, Object> formData;
} }

View File

@ -46,12 +46,6 @@ public class WorkflowInstance extends Entity<Long> {
@Column(name = "business_key") @Column(name = "business_key")
private String businessKey; private String businessKey;
/**
* 启动表单数据ID外键关联form_data
*/
@Column(name = "form_data_id")
private Long formDataId;
/** /**
* 实例状态 * 实例状态
*/ */
@ -60,12 +54,12 @@ public class WorkflowInstance extends Entity<Long> {
private WorkflowInstanceStatusEnums status; private WorkflowInstanceStatusEnums status;
/** /**
* 表单数据(JSON) * 流程变量
* 存储流程启动时传入的表单变量 * 存储所有业务数据如部署参数表单数据等
*/ */
@Type(JsonType.class) @Type(JsonType.class)
@Column(name = "form_data", columnDefinition = "json") @Column(name = "variables", columnDefinition = "TEXT")
private Map<String, Object> formData; private Map<String, Object> variables;
/** /**
* 流程图数据快照启动时保存用于画布还原 * 流程图数据快照启动时保存用于画布还原

View File

@ -19,16 +19,6 @@ import java.util.Map;
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, WorkflowInstanceQuery, Long> { public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, WorkflowInstanceQuery, Long> {
/**
* 创建工作流实例并关联Flowable实例
*
* @param request 工作流实例启动请求
* @param processInstance Flowable流程实例
* @param formDataId 表单数据ID可选如果有则关联
* @return 工作流实例
*/
WorkflowInstanceDTO createWorkflowInstance(WorkflowInstanceStartRequest request, ProcessInstance processInstance, Long formDataId);
/** /**
* 更新工作流实例状态 * 更新工作流实例状态
* *

View File

@ -86,28 +86,6 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
@Resource @Resource
private WorkflowNodeInstanceConverter workflowNodeInstanceConverter; private WorkflowNodeInstanceConverter workflowNodeInstanceConverter;
@Override
public WorkflowInstanceDTO createWorkflowInstance(WorkflowInstanceStartRequest request, ProcessInstance processInstance, Long formDataId) {
// 1. 查询流程定义获取 graph 快照
WorkflowDefinition definition = workflowDefinitionRepository.findByKey(request.getProcessKey())
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + request.getProcessKey()));
// 2. 使用 MapStruct Converter 创建实例处理多源对象映射
WorkflowInstance workflowInstance = workflowInstanceConverter.toEntity(
request,
processInstance,
definition,
formDataId
);
workflowInstanceRepository.save(workflowInstance);
log.info("创建工作流实例: instanceId={}, definitionId={}, formDataId={}, graph已保存",
workflowInstance.getId(), definition.getId(), formDataId);
// 3. 返回创建结果
return workflowInstanceConverter.toDto(workflowInstance);
}
@Override @Override
public WorkflowInstance updateInstanceStatus(String processInstanceId, WorkflowInstanceStatusEnums status, LocalDateTime endTime) { public WorkflowInstance updateInstanceStatus(String processInstanceId, WorkflowInstanceStatusEnums status, LocalDateTime endTime) {
WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId) WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
@ -190,59 +168,43 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
@Transactional @Transactional
public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) { public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) {
try { try {
// 步骤1: 先创建 FormData如果需要 // 1. 查询流程定义
FormDataDTO formData = null; WorkflowDefinition definition = workflowDefinitionRepository.findByKey(request.getProcessKey())
if (request.getFormKey() != null && request.getFormData() != null && !request.getFormData().isEmpty()) { .orElseThrow(() -> new RuntimeException("Workflow definition not found: " + request.getProcessKey()));
FormDataCreateFromWorkflowRequest formDataRequest = new FormDataCreateFromWorkflowRequest();
formDataRequest.setFormKey(request.getFormKey());
formDataRequest.setFormData(request.getFormData());
formDataRequest.setBusinessKey(request.getBusinessKey());
formData = formDataService.createFromWorkflowStart(formDataRequest);
log.info("FormData 已创建: id={}", formData.getId());
} else {
log.info("FormData 创建: 跳过(无 formKey 或表单数据)");
}
// 步骤2: 构建 Flowable 变量 // 2. 获取流程变量如果没有则使用空 Map
Map<String, Object> variables = buildFlowableVariables(request); Map<String, Object> variables = request.getVariables() != null ? request.getVariables() : new HashMap<>();
// 步骤3: 启动 Flowable 流程 // 3. 启动 Flowable 流程
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey(request.getProcessKey()) .processDefinitionKey(request.getProcessKey())
.variables(variables) .variables(variables)
.businessKey(request.getBusinessKey()) .businessKey(request.getBusinessKey())
.startAsync(); // 异步启动会自动执行 shell 任务 .startAsync(); // 异步启动会自动执行任务
log.info("Flowable 流程已启动: processInstanceId={}", processInstance.getId()); log.info("Flowable 流程已启动: processInstanceId={}, businessKey={}",
processInstance.getId(), request.getBusinessKey());
// 步骤4: 创建 WorkflowInstance 记录关联 FormData // 4. 创建 WorkflowInstance 记录
return createWorkflowInstance(request, processInstance, formData != null ? formData.getId() : null); WorkflowInstance workflowInstance = workflowInstanceConverter.toEntity(
request,
processInstance,
definition
);
workflowInstanceRepository.save(workflowInstance);
log.info("工作流实例已创建: instanceId={}, definitionId={}, businessKey={}",
workflowInstance.getId(), definition.getId(), request.getBusinessKey());
// 5. 返回创建结果
return workflowInstanceConverter.toDto(workflowInstance);
} catch (Exception e) { } catch (Exception e) {
log.error("启动工作流失败: processKey={}", request.getProcessKey(), e); log.error("启动工作流失败: processKey={}, businessKey={}",
request.getProcessKey(), request.getBusinessKey(), e);
throw new RuntimeException("启动工作流失败: " + e.getMessage(), e); throw new RuntimeException("启动工作流失败: " + e.getMessage(), e);
} }
} }
/**
* 构建 Flowable 流程变量
*
* @param request 工作流启动请求
* @return Flowable 变量 Map
*/
private Map<String, Object> buildFlowableVariables(WorkflowInstanceStartRequest request) {
Map<String, Object> variables = new HashMap<>();
// 将前端传入的 formData 包装到 "form" 变量中
// 这样流程中的 ${form.xxx} 表达式就能正确解析
Map<String, Object> formData = request.getFormData() != null ? request.getFormData() : new HashMap<>();
if (!formData.isEmpty()) {
variables.put("form", formData);
}
return variables;
}
@Override @Override
public Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query) { public Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query) {
Pageable pageable = PageRequest.of(query.getPageNum(), query.getPageSize()); Pageable pageable = PageRequest.of(query.getPageNum(), query.getPageSize());

View File

@ -623,16 +623,11 @@ CREATE TABLE workflow_instance
process_definition_id VARCHAR(64) NOT NULL COMMENT '流程定义ID', process_definition_id VARCHAR(64) NOT NULL COMMENT '流程定义ID',
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
business_key VARCHAR(64) NULL COMMENT '业务标识', business_key VARCHAR(64) NULL COMMENT '业务标识',
form_data_id BIGINT NULL COMMENT '启动表单数据ID外键关联form_data',
status ENUM('NOT_STARTED','CREATED','RUNNING','SUSPENDED','COMPLETED','COMPLETED_WITH_ERRORS','TERMINATED','FAILED') NOT NULL COMMENT '实例状态', status ENUM('NOT_STARTED','CREATED','RUNNING','SUSPENDED','COMPLETED','COMPLETED_WITH_ERRORS','TERMINATED','FAILED') NOT NULL COMMENT '实例状态',
variables TEXT NULL COMMENT '流程变量(JSON)', variables TEXT NULL COMMENT '流程变量(JSON包含所有业务数据)',
form_data JSON NULL COMMENT '表单数据(JSON)',
graph_snapshot JSON NULL COMMENT '流程图数据快照(启动时保存,用于画布还原)', graph_snapshot JSON NULL COMMENT '流程图数据快照(启动时保存,用于画布还原)',
start_time DATETIME(6) NULL COMMENT '开始时间', start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间', end_time DATETIME(6) NULL COMMENT '结束时间'
INDEX idx_form_data_id (form_data_id),
CONSTRAINT fk_workflow_instance_form_data FOREIGN KEY (form_data_id) REFERENCES form_data (id)
-- CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (process_definition_id) REFERENCES workflow_definition(id) -- CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (process_definition_id) REFERENCES workflow_definition(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';