团队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

@ -32,7 +32,6 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
* @param request 工作流启动请求
* @param processInstance Flowable 流程实例
* @param definition 工作流定义
* @param formDataId 表单数据ID可选
* @return 工作流实例
*/
@Mapping(target = "id", ignore = true)
@ -41,10 +40,9 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
@Mapping(target = "workflowDefinitionId", source = "definition.id")
@Mapping(target = "businessKey", source = "request.businessKey")
@Mapping(target = "graphSnapshot", source = "definition.graph")
@Mapping(target = "formDataId", source = "formDataId")
@Mapping(target = "formData", ignore = true) // @AfterMapping 中处理
@Mapping(target = "variables", source = "request.variables")
@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 = "createTime", ignore = true)
@Mapping(target = "updateTime", ignore = true)
@ -55,25 +53,7 @@ public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstanc
WorkflowInstance toEntity(
WorkflowInstanceStartRequest request,
ProcessInstance processInstance,
WorkflowDefinition definition,
Long formDataId
WorkflowDefinition definition
);
/**
* 后处理设置 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;
/**
* 启动表单数据ID
* 工作流定义ID
*/
private Long formDataId;
private Long workflowDefinitionId;
/**
* 实例状态
@ -40,10 +40,10 @@ public class WorkflowInstanceDTO extends BaseDTO {
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;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.Map;
/**
* 工作流实例启动请求
*
* @author qqchen
* @since 2025-11-02
*/
@Data
@Schema(description = "工作流实例启动入参")
@Schema(description = "工作流实例启动请求")
public class WorkflowInstanceStartRequest {
@Schema(description = "流程定义标识", example = "APPROVAL-AND-DEPLOYMENT")
@Schema(description = "流程定义标识(必填)", required = true, example = "standard-deploy")
@NotBlank(message = "流程定义标识不能为空")
private String processKey;
@Schema(description = "业务键", example = "DEPLOY-2023-001")
@Schema(description = "业务标识(必填)", required = true, example = "DEPLOY-user-service-20251102143000")
@NotBlank(message = "业务标识不能为空")
private String businessKey;
@Schema(description = "表单标识(可选,如果有则创建 FormData 记录)", example = "APPROVAL-AND-DEPLOYMENT")
private String formKey;
@Schema(description = "表单数据")
private Map<String, Object> formData;
@Schema(description = "流程变量(可选)", example = "{\"applicationId\":1,\"environmentId\":2,\"deployJob\":\"user-service-prod\"}")
private Map<String, Object> variables;
}

View File

@ -46,12 +46,6 @@ public class WorkflowInstance extends Entity<Long> {
@Column(name = "business_key")
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;
/**
* 表单数据(JSON)
* 存储流程启动时传入的表单变量
* 流程变量
* 存储所有业务数据如部署参数表单数据等
*/
@Type(JsonType.class)
@Column(name = "form_data", columnDefinition = "json")
private Map<String, Object> formData;
@Column(name = "variables", columnDefinition = "TEXT")
private Map<String, Object> variables;
/**
* 流程图数据快照启动时保存用于画布还原

View File

@ -19,16 +19,6 @@ import java.util.Map;
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
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
public WorkflowInstance updateInstanceStatus(String processInstanceId, WorkflowInstanceStatusEnums status, LocalDateTime endTime) {
WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
@ -190,59 +168,43 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
@Transactional
public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) {
try {
// 步骤1: 先创建 FormData如果需要
FormDataDTO formData = null;
if (request.getFormKey() != null && request.getFormData() != null && !request.getFormData().isEmpty()) {
FormDataCreateFromWorkflowRequest formDataRequest = new FormDataCreateFromWorkflowRequest();
formDataRequest.setFormKey(request.getFormKey());
formDataRequest.setFormData(request.getFormData());
formDataRequest.setBusinessKey(request.getBusinessKey());
// 1. 查询流程定义
WorkflowDefinition definition = workflowDefinitionRepository.findByKey(request.getProcessKey())
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + request.getProcessKey()));
formData = formDataService.createFromWorkflowStart(formDataRequest);
log.info("FormData 已创建: id={}", formData.getId());
} else {
log.info("FormData 创建: 跳过(无 formKey 或表单数据)");
}
// 2. 获取流程变量如果没有则使用空 Map
Map<String, Object> variables = request.getVariables() != null ? request.getVariables() : new HashMap<>();
// 步骤2: 构建 Flowable 变量
Map<String, Object> variables = buildFlowableVariables(request);
// 步骤3: 启动 Flowable 流程
// 3. 启动 Flowable 流程
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey(request.getProcessKey())
.variables(variables)
.businessKey(request.getBusinessKey())
.startAsync(); // 异步启动会自动执行 shell 任务
.startAsync(); // 异步启动会自动执行任务
log.info("Flowable 流程已启动: processInstanceId={}", processInstance.getId());
log.info("Flowable 流程已启动: processInstanceId={}, businessKey={}",
processInstance.getId(), request.getBusinessKey());
// 步骤4: 创建 WorkflowInstance 记录关联 FormData
return createWorkflowInstance(request, processInstance, formData != null ? formData.getId() : null);
// 4. 创建 WorkflowInstance 记录
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) {
log.error("启动工作流失败: processKey={}", request.getProcessKey(), e);
log.error("启动工作流失败: processKey={}, businessKey={}",
request.getProcessKey(), request.getBusinessKey(), 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
public Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query) {
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',
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
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 '实例状态',
variables TEXT NULL COMMENT '流程变量(JSON)',
form_data JSON NULL COMMENT '表单数据(JSON)',
variables TEXT NULL COMMENT '流程变量(JSON包含所有业务数据)',
graph_snapshot JSON NULL COMMENT '流程图数据快照(启动时保存,用于画布还原)',
start_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)
end_time DATETIME(6) NULL COMMENT '结束时间'
-- CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (process_definition_id) REFERENCES workflow_definition(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';