增加生成后端服务代码。
This commit is contained in:
parent
e0d58311c8
commit
7826d4ec27
@ -154,7 +154,7 @@ public class DeployAppConfigServiceImpl extends BaseServiceImpl<DeployAppConfig,
|
|||||||
WorkflowInstanceStartRequest request = new WorkflowInstanceStartRequest();
|
WorkflowInstanceStartRequest request = new WorkflowInstanceStartRequest();
|
||||||
request.setProcessKey(workflowDefinition.getKey());
|
request.setProcessKey(workflowDefinition.getKey());
|
||||||
request.setBusinessKey(environment.getEnvCode() + "_" + application.getAppCode() + "_" + System.currentTimeMillis() / 1000);
|
request.setBusinessKey(environment.getEnvCode() + "_" + application.getAppCode() + "_" + System.currentTimeMillis() / 1000);
|
||||||
request.setVariables(objectMapper.convertValue(dto.getBuildVariables(), new TypeReference<>() {
|
request.setFormData(objectMapper.convertValue(dto.getBuildVariables(), new TypeReference<>() {
|
||||||
}));
|
}));
|
||||||
WorkflowInstanceDTO workflowInstanceDTO = workflowInstanceService.startWorkflow(request);
|
WorkflowInstanceDTO workflowInstanceDTO = workflowInstanceService.startWorkflow(request);
|
||||||
buildAndSaveDeployLog(dto, environment, application, workflowInstanceDTO);
|
buildAndSaveDeployLog(dto, environment, application, workflowInstanceDTO);
|
||||||
|
|||||||
@ -0,0 +1,140 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.security;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 UserDetails 实现
|
||||||
|
* 扩展标准 UserDetails,包含用户ID、租户ID等额外信息
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @date 2025-10-25
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class CustomUserDetails implements UserDetails {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private final Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户是否未过期
|
||||||
|
*/
|
||||||
|
private final boolean accountNonExpired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户是否未锁定
|
||||||
|
*/
|
||||||
|
private final boolean accountNonLocked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证是否未过期
|
||||||
|
*/
|
||||||
|
private final boolean credentialsNonExpired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限列表
|
||||||
|
*/
|
||||||
|
private final Collection<? extends GrantedAuthority> authorities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户ID(可选)
|
||||||
|
*/
|
||||||
|
private final Long tenantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID(可选)
|
||||||
|
*/
|
||||||
|
private final Long departmentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*/
|
||||||
|
public CustomUserDetails(Long userId,
|
||||||
|
String username,
|
||||||
|
String password,
|
||||||
|
boolean enabled,
|
||||||
|
boolean accountNonExpired,
|
||||||
|
boolean accountNonLocked,
|
||||||
|
boolean credentialsNonExpired,
|
||||||
|
Collection<? extends GrantedAuthority> authorities,
|
||||||
|
Long tenantId,
|
||||||
|
Long departmentId) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.enabled = enabled;
|
||||||
|
this.accountNonExpired = accountNonExpired;
|
||||||
|
this.accountNonLocked = accountNonLocked;
|
||||||
|
this.credentialsNonExpired = credentialsNonExpired;
|
||||||
|
this.authorities = authorities;
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
this.departmentId = departmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化构造函数(不包含租户和部门信息)
|
||||||
|
*/
|
||||||
|
public CustomUserDetails(Long userId,
|
||||||
|
String username,
|
||||||
|
String password,
|
||||||
|
boolean enabled,
|
||||||
|
Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
this(userId, username, password, enabled, true, true, true, authorities, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return accountNonExpired;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return accountNonLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return credentialsNonExpired;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -4,10 +4,19 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security 工具类
|
||||||
|
* 提供获取当前用户信息、权限检查等功能
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @date 2025-10-25
|
||||||
|
*/
|
||||||
public class SecurityUtils {
|
public class SecurityUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前登录用户名
|
* 获取当前登录用户名
|
||||||
|
*
|
||||||
|
* @return 当前用户名,未认证时返回 "SYSTEM"
|
||||||
*/
|
*/
|
||||||
public static String getCurrentUsername() {
|
public static String getCurrentUsername() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
@ -22,8 +31,79 @@ public class SecurityUtils {
|
|||||||
return principal.toString();
|
return principal.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户ID(字符串类型)
|
||||||
|
*
|
||||||
|
* @return 当前用户ID,未认证时返回 "system"
|
||||||
|
*/
|
||||||
|
public static String getCurrentUserIdAsString() {
|
||||||
|
Long userId = getCurrentUserId();
|
||||||
|
return userId != null ? userId.toString() : "system";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户ID(Long 类型)
|
||||||
|
*
|
||||||
|
* @return 当前用户ID,未认证时返回 null
|
||||||
|
*/
|
||||||
|
public static Long getCurrentUserId() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
if (principal instanceof CustomUserDetails) {
|
||||||
|
return ((CustomUserDetails) principal).getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容标准 UserDetails(用于测试或特殊场景)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前租户ID
|
||||||
|
*
|
||||||
|
* @return 当前租户ID,未认证或未设置时返回 null
|
||||||
|
*/
|
||||||
|
public static Long getCurrentTenantId() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
if (principal instanceof CustomUserDetails) {
|
||||||
|
return ((CustomUserDetails) principal).getTenantId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前部门ID
|
||||||
|
*
|
||||||
|
* @return 当前部门ID,未认证或未设置时返回 null
|
||||||
|
*/
|
||||||
|
public static Long getCurrentDepartmentId() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
if (principal instanceof CustomUserDetails) {
|
||||||
|
return ((CustomUserDetails) principal).getDepartmentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否有指定权限
|
* 检查是否有指定权限
|
||||||
|
*
|
||||||
|
* @param permission 权限标识
|
||||||
|
* @return 是否拥有该权限
|
||||||
*/
|
*/
|
||||||
public static boolean hasPermission(String permission) {
|
public static boolean hasPermission(String permission) {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
@ -33,22 +113,10 @@ public class SecurityUtils {
|
|||||||
.anyMatch(auth -> auth.getAuthority().equals(permission));
|
.anyMatch(auth -> auth.getAuthority().equals(permission));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户ID
|
|
||||||
*/
|
|
||||||
public static Long getCurrentUserId() {
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
|
||||||
Object principal = authentication.getPrincipal();
|
|
||||||
if (principal instanceof UserDetails) {
|
|
||||||
return Long.valueOf(((UserDetails) principal).getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否已认证
|
* 检查是否已认证
|
||||||
|
*
|
||||||
|
* @return 是否已认证
|
||||||
*/
|
*/
|
||||||
public static boolean isAuthenticated() {
|
public static boolean isAuthenticated() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.system.service.impl;
|
package com.qqchen.deploy.backend.system.service.impl;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.security.CustomUserDetails;
|
||||||
import com.qqchen.deploy.backend.system.entity.User;
|
import com.qqchen.deploy.backend.system.entity.User;
|
||||||
import com.qqchen.deploy.backend.system.repository.IUserRepository;
|
import com.qqchen.deploy.backend.system.repository.IUserRepository;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@ -12,6 +13,13 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户详情服务实现
|
||||||
|
* 负责从数据库加载用户信息并转换为 Spring Security 的 UserDetails
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @date 2025-10-25
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||||
@ -22,15 +30,15 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
|||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
User user = userRepository.findByUsernameAndDeletedFalse(username)
|
User user = userRepository.findByUsernameAndDeletedFalse(username)
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
|
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
|
||||||
return new org.springframework.security.core.userdetails.User(
|
|
||||||
user.getUsername(),
|
// ✅ 返回自定义 UserDetails,包含用户ID等扩展信息
|
||||||
user.getPassword(),
|
return new CustomUserDetails(
|
||||||
user.getEnabled(),
|
user.getId(), // 用户ID
|
||||||
true,
|
user.getUsername(), // 用户名
|
||||||
true,
|
user.getPassword(), // 密码
|
||||||
true,
|
user.getEnabled(), // 是否启用
|
||||||
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
|
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) // 权限(TODO: 从数据库加载实际权限)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,19 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.converter;
|
package com.qqchen.deploy.backend.workflow.converter;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
|
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
|
||||||
|
import com.qqchen.deploy.backend.framework.security.SecurityUtils;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDataCreateFromWorkflowRequest;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDefinitionDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
||||||
|
import com.qqchen.deploy.backend.workflow.enums.FormBusinessTypeEnums;
|
||||||
|
import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单数据转换器
|
* 表单数据转换器
|
||||||
@ -13,5 +23,48 @@ import org.mapstruct.Mapper;
|
|||||||
*/
|
*/
|
||||||
@Mapper(config = BaseConverter.class)
|
@Mapper(config = BaseConverter.class)
|
||||||
public interface FormDataConverter extends BaseConverter<FormData, FormDataDTO> {
|
public interface FormDataConverter extends BaseConverter<FormData, FormDataDTO> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从工作流启动创建表单数据 DTO
|
||||||
|
* 从 FormDefinitionDTO 和 FormDataCreateFromWorkflowRequest 映射到 FormDataDTO
|
||||||
|
*
|
||||||
|
* @param formDefinition 表单定义
|
||||||
|
* @param request 工作流表单数据请求
|
||||||
|
* @return 表单数据 DTO
|
||||||
|
*/
|
||||||
|
@Mapping(target = "id", ignore = true)
|
||||||
|
@Mapping(target = "formDefinitionId", source = "formDefinition.id")
|
||||||
|
@Mapping(target = "formKey", source = "formDefinition.key")
|
||||||
|
@Mapping(target = "formVersion", source = "formDefinition.formVersion")
|
||||||
|
@Mapping(target = "categoryId", source = "formDefinition.categoryId")
|
||||||
|
@Mapping(target = "schemaSnapshot", source = "formDefinition.schema")
|
||||||
|
@Mapping(target = "data", source = "request.formData")
|
||||||
|
@Mapping(target = "businessKey", source = "request.businessKey")
|
||||||
|
@Mapping(target = "businessType", constant = "WORKFLOW")
|
||||||
|
@Mapping(target = "status", constant = "SUBMITTED")
|
||||||
|
@Mapping(target = "submitter", ignore = true) // 在 @AfterMapping 中设置
|
||||||
|
@Mapping(target = "submitTime", ignore = true) // 在 @AfterMapping 中设置
|
||||||
|
@Mapping(target = "createTime", ignore = true)
|
||||||
|
@Mapping(target = "updateTime", ignore = true)
|
||||||
|
@Mapping(target = "createBy", ignore = true)
|
||||||
|
@Mapping(target = "updateBy", ignore = true)
|
||||||
|
@Mapping(target = "version", ignore = true)
|
||||||
|
@Mapping(target = "deleted", ignore = true)
|
||||||
|
FormDataDTO fromWorkflowRequest(
|
||||||
|
FormDefinitionDTO formDefinition,
|
||||||
|
FormDataCreateFromWorkflowRequest request
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后处理:设置动态值
|
||||||
|
*/
|
||||||
|
@AfterMapping
|
||||||
|
default void setDynamicFields(@MappingTarget FormDataDTO formDataDTO) {
|
||||||
|
// ✅ 设置当前用户
|
||||||
|
formDataDTO.setSubmitter(SecurityUtils.getCurrentUserIdAsString());
|
||||||
|
|
||||||
|
// ✅ 设置提交时间
|
||||||
|
formDataDTO.setSubmitTime(LocalDateTime.now());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,22 +4,76 @@ import com.qqchen.deploy.backend.framework.converter.BaseConverter;
|
|||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowHistoricalInstancesDTO;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowHistoricalInstancesDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
import org.mapstruct.Mapper;
|
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
||||||
import org.mapstruct.Named;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
import org.mapstruct.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流定义转换器
|
* 工作流实例转换器
|
||||||
*/
|
*/
|
||||||
@Mapper(config = BaseConverter.class)
|
@Mapper(config = BaseConverter.class)
|
||||||
public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstance, WorkflowInstanceDTO> {
|
public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstance, WorkflowInstanceDTO> {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Named("toDto")
|
@Named("toDto")
|
||||||
WorkflowInstanceDTO toDto(WorkflowInstance entity);
|
WorkflowInstanceDTO toDto(WorkflowInstance entity);
|
||||||
|
|
||||||
@Named("toHistoricalDto")
|
@Named("toHistoricalDto")
|
||||||
WorkflowHistoricalInstancesDTO toWorkflowHistoricalInstancesDTO(WorkflowInstance workflowInstance);
|
WorkflowHistoricalInstancesDTO toWorkflowHistoricalInstancesDTO(WorkflowInstance workflowInstance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从多个源对象创建 WorkflowInstance
|
||||||
|
*
|
||||||
|
* @param request 工作流启动请求
|
||||||
|
* @param processInstance Flowable 流程实例
|
||||||
|
* @param definition 工作流定义
|
||||||
|
* @param formDataId 表单数据ID(可选)
|
||||||
|
* @return 工作流实例
|
||||||
|
*/
|
||||||
|
@Mapping(target = "id", ignore = true)
|
||||||
|
@Mapping(target = "processInstanceId", source = "processInstance.id")
|
||||||
|
@Mapping(target = "processDefinitionId", source = "processInstance.processDefinitionId")
|
||||||
|
@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 = "status", constant = "NOT_STARTED")
|
||||||
|
@Mapping(target = "startTime", ignore = true) // 在 @AfterMapping 中设置
|
||||||
|
@Mapping(target = "endTime", ignore = true)
|
||||||
|
@Mapping(target = "createTime", ignore = true)
|
||||||
|
@Mapping(target = "updateTime", ignore = true)
|
||||||
|
@Mapping(target = "createBy", ignore = true)
|
||||||
|
@Mapping(target = "updateBy", ignore = true)
|
||||||
|
@Mapping(target = "version", ignore = true)
|
||||||
|
@Mapping(target = "deleted", ignore = true)
|
||||||
|
WorkflowInstance toEntity(
|
||||||
|
WorkflowInstanceStartRequest request,
|
||||||
|
ProcessInstance processInstance,
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从工作流启动创建表单数据请求
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @date 2025-10-25
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "从工作流启动创建表单数据请求")
|
||||||
|
public class FormDataCreateFromWorkflowRequest {
|
||||||
|
|
||||||
|
@Schema(description = "表单标识", required = true, example = "APPROVAL-AND-DEPLOYMENT")
|
||||||
|
private String formKey;
|
||||||
|
|
||||||
|
@Schema(description = "表单填写数据", required = true)
|
||||||
|
private Map<String, Object> formData;
|
||||||
|
|
||||||
|
@Schema(description = "业务标识(工作流实例的 businessKey)", required = true, example = "DEPLOY-2023-001")
|
||||||
|
private String businessKey;
|
||||||
|
}
|
||||||
|
|
||||||
@ -34,20 +34,16 @@ public class WorkflowInstanceDTO extends BaseDTO {
|
|||||||
*/
|
*/
|
||||||
private Long formDataId;
|
private Long formDataId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动表单数据(用于展示)
|
|
||||||
*/
|
|
||||||
private FormDataDTO formData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实例状态
|
* 实例状态
|
||||||
*/
|
*/
|
||||||
private WorkflowInstanceStatusEnums status;
|
private WorkflowInstanceStatusEnums status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程变量(JSON)
|
* 表单数据(JSON)
|
||||||
|
* 存储流程启动时传入的表单变量
|
||||||
*/
|
*/
|
||||||
private String variables;
|
private Map<String, Object> formData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始时间
|
* 开始时间
|
||||||
|
|||||||
@ -9,10 +9,16 @@ import java.util.Map;
|
|||||||
@Schema(description = "工作流实例启动入参")
|
@Schema(description = "工作流实例启动入参")
|
||||||
public class WorkflowInstanceStartRequest {
|
public class WorkflowInstanceStartRequest {
|
||||||
|
|
||||||
|
@Schema(description = "流程定义标识", example = "APPROVAL-AND-DEPLOYMENT")
|
||||||
private String processKey;
|
private String processKey;
|
||||||
|
|
||||||
|
@Schema(description = "业务键", example = "DEPLOY-2023-001")
|
||||||
private String businessKey;
|
private String businessKey;
|
||||||
|
|
||||||
private Map<String, Object> variables;
|
@Schema(description = "表单标识(可选,如果有则创建 FormData 记录)", example = "APPROVAL-AND-DEPLOYMENT")
|
||||||
|
private String formKey;
|
||||||
|
|
||||||
|
@Schema(description = "表单数据")
|
||||||
|
private Map<String, Object> formData;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.qqchen.deploy.backend.framework.domain.Entity;
|
|||||||
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph;
|
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.converter.WorkflowGraphType;
|
import com.qqchen.deploy.backend.workflow.entity.converter.WorkflowGraphType;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
||||||
|
import com.vladmihalcea.hibernate.type.json.JsonType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
import jakarta.persistence.Enumerated;
|
import jakarta.persistence.Enumerated;
|
||||||
@ -13,6 +14,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流实例实体
|
* 工作流实例实体
|
||||||
@ -58,10 +60,12 @@ public class WorkflowInstance extends Entity<Long> {
|
|||||||
private WorkflowInstanceStatusEnums status;
|
private WorkflowInstanceStatusEnums status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程变量(JSON)
|
* 表单数据(JSON)
|
||||||
|
* 存储流程启动时传入的表单变量
|
||||||
*/
|
*/
|
||||||
@Column(columnDefinition = "TEXT")
|
@Type(JsonType.class)
|
||||||
private String variables;
|
@Column(name = "form_data", columnDefinition = "json")
|
||||||
|
private Map<String, Object> formData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程图数据快照(启动时保存,用于画布还原)
|
* 流程图数据快照(启动时保存,用于画布还原)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.service;
|
package com.qqchen.deploy.backend.workflow.service;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.service.IBaseService;
|
import com.qqchen.deploy.backend.framework.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDataCreateFromWorkflowRequest;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.query.FormDataQuery;
|
import com.qqchen.deploy.backend.workflow.dto.query.FormDataQuery;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
||||||
@ -28,5 +29,14 @@ public interface IFormDataService extends IBaseService<FormData, FormDataDTO, Fo
|
|||||||
* @return 表单数据
|
* @return 表单数据
|
||||||
*/
|
*/
|
||||||
FormDataDTO getByBusinessKey(String businessKey);
|
FormDataDTO getByBusinessKey(String businessKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从工作流启动创建表单数据
|
||||||
|
* 该方法会自动查询表单定义,组装完整的 FormData 对象
|
||||||
|
*
|
||||||
|
* @param request 创建请求(包含 formKey、formData、businessKey)
|
||||||
|
* @return 创建后的表单数据
|
||||||
|
*/
|
||||||
|
FormDataDTO createFromWorkflowStart(FormDataCreateFromWorkflowRequest request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,10 +22,12 @@ public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance,
|
|||||||
/**
|
/**
|
||||||
* 创建工作流实例并关联Flowable实例
|
* 创建工作流实例并关联Flowable实例
|
||||||
*
|
*
|
||||||
|
* @param request 工作流实例启动请求
|
||||||
* @param processInstance Flowable流程实例
|
* @param processInstance Flowable流程实例
|
||||||
|
* @param formDataId 表单数据ID(可选,如果有则关联)
|
||||||
* @return 工作流实例
|
* @return 工作流实例
|
||||||
*/
|
*/
|
||||||
WorkflowInstanceDTO createWorkflowInstance(Long workflowDefinitionId, String businessKey, ProcessInstance processInstance);
|
WorkflowInstanceDTO createWorkflowInstance(WorkflowInstanceStartRequest request, ProcessInstance processInstance, Long formDataId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新工作流实例状态
|
* 更新工作流实例状态
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
package com.qqchen.deploy.backend.workflow.service.impl;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||||
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDataCreateFromWorkflowRequest;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDefinitionDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.query.FormDataQuery;
|
import com.qqchen.deploy.backend.workflow.dto.query.FormDataQuery;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
import com.qqchen.deploy.backend.workflow.entity.FormData;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.FormDefinition;
|
import com.qqchen.deploy.backend.workflow.entity.FormDefinition;
|
||||||
@ -10,6 +13,7 @@ import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums;
|
|||||||
import com.qqchen.deploy.backend.workflow.repository.IFormDataRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IFormDataRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IFormDefinitionRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IFormDefinitionRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.service.IFormDataService;
|
import com.qqchen.deploy.backend.workflow.service.IFormDataService;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.IFormDefinitionService;
|
||||||
import com.qqchen.deploy.backend.workflow.converter.FormDataConverter;
|
import com.qqchen.deploy.backend.workflow.converter.FormDataConverter;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -34,6 +38,9 @@ public class FormDataServiceImpl extends BaseServiceImpl<FormData, FormDataDTO,
|
|||||||
@Resource
|
@Resource
|
||||||
private IFormDefinitionRepository formDefinitionRepository;
|
private IFormDefinitionRepository formDefinitionRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IFormDefinitionService formDefinitionService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FormDataConverter formDataConverter;
|
private FormDataConverter formDataConverter;
|
||||||
|
|
||||||
@ -71,5 +78,27 @@ public class FormDataServiceImpl extends BaseServiceImpl<FormData, FormDataDTO,
|
|||||||
|
|
||||||
return formDataConverter.toDto(formData);
|
return formDataConverter.toDto(formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public FormDataDTO createFromWorkflowStart(FormDataCreateFromWorkflowRequest request) {
|
||||||
|
log.info("从工作流启动创建表单数据: formKey={}, businessKey={}", request.getFormKey(), request.getBusinessKey());
|
||||||
|
|
||||||
|
// 1. 查询表单定义(获取最新版本)
|
||||||
|
FormDefinitionDTO formDefinition = formDefinitionService.getLatestByKey(request.getFormKey());
|
||||||
|
if (formDefinition == null) {
|
||||||
|
throw new BusinessException(ResponseCode.FORM_DEFINITION_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 使用 MapStruct Converter 创建 FormDataDTO(处理多源对象映射)
|
||||||
|
FormDataDTO formDataDTO = formDataConverter.fromWorkflowRequest(formDefinition, request);
|
||||||
|
|
||||||
|
// 3. 提交表单数据
|
||||||
|
FormDataDTO savedFormData = submit(formDataDTO);
|
||||||
|
log.info("工作流表单数据创建成功: id={}, formKey={}, formVersion={}",
|
||||||
|
savedFormData.getId(), formDefinition.getKey(), formDefinition.getFormVersion());
|
||||||
|
|
||||||
|
return savedFormData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
||||||
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
|
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
|
||||||
import com.qqchen.deploy.backend.workflow.converter.WorkflowNodeInstanceConverter;
|
import com.qqchen.deploy.backend.workflow.converter.WorkflowNodeInstanceConverter;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDataCreateFromWorkflowRequest;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.FormDataDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowHistoricalInstancesDTO;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowHistoricalInstancesDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||||
@ -21,6 +23,8 @@ import com.qqchen.deploy.backend.workflow.dto.query.WorkflowHistoricalInstancesQ
|
|||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowNodeInstanceRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowNodeInstanceRepository;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.IFormDataService;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.IFormDefinitionService;
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
||||||
import com.qqchen.deploy.backend.workflow.util.FlowableUtils;
|
import com.qqchen.deploy.backend.workflow.util.FlowableUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@ -55,6 +59,12 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
@Resource
|
@Resource
|
||||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IFormDefinitionService formDefinitionService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IFormDataService formDataService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RuntimeService runtimeService;
|
private RuntimeService runtimeService;
|
||||||
|
|
||||||
@ -77,24 +87,22 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
private WorkflowNodeInstanceConverter workflowNodeInstanceConverter;
|
private WorkflowNodeInstanceConverter workflowNodeInstanceConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorkflowInstanceDTO createWorkflowInstance(Long workflowDefinitionId, String businessKey, ProcessInstance processInstance) {
|
public WorkflowInstanceDTO createWorkflowInstance(WorkflowInstanceStartRequest request, ProcessInstance processInstance, Long formDataId) {
|
||||||
// 1. 查询流程定义,获取 graph 快照
|
// 1. 查询流程定义,获取 graph 快照
|
||||||
WorkflowDefinition definition = workflowDefinitionRepository.findById(workflowDefinitionId)
|
WorkflowDefinition definition = workflowDefinitionRepository.findByKey(request.getProcessKey())
|
||||||
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + workflowDefinitionId));
|
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + request.getProcessKey()));
|
||||||
|
|
||||||
// 2. 创建实例并保存 graph 快照
|
// 2. 使用 MapStruct Converter 创建实例(处理多源对象映射)
|
||||||
WorkflowInstance workflowInstance = new WorkflowInstance();
|
WorkflowInstance workflowInstance = workflowInstanceConverter.toEntity(
|
||||||
workflowInstance.setProcessInstanceId(processInstance.getId());
|
request,
|
||||||
workflowInstance.setProcessDefinitionId(processInstance.getProcessDefinitionId());
|
processInstance,
|
||||||
workflowInstance.setWorkflowDefinitionId(workflowDefinitionId);
|
definition,
|
||||||
workflowInstance.setBusinessKey(businessKey);
|
formDataId
|
||||||
workflowInstance.setGraphSnapshot(definition.getGraph()); // ✅ 保存画布数据快照
|
);
|
||||||
workflowInstance.setStatus(WorkflowInstanceStatusEnums.NOT_STARTED);
|
|
||||||
workflowInstance.setStartTime(LocalDateTime.now());
|
|
||||||
workflowInstanceRepository.save(workflowInstance);
|
workflowInstanceRepository.save(workflowInstance);
|
||||||
|
|
||||||
log.info("创建工作流实例: instanceId={}, definitionId={}, graph已保存",
|
log.info("创建工作流实例: instanceId={}, definitionId={}, formDataId={}, graph已保存",
|
||||||
workflowInstance.getId(), workflowDefinitionId);
|
workflowInstance.getId(), definition.getId(), formDataId);
|
||||||
|
|
||||||
// 3. 返回创建结果
|
// 3. 返回创建结果
|
||||||
return workflowInstanceConverter.toDto(workflowInstance);
|
return workflowInstanceConverter.toDto(workflowInstance);
|
||||||
@ -170,11 +178,8 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
|
WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
|
||||||
.orElseThrow(() -> new RuntimeException("Workflow instance not found: " + processInstanceId));
|
.orElseThrow(() -> new RuntimeException("Workflow instance not found: " + processInstanceId));
|
||||||
|
|
||||||
try {
|
// ✅ 不再更新 formData,因为它应该保持启动时的数据不变
|
||||||
instance.setVariables(objectMapper.writeValueAsString(variables));
|
// formData 字段只在流程启动时设置一次,用于记录启动表单数据
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.error("Failed to serialize variables", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.setStatus(WorkflowInstanceStatusEnums.COMPLETED);
|
instance.setStatus(WorkflowInstanceStatusEnums.COMPLETED);
|
||||||
instance.setEndTime(LocalDateTime.now());
|
instance.setEndTime(LocalDateTime.now());
|
||||||
@ -185,28 +190,59 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
@Transactional
|
@Transactional
|
||||||
public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) {
|
public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) {
|
||||||
try {
|
try {
|
||||||
WorkflowDefinition workflowDefinition = workflowDefinitionRepository.findByKey(request.getProcessKey()).orElseThrow(() -> new RuntimeException("Workflow definition process key not found: " + request.getProcessKey()));
|
// ✅ 步骤1: 先创建 FormData(如果需要)
|
||||||
|
FormDataDTO formData = null;
|
||||||
// ✅ 将前端传入的 variables 包装到 "form" 变量中
|
if (request.getFormKey() != null && request.getFormData() != null && !request.getFormData().isEmpty()) {
|
||||||
// 这样流程中的 ${form.xxx} 表达式就能正确解析
|
FormDataCreateFromWorkflowRequest formDataRequest = new FormDataCreateFromWorkflowRequest();
|
||||||
Map<String, Object> variables = new HashMap<>();
|
formDataRequest.setFormKey(request.getFormKey());
|
||||||
if (request.getVariables() != null && !request.getVariables().isEmpty()) {
|
formDataRequest.setFormData(request.getFormData());
|
||||||
variables.put("form", request.getVariables());
|
formDataRequest.setBusinessKey(request.getBusinessKey());
|
||||||
|
|
||||||
|
formData = formDataService.createFromWorkflowStart(formDataRequest);
|
||||||
|
log.info("FormData 已创建: id={}", formData.getId());
|
||||||
|
} else {
|
||||||
|
log.info("FormData 创建: 跳过(无 formKey 或表单数据)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 步骤2: 构建 Flowable 变量
|
||||||
|
Map<String, Object> variables = buildFlowableVariables(request);
|
||||||
|
|
||||||
|
// ✅ 步骤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(); // 异步启动,会自动执行 shell 任务
|
||||||
// .start();
|
|
||||||
return createWorkflowInstance(workflowDefinition.getId(), request.getBusinessKey(), processInstance);
|
log.info("Flowable 流程已启动: processInstanceId={}", processInstance.getId());
|
||||||
|
|
||||||
|
// ✅ 步骤4: 创建 WorkflowInstance 记录(关联 FormData)
|
||||||
|
return createWorkflowInstance(request, processInstance, formData != null ? formData.getId() : null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to create workflow: {}", request.getProcessKey(), e);
|
log.error("启动工作流失败: processKey={}", request.getProcessKey(), e);
|
||||||
throw new RuntimeException("Failed to create workflow", 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());
|
||||||
|
|||||||
@ -452,8 +452,10 @@ 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 VARCHAR(100) NOT NULL COMMENT '实例状态',
|
status VARCHAR(100) NOT NULL COMMENT '实例状态',
|
||||||
variables TEXT NULL COMMENT '流程变量(JSON)',
|
form_data JSON NULL COMMENT '表单数据(JSON)',
|
||||||
|
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 '结束时间'
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user