增加form表单

This commit is contained in:
dengqichen 2025-10-24 14:54:01 +08:00
parent 6d8afea807
commit 42a8609b8c
25 changed files with 443 additions and 341 deletions

View File

@ -0,0 +1,33 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowCategoryDTO;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowCategoryQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowCategory;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 工作流分类API Controller
*
* @author qqchen
* @date 2025-10-24
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/categories")
@Tag(name = "工作流分类管理", description = "工作流分类相关接口")
public class WorkflowCategoryApiController extends BaseController<WorkflowCategory, WorkflowCategoryDTO, Long, WorkflowCategoryQuery> {
@Override
protected void exportData(HttpServletResponse response, List<WorkflowCategoryDTO> data) {
// TODO: 实现工作流分类数据导出逻辑
log.warn("工作流分类数据导出功能暂未实现");
}
}

View File

@ -0,0 +1,17 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowCategoryDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowCategory;
import org.mapstruct.Mapper;
/**
* 工作流分类转换器
*
* @author qqchen
* @date 2025-10-24
*/
@Mapper(config = BaseConverter.class)
public interface WorkflowCategoryConverter extends BaseConverter<WorkflowCategory, WorkflowCategoryDTO> {
}

View File

@ -1,7 +1,6 @@
package com.qqchen.deploy.backend.workflow.dto; package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO; import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.dto.form.FormSchema;
import com.qqchen.deploy.backend.workflow.enums.FormBusinessTypeEnums; import com.qqchen.deploy.backend.workflow.enums.FormBusinessTypeEnums;
import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums; import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -40,6 +39,18 @@ public class FormDataDTO extends BaseDTO {
@Schema(description = "表单版本", example = "1") @Schema(description = "表单版本", example = "1")
private Integer formVersion; private Integer formVersion;
/**
* 表单分类ID冗余存储便于统计和查询
*/
@Schema(description = "表单分类ID", example = "1")
private Long categoryId;
/**
* 表单分类信息用于展示
*/
@Schema(description = "表单分类信息")
private FormCategoryDTO category;
/** /**
* 业务标识如工作流实例ID订单号等 * 业务标识如工作流实例ID订单号等
*/ */
@ -59,10 +70,10 @@ public class FormDataDTO extends BaseDTO {
private Map<String, Object> data; private Map<String, Object> data;
/** /**
* 表单Schema快照用于历史追溯确保数据可还原 * 表单Schema快照用于历史追溯确保数据可还原原样存储
*/ */
@Schema(description = "表单Schema快照") @Schema(description = "表单Schema快照")
private FormSchema schemaSnapshot; private Map<String, Object> schemaSnapshot;
/** /**
* 提交人 * 提交人

View File

@ -1,13 +1,13 @@
package com.qqchen.deploy.backend.workflow.dto; package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO; import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.dto.form.FormSchema;
import com.qqchen.deploy.backend.workflow.enums.FormDefinitionStatusEnums; import com.qqchen.deploy.backend.workflow.enums.FormDefinitionStatusEnums;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 表单定义DTO * 表单定义DTO
@ -57,10 +57,10 @@ public class FormDefinitionDTO extends BaseDTO {
private String description; private String description;
/** /**
* 表单Schema前端设计器导出的JSON结构 * 表单Schema前端设计器导出的JSON结构原样存储
*/ */
@Schema(description = "表单Schema") @Schema(description = "表单Schema")
private FormSchema schema; private Map<String, Object> schema;
/** /**
* 标签用于分类和搜索 * 标签用于分类和搜索

View File

@ -1,19 +1,64 @@
package com.qqchen.deploy.backend.workflow.dto; package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List; import java.util.List;
/**
* 工作流分类DTO
*
* @author qqchen
* @date 2025-10-24
*/
@Data @Data
public class WorkflowCategoryDTO { @EqualsAndHashCode(callSuper = true)
@Schema(description = "工作流分类DTO")
public class WorkflowCategoryDTO extends BaseDTO {
private Long id;
/**
* 分类名称
*/
@Schema(description = "分类名称", example = "脚本执行")
private String name;
/**
* 分类编码唯一
*/
@Schema(description = "分类编码", example = "SCRIPT_EXECUTION")
private String code; private String code;
private String lable; /**
* 分类描述
*/
@Schema(description = "分类描述", example = "用于执行各类脚本的流程")
private String description; private String description;
private List<WorkflowTriggerTypeDTO> supportedTriggers; /**
* 图标
*/
@Schema(description = "图标", example = "CodeOutlined")
private String icon;
/**
* 排序
*/
@Schema(description = "排序", example = "1")
private Integer sort;
/**
* 支持的触发方式列表
*/
@Schema(description = "支持的触发方式列表", example = "[\"MANUAL\",\"SCHEDULED\"]")
private List<String> supportedTriggers;
/**
* 是否启用
*/
@Schema(description = "是否启用", example = "true")
private Boolean enabled;
} }

View File

@ -3,7 +3,6 @@ package com.qqchen.deploy.backend.workflow.dto;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.framework.dto.BaseDTO; import com.qqchen.deploy.backend.framework.dto.BaseDTO;
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.enums.WorkflowCategoryEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums; import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowTriggerTypeEnum; import com.qqchen.deploy.backend.workflow.enums.WorkflowTriggerTypeEnum;
import lombok.Data; import lombok.Data;
@ -32,9 +31,14 @@ public class WorkflowDefinitionDTO extends BaseDTO {
private String key; private String key;
/** /**
* 分类 * 分类ID
*/ */
private WorkflowCategoryEnum category; private Long categoryId;
/**
* 分类信息用于展示
*/
private WorkflowCategoryDTO category;
/** /**
* 触发类型列表 * 触发类型列表

View File

@ -1,79 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.form;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 表单Schema定义
* 用于存储前端表单设计器导出的JSON结构
*
* @author qqchen
* @date 2025-10-24
*/
@Data
public class FormSchema {
/**
* 表单标题
*/
private String title;
/**
* 表单描述
*/
private String description;
/**
* 表单字段列表
*/
private List<FormField> fields;
/**
* 表单配置
*/
private Map<String, Object> config;
/**
* 表单字段定义
*/
@Data
public static class FormField {
/**
* 字段名称
*/
private String name;
/**
* 字段标签
*/
private String label;
/**
* 字段类型inputselecttextareadatenumber等
*/
private String type;
/**
* 是否必填
*/
private Boolean required;
/**
* 默认值
*/
private Object defaultValue;
/**
* 字段配置不同类型字段有不同配置
*/
private Map<String, Object> config;
/**
* 验证规则
*/
private List<Map<String, Object>> validators;
}
}

View File

@ -30,6 +30,12 @@ public class FormDataQuery extends BaseQuery {
@QueryField(field = "formKey", type = QueryType.EQUAL) @QueryField(field = "formKey", type = QueryType.EQUAL)
private String formKey; private String formKey;
/**
* 表单分类ID精确查询
*/
@QueryField(field = "categoryId", type = QueryType.EQUAL)
private Long categoryId;
/** /**
* 业务标识精确查询 * 业务标识精确查询
*/ */

View File

@ -0,0 +1,37 @@
package com.qqchen.deploy.backend.workflow.dto.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流分类查询对象
*
* @author qqchen
* @date 2025-10-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowCategoryQuery extends BaseQuery {
/**
* 分类名称模糊查询
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 分类编码精确查询
*/
@QueryField(field = "code", type = QueryType.EQUAL)
private String code;
/**
* 是否启用精确查询
*/
@QueryField(field = "enabled", type = QueryType.EQUAL)
private Boolean enabled;
}

View File

@ -3,6 +3,7 @@ package com.qqchen.deploy.backend.workflow.dto.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField; import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType; import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery; import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -25,9 +26,21 @@ public class WorkflowDefinitionQuery extends BaseQuery {
@QueryField(field = "key", type = QueryType.EQUAL) @QueryField(field = "key", type = QueryType.EQUAL)
private String key; private String key;
/**
* 分类ID
*/
@QueryField(field = "categoryId", type = QueryType.EQUAL)
private Long categoryId;
/** /**
* 流程版本 * 流程版本
*/ */
@QueryField(field = "version", type = QueryType.EQUAL) @QueryField(field = "flowVersion", type = QueryType.EQUAL)
private Integer version; private Integer flowVersion;
/**
* 流程状态
*/
@QueryField(field = "status", type = QueryType.EQUAL)
private WorkflowDefinitionStatusEnums status;
} }

View File

@ -2,8 +2,6 @@ package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.dto.form.FormSchema;
import com.qqchen.deploy.backend.workflow.entity.converter.FormSchemaType;
import com.qqchen.deploy.backend.workflow.enums.FormBusinessTypeEnums; import com.qqchen.deploy.backend.workflow.enums.FormBusinessTypeEnums;
import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums; import com.qqchen.deploy.backend.workflow.enums.FormDataStatusEnums;
import com.vladmihalcea.hibernate.type.json.JsonType; import com.vladmihalcea.hibernate.type.json.JsonType;
@ -46,6 +44,12 @@ public class FormData extends Entity<Long> {
@Column(name = "form_version", nullable = false) @Column(name = "form_version", nullable = false)
private Integer formVersion; private Integer formVersion;
/**
* 表单分类ID冗余存储便于统计和查询
*/
@Column(name = "category_id")
private Long categoryId;
/** /**
* 业务标识如工作流实例ID订单号等 * 业务标识如工作流实例ID订单号等
*/ */
@ -67,11 +71,11 @@ public class FormData extends Entity<Long> {
private Map<String, Object> data; private Map<String, Object> data;
/** /**
* 表单Schema快照用于历史追溯确保数据可还原 * 表单Schema快照用于历史追溯确保数据可还原原样存储
*/ */
@Type(FormSchemaType.class) @Type(JsonType.class)
@Column(name = "schema_snapshot", nullable = false, columnDefinition = "json") @Column(name = "schema_snapshot", nullable = false, columnDefinition = "json")
private FormSchema schemaSnapshot; private Map<String, Object> schemaSnapshot;
/** /**
* 提交人 * 提交人

View File

@ -2,8 +2,6 @@ package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.dto.form.FormSchema;
import com.qqchen.deploy.backend.workflow.entity.converter.FormSchemaType;
import com.qqchen.deploy.backend.workflow.enums.FormDefinitionStatusEnums; import com.qqchen.deploy.backend.workflow.enums.FormDefinitionStatusEnums;
import com.vladmihalcea.hibernate.type.json.JsonType; import com.vladmihalcea.hibernate.type.json.JsonType;
import jakarta.persistence.*; import jakarta.persistence.*;
@ -12,6 +10,7 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 表单定义实体 * 表单定义实体
@ -57,11 +56,11 @@ public class FormDefinition extends Entity<Long> {
private String description; private String description;
/** /**
* 表单Schema前端设计器导出的JSON结构 * 表单Schema前端设计器导出的JSON结构原样存储
*/ */
@Type(FormSchemaType.class) @Type(JsonType.class)
@Column(name = "`schema`", nullable = false, columnDefinition = "json") @Column(name = "`schema`", nullable = false, columnDefinition = "json")
private FormSchema schema; private Map<String, Object> schema;
/** /**
* 标签用于分类和搜索 * 标签用于分类和搜索

View File

@ -0,0 +1,69 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.vladmihalcea.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import java.util.List;
/**
* 工作流分类实体
*
* @author qqchen
* @date 2025-10-24
*/
@Data
@Table(name = "workflow_category")
@jakarta.persistence.Entity
@EqualsAndHashCode(callSuper = true)
@LogicDelete
public class WorkflowCategory extends Entity<Long> {
/**
* 分类名称
*/
@Column(name = "name", nullable = false, length = 100)
private String name;
/**
* 分类编码唯一如SCRIPT_EXECUTION
*/
@Column(name = "code", nullable = false, length = 50, unique = true)
private String code;
/**
* 分类描述
*/
@Column(name = "description", length = 500)
private String description;
/**
* 图标
*/
@Column(name = "icon", length = 50)
private String icon;
/**
* 排序
*/
@Column(name = "sort", nullable = false)
private Integer sort = 0;
/**
* 支持的触发方式列表JSON数组["MANUAL","SCHEDULED"]
*/
@Type(JsonType.class)
@Column(name = "supported_triggers", columnDefinition = "json")
private List<String> supportedTriggers;
/**
* 是否启用
*/
@Column(name = "enabled", nullable = false)
private Boolean enabled = true;
}

View File

@ -3,7 +3,6 @@ package com.qqchen.deploy.backend.workflow.entity;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
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.enums.WorkflowCategoryEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums; import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowTriggerTypeEnum; import com.qqchen.deploy.backend.workflow.enums.WorkflowTriggerTypeEnum;
import com.qqchen.deploy.backend.workflow.entity.converter.WorkflowGraphType; import com.qqchen.deploy.backend.workflow.entity.converter.WorkflowGraphType;
@ -43,11 +42,10 @@ public class WorkflowDefinition extends Entity<Long> {
private String key; private String key;
/** /**
* 流程分类 * 流程分类ID外键关联workflow_category
*/ */
@Column(name = "category", nullable = false) @Column(name = "category_id")
@Enumerated(EnumType.STRING) private Long categoryId;
private WorkflowCategoryEnum category;
@Column(name = "triggers", nullable = false) @Column(name = "triggers", nullable = false)

View File

@ -1,131 +0,0 @@
package com.qqchen.deploy.backend.workflow.entity.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.workflow.dto.form.FormSchema;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
/**
* 自定义 Hibernate 类型用于处理 FormSchema 的序列化和反序列化
*
* @author qqchen
* @date 2025-10-24
*/
public class FormSchemaType implements UserType<FormSchema> {
private static final Logger log = LoggerFactory.getLogger(FormSchemaType.class);
private final ObjectMapper objectMapper;
public FormSchemaType() {
this.objectMapper = new ObjectMapper();
// 配置 ObjectMapper忽略未知属性
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public int getSqlType() {
return Types.VARCHAR;
}
@Override
public Class<FormSchema> returnedClass() {
return FormSchema.class;
}
@Override
public boolean equals(FormSchema x, FormSchema y) {
if (x == y) {
return true;
}
if (x == null || y == null) {
return false;
}
return x.equals(y);
}
@Override
public int hashCode(FormSchema x) {
return x == null ? 0 : x.hashCode();
}
@Override
public FormSchema nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner)
throws SQLException {
String value = rs.getString(position);
if (value == null) {
return null;
}
try {
return objectMapper.readValue(value, FormSchema.class);
} catch (JsonProcessingException e) {
// 记录错误日志但不抛出异常返回 null 使得单条记录的错误不影响整体查询
log.error("Failed to convert String to FormSchema: " + value, e);
return null;
}
}
@Override
public void nullSafeSet(PreparedStatement st, FormSchema value, int index, SharedSessionContractImplementor session)
throws SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
return;
}
try {
st.setString(index, objectMapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new HibernateException("Failed to convert FormSchema to String: " + value, e);
}
}
@Override
public FormSchema deepCopy(FormSchema value) {
if (value == null) {
return null;
}
try {
return objectMapper.readValue(objectMapper.writeValueAsString(value), FormSchema.class);
} catch (JsonProcessingException e) {
throw new HibernateException("Failed to deep copy FormSchema", e);
}
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Serializable disassemble(FormSchema value) {
try {
return value == null ? null : objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new HibernateException("Failed to disassemble FormSchema", e);
}
}
@Override
public FormSchema assemble(Serializable cached, Object owner) {
try {
return cached == null ? null : objectMapper.readValue(cached.toString(), FormSchema.class);
} catch (JsonProcessingException e) {
throw new HibernateException("Failed to assemble FormSchema", e);
}
}
@Override
public FormSchema replace(FormSchema detached, FormSchema managed, Object owner) {
return deepCopy(detached);
}
}

View File

@ -1,72 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 工作流节点分类枚举
*/
@Getter
public enum WorkflowCategoryEnum {
// 1. 脚本执行类
SCRIPT_EXECUTION(
"脚本执行",
"用于执行各类脚本的流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL, WorkflowTriggerTypeEnum.SCHEDULED)
),
// 2. 部署类
DEPLOYMENT(
"应用部署",
"用于应用部署的流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL, WorkflowTriggerTypeEnum.SCHEDULED, WorkflowTriggerTypeEnum.APPROVAL)
),
// 3. 数据同步类
DATA_SYNC(
"数据同步",
"用于第三方系统数据同步的流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL, WorkflowTriggerTypeEnum.SCHEDULED)
),
// 4. 配置同步类
CONFIG_SYNC(
"配置同步",
"用于配置中心数据同步的流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL, WorkflowTriggerTypeEnum.APPROVAL)
),
// 5. 审批流程类
APPROVAL(
"审批流程",
"纯审批流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL)
),
// 6. 其他类型用于扩展
OTHER(
"其他",
"其他类型流程",
Arrays.asList(WorkflowTriggerTypeEnum.MANUAL)
);
private final String label; // 显示名称
private final String description; // 描述
private final List<WorkflowTriggerTypeEnum> supportedTriggers; // 支持的触发方式
WorkflowCategoryEnum(String label, String description, List<WorkflowTriggerTypeEnum> supportedTriggers) {
this.label = label;
this.description = description;
this.supportedTriggers = supportedTriggers;
}
// 判断是否支持某种触发方式
public boolean supportsTriggerType(WorkflowTriggerTypeEnum triggerType) {
return supportedTriggers.contains(triggerType);
}
}

View File

@ -0,0 +1,24 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowCategory;
import org.springframework.stereotype.Repository;
/**
* 工作流分类Repository
*
* @author qqchen
* @date 2025-10-24
*/
@Repository
public interface IWorkflowCategoryRepository extends IBaseRepository<WorkflowCategory, Long> {
/**
* 根据编码查询分类
*
* @param code 分类编码
* @return 工作流分类
*/
WorkflowCategory findByCodeAndDeletedFalse(String code);
}

View File

@ -0,0 +1,16 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowCategoryDTO;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowCategoryQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowCategory;
/**
* 工作流分类Service接口
*
* @author qqchen
* @date 2025-10-24
*/
public interface IWorkflowCategoryService extends IBaseService<WorkflowCategory, WorkflowCategoryDTO, WorkflowCategoryQuery, Long> {
}

View File

@ -9,7 +9,6 @@ import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceCreateDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO; import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowDefinitionQuery; import com.qqchen.deploy.backend.workflow.dto.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowCategoryEnum;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;

View File

@ -50,6 +50,7 @@ public class FormDataServiceImpl extends BaseServiceImpl<FormData, FormDataDTO,
FormData formData = formDataConverter.toEntity(dto); FormData formData = formDataConverter.toEntity(dto);
formData.setFormKey(formDefinition.getKey()); formData.setFormKey(formDefinition.getKey());
formData.setFormVersion(formDefinition.getFormVersion()); formData.setFormVersion(formDefinition.getFormVersion());
formData.setCategoryId(formDefinition.getCategoryId()); // 冗余存储分类ID
formData.setSchemaSnapshot(formDefinition.getSchema()); // 保存schema快照 formData.setSchemaSnapshot(formDefinition.getSchema()); // 保存schema快照
formData.setSubmitTime(LocalDateTime.now()); formData.setSubmitTime(LocalDateTime.now());
formData.setStatus(FormDataStatusEnums.SUBMITTED); formData.setStatus(FormDataStatusEnums.SUBMITTED);

View File

@ -30,6 +30,51 @@ public class FormDefinitionServiceImpl extends BaseServiceImpl<FormDefinition, F
@Resource @Resource
private FormDefinitionConverter formDefinitionConverter; private FormDefinitionConverter formDefinitionConverter;
@Override
@Transactional
public FormDefinitionDTO create(FormDefinitionDTO dto) {
log.info("创建表单定义: name={}, key={}", dto.getName(), dto.getKey());
// 1. 转换为实体
FormDefinition formDefinition = formDefinitionConverter.toEntity(dto);
// 2. 后端控制版本号和状态前端传递的值会被覆盖
formDefinition.setFormVersion(1); // 新建表单版本号固定为 1
formDefinition.setStatus(FormDefinitionStatusEnums.DRAFT); // 新建表单状态固定为草稿
// 3. 保存
FormDefinition savedForm = formDefinitionRepository.save(formDefinition);
log.info("表单定义创建成功: id={}, key={}, version={}", savedForm.getId(), savedForm.getKey(), savedForm.getFormVersion());
return formDefinitionConverter.toDto(savedForm);
}
@Override
@Transactional
public FormDefinitionDTO update(Long id, FormDefinitionDTO dto) {
log.info("更新表单定义: id={}, name={}", id, dto.getName());
// 1. 查询原始表单定义
FormDefinition existingForm = formDefinitionRepository.findById(id)
.orElseThrow(() -> new BusinessException(com.qqchen.deploy.backend.framework.enums.ResponseCode.FORM_DEFINITION_NOT_FOUND));
// 2. 保留后端控制的字段版本号和状态由后端控制不允许前端修改
Integer originalFormVersion = existingForm.getFormVersion();
FormDefinitionStatusEnums originalStatus = existingForm.getStatus();
// 3. 转换并更新允许修改的字段
FormDefinition formDefinition = formDefinitionConverter.toEntity(dto);
formDefinition.setId(id); // 使用路径参数的ID
formDefinition.setFormVersion(originalFormVersion); // 保留原始版本号
formDefinition.setStatus(originalStatus); // 保留原始状态
// 4. 保存
FormDefinition savedForm = formDefinitionRepository.save(formDefinition);
log.info("表单定义更新成功: id={}, version={}, status={}", savedForm.getId(), savedForm.getFormVersion(), savedForm.getStatus());
return formDefinitionConverter.toDto(savedForm);
}
@Override @Override
@Transactional @Transactional
public FormDefinitionDTO publish(Long id) { public FormDefinitionDTO publish(Long id) {

View File

@ -0,0 +1,21 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.dto.WorkflowCategoryDTO;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowCategoryQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowCategory;
import com.qqchen.deploy.backend.workflow.service.IWorkflowCategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 工作流分类Service实现
*
* @author qqchen
* @date 2025-10-24
*/
@Slf4j
@Service
public class WorkflowCategoryServiceImpl extends BaseServiceImpl<WorkflowCategory, WorkflowCategoryDTO, WorkflowCategoryQuery, Long> implements IWorkflowCategoryService {
}

View File

@ -8,7 +8,6 @@ import com.qqchen.deploy.backend.workflow.dto.WorkflowTriggerTypeDTO;
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.dto.query.WorkflowDefinitionQuery; import com.qqchen.deploy.backend.workflow.dto.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowCategoryEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums; import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums; import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
@ -68,6 +67,11 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
@Resource @Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository; private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private com.qqchen.deploy.backend.workflow.repository.IWorkflowCategoryRepository workflowCategoryRepository;
@Resource
private com.qqchen.deploy.backend.workflow.converter.WorkflowCategoryConverter workflowCategoryConverter;
@Resource @Resource
private BpmnConverter bpmnConverter; private BpmnConverter bpmnConverter;
@ -337,25 +341,11 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
@Override @Override
public List<WorkflowCategoryDTO> getWorkflowCategories() { public List<WorkflowCategoryDTO> getWorkflowCategories() {
return Arrays.stream(WorkflowCategoryEnum.values()) // 从数据库查询启用的工作流分类按排序字段排序
.map(category -> { return workflowCategoryRepository.findAll().stream()
WorkflowCategoryDTO dto = new WorkflowCategoryDTO(); .filter(category -> category.getEnabled() && !category.getDeleted())
dto.setCode(category.name()); .sorted((a, b) -> a.getSort().compareTo(b.getSort()))
dto.setLable(category.getLabel()); .map(workflowCategoryConverter::toDto)
dto.setDescription(category.getDescription());
// 获取该类别支持的触发方式
dto.setSupportedTriggers(
category.getSupportedTriggers().stream()
.map(triggerType -> {
WorkflowTriggerTypeDTO triggerDto = new WorkflowTriggerTypeDTO();
triggerDto.setCode(triggerType.name());
triggerDto.setLable(triggerType.getLabel());
return triggerDto;
})
.collect(Collectors.toList())
);
return dto;
})
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@ -219,10 +219,21 @@ INSERT INTO sys_external_system (
-- -------------------------------------------------------------------------------------- -- --------------------------------------------------------------------------------------
-- 初始化工作流相关数据 -- 初始化工作流相关数据
-- -------------------------------------------------------------------------------------- -- --------------------------------------------------------------------------------------
-- 工作流分类初始数据
INSERT INTO workflow_category (name, code, description, icon, sort, supported_triggers, enabled, create_by, create_time, update_by, update_time, version, deleted)
VALUES
('脚本执行', 'SCRIPT_EXECUTION', '用于执行各类脚本的流程', 'CodeOutlined', 1, '["MANUAL","SCHEDULED"]', 1, 'system', NOW(), 'system', NOW(), 0, 0),
('应用部署', 'DEPLOYMENT', '用于应用部署的流程', 'DeploymentUnitOutlined', 2, '["MANUAL","SCHEDULED","APPROVAL"]', 1, 'system', NOW(), 'system', NOW(), 0, 0),
('数据同步', 'DATA_SYNC', '用于第三方系统数据同步的流程', 'SyncOutlined', 3, '["MANUAL","SCHEDULED"]', 1, 'system', NOW(), 'system', NOW(), 0, 0),
('配置同步', 'CONFIG_SYNC', '用于配置中心数据同步的流程', 'SettingOutlined', 4, '["MANUAL","APPROVAL"]', 1, 'system', NOW(), 'system', NOW(), 0, 0),
('审批流程', 'APPROVAL', '纯审批流程', 'AuditOutlined', 5, '["MANUAL"]', 1, 'system', NOW(), 'system', NOW(), 0, 0),
('其他', 'OTHER', '其他类型流程', 'AppstoreOutlined', 99, '["MANUAL"]', 1, 'system', NOW(), 'system', NOW(), 0, 0);
-- 工作流定义测试数据 -- 工作流定义测试数据
INSERT INTO workflow_definition ( INSERT INTO workflow_definition (
-- 基础信息 -- 基础信息
name, `key`, process_definition_id, flow_version, description, category, triggers, name, `key`, process_definition_id, flow_version, description, category_id, triggers,
-- 流程配置 -- 流程配置
graph, tags, graph, tags,
-- 流程属性 -- 流程属性
@ -232,7 +243,7 @@ INSERT INTO workflow_definition (
) VALUES ) VALUES
-- 简单脚本流程:开始 -> 脚本任务 -> 结束 -- 简单脚本流程:开始 -> 脚本任务 -> 结束
( (
'简单脚本流程', 'simple_script_flow', null, 1, '一个包含脚本任务的简单流程', 'SCRIPT_EXECUTION', null, '简单脚本流程', 'simple_script_flow', null, 1, '一个包含脚本任务的简单流程', 1, null,
'{ '{
"nodes" : [ { "nodes" : [ {
"id" : "startEvent1", "id" : "startEvent1",
@ -400,7 +411,7 @@ INSERT INTO workflow_definition (
-- 复杂业务流程:开始 -> 脚本任务A -> 脚本任务B -> 结束 -- 复杂业务流程:开始 -> 脚本任务A -> 脚本任务B -> 结束
( (
'复杂业务流程', 'complex_business_flow', null, 1, '包含多个脚本任务节点的业务流程', 'SCRIPT_EXECUTION', null, '复杂业务流程', 'complex_business_flow', null, 1, '包含多个脚本任务节点的业务流程', 1, null,
'{ '{
"nodes" : [ { "nodes" : [ {
"id" : "startEvent1", "id" : "startEvent1",

View File

@ -371,6 +371,41 @@ CREATE TABLE deploy_repo_branch
-- 工作流相关表 -- 工作流相关表
-- -------------------------------------------------------------------------------------- -- --------------------------------------------------------------------------------------
-- 工作流分类表
CREATE TABLE workflow_category
(
-- 主键
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
-- 基础信息
name VARCHAR(100) NOT NULL COMMENT '分类名称',
code VARCHAR(50) NOT NULL COMMENT '分类编码唯一如SCRIPT_EXECUTION',
description VARCHAR(500) NULL COMMENT '分类描述',
icon VARCHAR(50) NULL COMMENT '图标',
sort INT NOT NULL DEFAULT 0 COMMENT '排序',
-- 支持的触发方式JSON数组如["MANUAL","SCHEDULED"]
supported_triggers JSON NULL COMMENT '支持的触发方式列表',
-- 状态
enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用',
-- 审计字段
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
-- 约束和索引
UNIQUE KEY uk_code (code),
INDEX idx_enabled (enabled),
INDEX idx_deleted (deleted),
INDEX idx_sort (sort)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci
COMMENT = '工作流分类表';
-- 工作流定义表 -- 工作流定义表
CREATE TABLE workflow_definition CREATE TABLE workflow_definition
( (
@ -380,8 +415,8 @@ CREATE TABLE workflow_definition
-- 基础信息 -- 基础信息
name VARCHAR(255) NOT NULL COMMENT '流程名称', name VARCHAR(255) NOT NULL COMMENT '流程名称',
`key` VARCHAR(255) NOT NULL COMMENT '流程标识', `key` VARCHAR(255) NOT NULL COMMENT '流程标识',
category VARCHAR(100) COMMENT '流程分类', category_id BIGINT NULL COMMENT '流程分类ID外键关联workflow_category',
triggers VARCHAR(200) COMMENT '流程分类', triggers JSON NULL COMMENT '触发器配置JSON数组',
process_definition_id VARCHAR(100) NULL COMMENT '工作流定义ID', process_definition_id VARCHAR(100) NULL COMMENT '工作流定义ID',
flow_version INT NOT NULL COMMENT '流程版本', flow_version INT NOT NULL COMMENT '流程版本',
description TEXT COMMENT '流程描述', description TEXT COMMENT '流程描述',
@ -402,8 +437,12 @@ CREATE TABLE workflow_definition
update_time DATETIME(6) NULL COMMENT '更新时间', update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
-- 约束 -- 约束和索引
UNIQUE KEY uk_key_version (`key`, flow_version) UNIQUE KEY uk_key_version (`key`, flow_version),
INDEX idx_category_id (category_id),
INDEX idx_status (status),
INDEX idx_deleted (deleted),
CONSTRAINT fk_workflow_definition_category FOREIGN KEY (category_id) REFERENCES workflow_category(id)
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT ='工作流定义表'; COLLATE = utf8mb4_unicode_ci COMMENT ='工作流定义表';
@ -776,6 +815,7 @@ CREATE TABLE form_data
form_definition_id BIGINT NOT NULL COMMENT '表单定义ID', form_definition_id BIGINT NOT NULL COMMENT '表单定义ID',
form_key VARCHAR(255) NOT NULL COMMENT '表单标识冗余存储避免JOIN', form_key VARCHAR(255) NOT NULL COMMENT '表单标识冗余存储避免JOIN',
form_version INT NOT NULL COMMENT '表单版本(冗余存储,用于历史追溯)', form_version INT NOT NULL COMMENT '表单版本(冗余存储,用于历史追溯)',
category_id BIGINT NULL COMMENT '表单分类ID冗余存储便于统计和查询',
-- 业务关联(松耦合) -- 业务关联(松耦合)
business_key VARCHAR(255) NULL COMMENT '业务标识如工作流实例ID、订单号等', business_key VARCHAR(255) NULL COMMENT '业务标识如工作流实例ID、订单号等',
@ -803,6 +843,7 @@ CREATE TABLE form_data
-- 索引 -- 索引
INDEX idx_form_definition_id (form_definition_id), INDEX idx_form_definition_id (form_definition_id),
INDEX idx_form_key (form_key), INDEX idx_form_key (form_key),
INDEX idx_category_id (category_id),
INDEX idx_business_key (business_key), INDEX idx_business_key (business_key),
INDEX idx_business_type (business_type), INDEX idx_business_type (business_type),
INDEX idx_submitter (submitter), INDEX idx_submitter (submitter),