linqilong 7 місяців тому
батько
коміт
1e0c0d9c01
41 змінених файлів з 1764 додано та 7 видалено
  1. 8 0
      pom.xml
  2. 2 2
      ruoyi-admin/src/main/resources/application-druid.yml
  3. 1 1
      ruoyi-admin/src/main/resources/application-test.yml
  4. 45 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelStepController.java
  5. 45 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelStepExecutionController.java
  6. 45 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelWorkflowController.java
  7. 46 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelWorkflowExecutionController.java
  8. 39 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/WorkflowController.java
  9. 45 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelStep.java
  10. 104 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelStepExecution.java
  11. 44 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelWorkflow.java
  12. 47 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelWorkflowExecution.java
  13. 11 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/exception/StepExecutionException.java
  14. 11 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelStepExecutionMapper.java
  15. 11 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelStepMapper.java
  16. 11 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelWorkflowExecutionMapper.java
  17. 11 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelWorkflowMapper.java
  18. 13 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelStepExecutionService.java
  19. 16 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelStepService.java
  20. 13 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelWorkflowExecutionService.java
  21. 13 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelWorkflowService.java
  22. 30 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelStepExecutionServiceImpl.java
  23. 40 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelStepServiceImpl.java
  24. 30 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelWorkflowExecutionServiceImpl.java
  25. 30 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelWorkflowServiceImpl.java
  26. 63 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutionContext.java
  27. 110 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutionResult.java
  28. 75 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutorFactory.java
  29. 157 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/WorkflowEngine.java
  30. 39 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/WorkflowUtils.java
  31. 112 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/ApiStepExecutor.java
  32. 21 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/ConditionStepExecutor.java
  33. 49 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/DbStepExecutor.java
  34. 29 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/StepExecutor.java
  35. 9 0
      ruoyi-common/pom.xml
  36. 47 0
      ruoyi-common/src/main/java/com/ruoyi/common/enums/DataType.java
  37. 139 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpRequestUtil.java
  38. 99 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/StringDataTypeUtils.java
  39. 99 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/Xml2JsonUtils.java
  40. 1 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
  41. 4 4
      ruoyi-ui/src/views/service/info/index.vue

+ 8 - 0
pom.xml

@@ -32,6 +32,7 @@
         <poi.version>4.1.2</poi.version>
         <velocity.version>2.3</velocity.version>
         <jwt.version>0.9.1</jwt.version>
+        <dom4j.version>2.1.4</dom4j.version>
     </properties>
 
     <!-- 依赖声明 -->
@@ -151,6 +152,13 @@
                 <version>${fastjson.version}</version>
             </dependency>
 
+            <!-- xml解析工具类 -->
+            <dependency>
+                <groupId>org.dom4j</groupId>
+                <artifactId>dom4j</artifactId>
+                <version>${dom4j.version}</version>
+            </dependency>
+
             <!-- Token生成与解析-->
             <dependency>
                 <groupId>io.jsonwebtoken</groupId>

+ 2 - 2
ruoyi-admin/src/main/resources/application-druid.yml

@@ -13,7 +13,7 @@ spring:
       slave:
         # 从数据源开关/默认关闭
         enabled: false
-        url: jdbc:mysql://192.168.0.146:3306/sh_project?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        url: jdbc:mysql://39.98.38.2:13306/sh_project?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
         username: zj-hhst
         password: ENC(10zA4Za2Kj2pvnRnLDYCWWkP71Dcsavw)
       # 初始连接数
@@ -80,4 +80,4 @@ spring:
         # 连接池的最大数据库连接数
         max-active: 8
         # #连接池最大阻塞等待时间(使用负值表示没有限制)
-        max-wait: -1ms
+        max-wait: -1ms

+ 1 - 1
ruoyi-admin/src/main/resources/application-test.yml

@@ -83,4 +83,4 @@ spring:
         # 连接池的最大数据库连接数
         max-active: 8
         # #连接池最大阻塞等待时间(使用负值表示没有限制)
-        max-wait: -1ms
+        max-wait: -1ms

+ 45 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelStepController.java

@@ -0,0 +1,45 @@
+package com.ruoyi.interfaces.controller;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.service.MdModelStepService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Api(value = "步骤表", tags = "步骤表")
+@RestController
+@RequestMapping("/md/model/step")
+public class MdModelStepController extends BaseController {
+
+    @Autowired
+    private MdModelStepService mdModelStepService;
+
+
+    @ApiOperation(value = "新增/修改步骤表")
+    @PostMapping("")
+    public AjaxResult save(@ApiParam(name = "mdModelStep", value = "MdModelStep", required = true) @RequestBody MdModelStep mdModelStep) {
+        return AjaxResult.success(mdModelStepService.saveOrUpdate(mdModelStep));
+    }
+
+    @ApiOperation(value = "根据ID删除步骤表")
+    @DeleteMapping(value = "/{id}")
+    public AjaxResult delete(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelStepService.removeById(id));
+    }
+
+    @ApiOperation(value = "根据ID获取步骤表(单表)")
+    @GetMapping("/{id}")
+    public AjaxResult get(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelStepService.getById(id));
+    }
+
+
+}

+ 45 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelStepExecutionController.java

@@ -0,0 +1,45 @@
+package com.ruoyi.interfaces.controller;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.service.MdModelStepExecutionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Api(value = "步骤执行记录", tags = "步骤执行记录")
+@RestController
+@RequestMapping("/md/model/step/execution")
+public class MdModelStepExecutionController extends BaseController {
+
+    @Autowired
+    private MdModelStepExecutionService mdModelStepExecutionService;
+
+
+    @ApiOperation(value = "新增/修改步骤执行记录")
+    @PostMapping("")
+    public AjaxResult save(@ApiParam(name = "mdModelStepExecution", value = "MdModelStepExecution", required = true) @RequestBody MdModelStepExecution mdModelStepExecution) {
+        return AjaxResult.success(mdModelStepExecutionService.saveOrUpdate(mdModelStepExecution));
+    }
+
+    @ApiOperation(value = "根据ID删除步骤执行记录")
+    @DeleteMapping(value = "/{id}")
+    public AjaxResult delete(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelStepExecutionService.removeById(id));
+    }
+
+    @ApiOperation(value = "根据ID获取步骤执行记录(单表)")
+    @GetMapping("/{id}")
+    public AjaxResult get(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelStepExecutionService.getById(id));
+    }
+
+
+}

+ 45 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelWorkflowController.java

@@ -0,0 +1,45 @@
+package com.ruoyi.interfaces.controller;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.interfaces.domain.MdModelWorkflow;
+import com.ruoyi.interfaces.service.MdModelWorkflowService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Api(value = "工作流", tags = "工作流")
+@RestController
+@RequestMapping("/md/model/workflow")
+public class MdModelWorkflowController extends BaseController {
+
+    @Autowired
+    private MdModelWorkflowService mdModelWorkflowService;
+
+
+    @ApiOperation(value = "新增/修改工作流")
+    @PostMapping("")
+    public AjaxResult save(@ApiParam(name = "mdModelWorkflow", value = "MdModelWorkflow", required = true) @RequestBody MdModelWorkflow mdModelWorkflow) {
+        return AjaxResult.success(mdModelWorkflowService.saveOrUpdate(mdModelWorkflow));
+    }
+
+    @ApiOperation(value = "根据ID删除工作流")
+    @DeleteMapping(value = "/{id}")
+    public AjaxResult delete(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelWorkflowService.removeById(id));
+    }
+
+    @ApiOperation(value = "根据ID获取工作流(单表)")
+    @GetMapping("/{id}")
+    public AjaxResult get(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelWorkflowService.getById(id));
+    }
+
+
+}

+ 46 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdModelWorkflowExecutionController.java

@@ -0,0 +1,46 @@
+package com.ruoyi.interfaces.controller;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.interfaces.domain.MdModelWorkflowExecution;
+import com.ruoyi.interfaces.service.MdModelWorkflowExecutionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Api(value = "工作流执行记录", tags = "工作流执行记录")
+@RestController
+@RequestMapping("/md/model/workflow/execution")
+public class MdModelWorkflowExecutionController extends BaseController {
+
+    @Autowired
+    private MdModelWorkflowExecutionService mdModelWorkflowExecutionService;
+
+
+    @ApiOperation(value = "新增/修改工作流执行记录")
+    @PostMapping("")
+    public AjaxResult save(@ApiParam(name = "mdModelWorkflowExecution", value = "MdModelWorkflowExecution", required = true)
+                           @RequestBody MdModelWorkflowExecution mdModelWorkflowExecution) {
+        return AjaxResult.success(mdModelWorkflowExecutionService.saveOrUpdate(mdModelWorkflowExecution));
+    }
+
+    @ApiOperation(value = "根据ID删除工作流执行记录")
+    @DeleteMapping(value = "/{id}")
+    public AjaxResult delete(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelWorkflowExecutionService.removeById(id));
+    }
+
+    @ApiOperation(value = "根据ID获取工作流执行记录(单表)")
+    @GetMapping("/{id}")
+    public AjaxResult get(@ApiParam(name = "id", value = "id", required = true) @PathVariable String id) {
+        return AjaxResult.success(mdModelWorkflowExecutionService.getById(id));
+    }
+
+
+}

+ 39 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/WorkflowController.java

@@ -0,0 +1,39 @@
+package com.ruoyi.interfaces.controller;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.interfaces.workflow.ExecutionResult;
+import com.ruoyi.interfaces.workflow.WorkflowEngine;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/16 10:08
+ */
+@RestController
+@RequestMapping("/workflows")
+public class WorkflowController extends BaseController {
+
+    @Autowired
+    private WorkflowEngine engine;
+
+    @PostMapping("/{id}/execute")
+    public AjaxResult executeWorkflow(@PathVariable Long id, @RequestBody Map<String, Object> input) {
+        ExecutionResult result = engine.execute(id, input);
+        return AjaxResult.success(result);
+    }
+
+    /**
+     * 获取历史执行记录
+     * @param id
+     * @return
+     */
+    @GetMapping("/{id}/executions")
+    public AjaxResult getExecutions(@PathVariable Long id) {
+        return AjaxResult.success();
+    }
+
+}

+ 45 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelStep.java

@@ -0,0 +1,45 @@
+package com.ruoyi.interfaces.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * entity:MdModelStep
+ *
+ * @author lql
+ * @date 2025-7-15
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName
+public class MdModelStep implements Serializable {
+    public enum StepType {API, TRANSFORM, CONDITION, WAIT, DB}
+
+    public enum ErrorPolicy {ABORT, IGNORE, RETRY}
+
+    @TableId
+    private Long id;
+    @ApiModelProperty("工作流ID")
+    private Long workflowId;
+    @ApiModelProperty("步骤名称")
+    private String name;
+    @ApiModelProperty("步骤类型")
+    private StepType type;
+    @ApiModelProperty("步骤配置(JSON格式)")
+    private String config;
+    @ApiModelProperty("执行顺序")
+    private Integer sequence;
+    private ErrorPolicy errorPolicy;
+    @ApiModelProperty("重试次数")
+    private Integer retryCount;
+    @ApiModelProperty("输出映射(JSON格式)")
+    private String outputMapping;
+
+}

+ 104 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelStepExecution.java

@@ -0,0 +1,104 @@
+package com.ruoyi.interfaces.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.utils.JsonUtils;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * entity:MdModelStepExecution
+ *
+ * @author lql
+ * @date 2025-7-15
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName
+public class MdModelStepExecution implements Serializable {
+
+    // 步骤执行状态
+    public enum StepStatus {
+        PENDING,      // 待执行
+        RUNNING,      // 执行中
+        SUCCESS,      // 成功
+        FAILED,       // 失败
+        SKIPPED,      // 已跳过
+        RETRYING      // 重试中
+    }
+
+    @TableId
+    private Long id;
+    private Long executionId;
+    private Long stepId;
+    private String stepName;         // 步骤名称
+    private MdModelStep.StepType stepType;  // 步骤类型
+    @ApiModelProperty("状态")
+    private StepStatus status;
+    @ApiModelProperty("步骤输入")
+    private String input;
+    @ApiModelProperty("步骤输出")
+    private String output;
+    @ApiModelProperty("错误信息")
+    private String errorMsg;
+    private Date executionTime;
+    private int retryCount = 0;      // 重试次数
+    private Date startTime;          // 开始时间
+    private Date endTime;            // 结束时间
+    private long duration;           // 耗时(毫秒)
+
+    // 标记步骤开始
+    public void markStart() {
+        this.status = StepStatus.RUNNING;
+        this.startTime = new Date();
+    }
+
+    // 标记步骤成功
+    public void markSuccess(Map<String, Object> output) {
+        this.status = StepStatus.SUCCESS;
+        this.output = JsonUtils.objectToJson(output);
+        this.endTime = new Date();
+        this.duration = endTime.getTime() - startTime.getTime();
+    }
+
+    // 标记步骤失败
+    public void markFailed(String errorMsg) {
+        this.status = StepStatus.FAILED;
+        this.errorMsg = errorMsg;
+        this.endTime = new Date();
+        this.duration = endTime.getTime() - startTime.getTime();
+    }
+
+    // 标记步骤跳过
+    public void markSkipped() {
+        this.status = StepStatus.SKIPPED;
+        this.endTime = new Date();
+        if (startTime != null) {
+            this.duration = endTime.getTime() - startTime.getTime();
+        }
+    }
+
+    // 增加重试次数
+    public void increaseRetryCount() {
+        this.retryCount++;
+        this.status = StepStatus.RETRYING;
+    }
+
+    // 是否成功
+    public boolean isSuccess() {
+        return StepStatus.SUCCESS.equals(status);
+    }
+
+    // 是否失败
+    public boolean isFailed() {
+        return StepStatus.FAILED.equals(status);
+    }
+
+}

+ 44 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelWorkflow.java

@@ -0,0 +1,44 @@
+package com.ruoyi.interfaces.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * entity:MdModelWorkflow
+ *
+ * @author lql
+ * @date 2025-7-15
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName
+public class MdModelWorkflow implements Serializable {
+
+    @TableId
+    private Integer id;
+    @ApiModelProperty("工作流名称")
+    private String name;
+    @ApiModelProperty("描述")
+    private String description;
+    @ApiModelProperty("状态:1启用,0禁用")
+    private Integer status;
+    @ApiModelProperty("输入参数模板")
+    private String inputTemplate;
+    private Date createTime;
+    private Date updateTime;
+
+    @TableField(exist = false)
+    @ApiModelProperty("步骤列表")
+    private List<MdModelStep> steps;
+
+}

+ 47 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdModelWorkflowExecution.java

@@ -0,0 +1,47 @@
+package com.ruoyi.interfaces.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * entity:MdModelWorkflowExecution
+ *
+ * @author lql
+ * @date 2025-7-15
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName
+public class MdModelWorkflowExecution implements Serializable {
+
+    public enum ExecutionStatus {
+        PENDING, RUNNING, SUCCESS, FAILED, SKIPPED, RETRYING
+    }
+
+    @TableId
+    private Long id;
+    private Long workflowId;
+    private ExecutionStatus status;
+    @ApiModelProperty("执行输入")
+    private String input;
+    @ApiModelProperty("最终输出")
+    private String output;
+    @ApiModelProperty("执行上下文")
+    private String context;
+    private Date startTime;
+    private Date endTime;
+
+    @TableField(exist = false)
+    @ApiModelProperty("步骤列表")
+    private List<MdModelStepExecution> stepExecutions;
+}

+ 11 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/exception/StepExecutionException.java

@@ -0,0 +1,11 @@
+package com.ruoyi.interfaces.exception;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/17 10:55
+ */
+public class StepExecutionException extends RuntimeException {
+    public StepExecutionException(String message) {
+        super(message);
+    }
+}

+ 11 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelStepExecutionMapper.java

@@ -0,0 +1,11 @@
+package com.ruoyi.interfaces.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MdModelStepExecutionMapper extends BaseMapper<MdModelStepExecution> {
+
+
+}

+ 11 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelStepMapper.java

@@ -0,0 +1,11 @@
+package com.ruoyi.interfaces.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MdModelStepMapper extends BaseMapper<MdModelStep> {
+
+
+}

+ 11 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelWorkflowExecutionMapper.java

@@ -0,0 +1,11 @@
+package com.ruoyi.interfaces.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.interfaces.domain.MdModelWorkflowExecution;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MdModelWorkflowExecutionMapper extends BaseMapper<MdModelWorkflowExecution> {
+
+
+}

+ 11 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdModelWorkflowMapper.java

@@ -0,0 +1,11 @@
+package com.ruoyi.interfaces.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.interfaces.domain.MdModelWorkflow;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MdModelWorkflowMapper extends BaseMapper<MdModelWorkflow> {
+
+
+}

+ 13 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelStepExecutionService.java

@@ -0,0 +1,13 @@
+package com.ruoyi.interfaces.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+public interface MdModelStepExecutionService extends IService<MdModelStepExecution> {
+
+
+}

+ 16 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelStepService.java

@@ -0,0 +1,16 @@
+package com.ruoyi.interfaces.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.interfaces.domain.MdModelStep;
+
+import java.util.List;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+public interface MdModelStepService extends IService<MdModelStep> {
+
+    List<MdModelStep> listByWorkflowIdOrderBySequence(Long workflowId);
+
+}

+ 13 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelWorkflowExecutionService.java

@@ -0,0 +1,13 @@
+package com.ruoyi.interfaces.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.interfaces.domain.MdModelWorkflowExecution;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+public interface MdModelWorkflowExecutionService extends IService<MdModelWorkflowExecution> {
+
+
+}

+ 13 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/MdModelWorkflowService.java

@@ -0,0 +1,13 @@
+package com.ruoyi.interfaces.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.interfaces.domain.MdModelWorkflow;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+public interface MdModelWorkflowService extends IService<MdModelWorkflow> {
+
+
+}

+ 30 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelStepExecutionServiceImpl.java

@@ -0,0 +1,30 @@
+package com.ruoyi.interfaces.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.mapper.MdModelStepExecutionMapper;
+import com.ruoyi.interfaces.service.MdModelStepExecutionService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Slf4j
+@Service
+@Transactional
+public class MdModelStepExecutionServiceImpl extends ServiceImpl<MdModelStepExecutionMapper, MdModelStepExecution> implements MdModelStepExecutionService {
+
+    @Override
+    public boolean save(MdModelStepExecution mdModelStepExecution) {
+        return super.save(mdModelStepExecution);
+    }
+
+    @Override
+    public boolean updateById(MdModelStepExecution mdModelStepExecution) {
+        return super.updateById(mdModelStepExecution);
+    }
+
+}

+ 40 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelStepServiceImpl.java

@@ -0,0 +1,40 @@
+package com.ruoyi.interfaces.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.mapper.MdModelStepMapper;
+import com.ruoyi.interfaces.service.MdModelStepService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Slf4j
+@Service
+@Transactional
+public class MdModelStepServiceImpl extends ServiceImpl<MdModelStepMapper, MdModelStep> implements MdModelStepService {
+
+    @Override
+    public boolean save(MdModelStep mdModelStep) {
+        return super.save(mdModelStep);
+    }
+
+    @Override
+    public boolean updateById(MdModelStep mdModelStep) {
+        return super.updateById(mdModelStep);
+    }
+
+    @Override
+    public List<MdModelStep> listByWorkflowIdOrderBySequence(Long workflowId) {
+        QueryWrapper<MdModelStep> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("workflow_id", workflowId)
+                .orderByAsc("sequence");
+        return list(queryWrapper);
+    }
+}

+ 30 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelWorkflowExecutionServiceImpl.java

@@ -0,0 +1,30 @@
+package com.ruoyi.interfaces.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.interfaces.domain.MdModelWorkflowExecution;
+import com.ruoyi.interfaces.mapper.MdModelWorkflowExecutionMapper;
+import com.ruoyi.interfaces.service.MdModelWorkflowExecutionService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Slf4j
+@Service
+@Transactional
+public class MdModelWorkflowExecutionServiceImpl extends ServiceImpl<MdModelWorkflowExecutionMapper, MdModelWorkflowExecution> implements MdModelWorkflowExecutionService {
+
+    @Override
+    public boolean save(MdModelWorkflowExecution mdModelWorkflowExecution) {
+        return super.save(mdModelWorkflowExecution);
+    }
+
+    @Override
+    public boolean updateById(MdModelWorkflowExecution mdModelWorkflowExecution) {
+        return super.updateById(mdModelWorkflowExecution);
+    }
+
+}

+ 30 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelWorkflowServiceImpl.java

@@ -0,0 +1,30 @@
+package com.ruoyi.interfaces.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.interfaces.domain.MdModelWorkflow;
+import com.ruoyi.interfaces.mapper.MdModelWorkflowMapper;
+import com.ruoyi.interfaces.service.MdModelWorkflowService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author lql
+ * @date 2025-7-15
+ */
+@Slf4j
+@Service
+@Transactional
+public class MdModelWorkflowServiceImpl extends ServiceImpl<MdModelWorkflowMapper, MdModelWorkflow> implements MdModelWorkflowService {
+
+    @Override
+    public boolean save(MdModelWorkflow mdModelWorkflow) {
+        return super.save(mdModelWorkflow);
+    }
+
+    @Override
+    public boolean updateById(MdModelWorkflow mdModelWorkflow) {
+        return super.updateById(mdModelWorkflow);
+    }
+
+}

+ 63 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutionContext.java

@@ -0,0 +1,63 @@
+package com.ruoyi.interfaces.workflow;
+
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/15 16:16
+ */
+public class ExecutionContext {
+    private Map<String, Object> input;
+    private Map<String, Object> contextData = new HashMap<>();
+    private Map<String, Map<String, Object>> stepOutputs = new HashMap<>();
+
+    public ExecutionContext(Map<String, Object> input) {
+        this.input = input;
+        // 初始化时将输入数据放入上下文
+        contextData.put("input", input);
+    }
+
+    // 设置步骤输出(按步骤名称存储)
+    public void setStepOutput(String stepName, Map<String, Object> output) {
+        stepOutputs.put(stepName, output);
+        // 同时将输出数据放入顶层上下文
+        contextData.put(stepName, output);
+    }
+
+    // 将特定值放入上下文
+    public void setContextValue(String key, Object value) {
+        contextData.put(key, value);
+    }
+
+    // 获取完整的上下文数据
+    public Map<String, Object> getAllContextData() {
+        return contextData;
+    }
+
+    // 使用SpEL表达式渲染模板
+    public String renderTemplate(String template) {
+        ExpressionParser parser = new SpelExpressionParser();
+        return parser.parseExpression(template, new TemplateParserContext())
+                .getValue(this.contextData, String.class);
+    }
+
+    // 使用SpEL解析JSON对象
+    public Map<String, Object> parseTemplate(Map<String, Object> template) {
+        Map<String, Object> result = new HashMap<>();
+        template.forEach((key, value) -> {
+            if (value instanceof String) {
+                result.put(key, renderTemplate((String) value));
+            } else if (value instanceof Map) {
+                result.put(key, parseTemplate((Map<String, Object>) value));
+            } else {
+                result.put(key, value);
+            }
+        });
+        return result;
+    }
+}

+ 110 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutionResult.java

@@ -0,0 +1,110 @@
+package com.ruoyi.interfaces.workflow;
+
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工作流执行结果
+ */
+@Data
+public class ExecutionResult {
+
+    // 执行状态枚举
+    public enum ExecutionStatus {
+        RUNNING,      // 执行中
+        SUCCESS,      // 成功
+        FAILED,       // 失败
+        TIMEOUT,      // 超时
+        PARTIAL_SUCCESS // 部分成功
+    }
+
+    private String executionId;          // 执行ID
+    private ExecutionStatus status;      // 整体执行状态
+    private String workflowId;           // 工作流ID
+    private Map<String, Object> input;   // 输入数据
+    private Map<String, Object> output;  // 最终输出数据
+    private Map<String, Object> context; // 执行上下文(全部数据快照)
+    private List<MdModelStepExecution> stepExecutions = new ArrayList<>(); // 步骤执行记录
+    private String errorMessage;         // 错误信息(如整体失败)
+    private long startTime;              // 开始时间(毫秒时间戳)
+    private long endTime;                // 结束时间(毫秒时间戳)
+
+    public ExecutionResult(String workflowId, Map<String, Object> input) {
+        this.workflowId = workflowId;
+        this.input = input;
+        this.status = ExecutionStatus.RUNNING;
+        this.context = new HashMap<>();
+        this.startTime = System.currentTimeMillis();
+    }
+
+    // 添加步骤执行记录
+    public void addStepExecution(MdModelStepExecution stepExecution) {
+        stepExecutions.add(stepExecution);
+    }
+
+    // 标记执行成功
+    public void markSuccess(Map<String, Object> output) {
+        this.status = ExecutionStatus.SUCCESS;
+        this.output = output;
+        this.endTime = System.currentTimeMillis();
+    }
+
+    // 标记执行失败
+    public void markFailed(String errorMessage) {
+        this.status = ExecutionStatus.FAILED;
+        this.errorMessage = errorMessage;
+        this.endTime = System.currentTimeMillis();
+    }
+
+    // 标记部分成功(有条件的分支成功)
+    public void markPartialSuccess(Map<String, Object> output, String reason) {
+        this.status = ExecutionStatus.PARTIAL_SUCCESS;
+        this.output = output;
+        this.errorMessage = reason;
+        this.endTime = System.currentTimeMillis();
+    }
+
+    // 计算总耗时(毫秒)
+    public long getTotalDuration() {
+        if (endTime > startTime) {
+            return endTime - startTime;
+        }
+        return System.currentTimeMillis() - startTime;
+    }
+
+    // 获取某个步骤的执行记录
+    public MdModelStepExecution getStepExecution(String stepName) {
+        return stepExecutions.stream()
+                .filter(se -> stepName.equals(se.getStepName()))
+                .findFirst()
+                .orElse(null);
+    }
+
+    // Getter & Setter 方法
+    public String getExecutionId() { return executionId; }
+    public void setExecutionId(String executionId) { this.executionId = executionId; }
+
+    public ExecutionStatus getStatus() { return status; }
+
+    public String getWorkflowId() { return workflowId; }
+
+    public Map<String, Object> getInput() { return input; }
+
+    public Map<String, Object> getOutput() { return output; }
+
+    public Map<String, Object> getContext() { return context; }
+
+    public List<MdModelStepExecution> getStepExecutions() { return stepExecutions; }
+
+    public String getErrorMessage() { return errorMessage; }
+
+    public long getStartTime() { return startTime; }
+
+    public long getEndTime() { return endTime; }
+}
+

+ 75 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/ExecutorFactory.java

@@ -0,0 +1,75 @@
+package com.ruoyi.interfaces.workflow;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/15 16:48
+ */
+
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.workflow.executor.ApiStepExecutor;
+import com.ruoyi.interfaces.workflow.executor.ConditionStepExecutor;
+import com.ruoyi.interfaces.workflow.executor.StepExecutor;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 步骤执行器工厂
+ */
+@Component
+public class ExecutorFactory implements ApplicationContextAware {
+
+    // 步骤类型到执行器的映射
+    private final Map<MdModelStep.StepType, StepExecutor> executorMap = new HashMap<>();
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        // 从Spring容器中获取所有StepExecutor实现
+        Map<String, StepExecutor> executors = applicationContext.getBeansOfType(StepExecutor.class);
+
+        // 建立步骤类型到执行器的映射
+        for (StepExecutor executor : executors.values()) {
+            MdModelStep.StepType supportedType = executor.getSupportedStepType();
+            if (supportedType != null) {
+                executorMap.put(supportedType, executor);
+            }
+        }
+    }
+
+    /**
+     * 根据步骤类型获取执行器
+     *
+     * @param stepType 步骤类型
+     * @return 对应的执行器实例
+     */
+    public StepExecutor getExecutor(MdModelStep.StepType stepType) {
+        StepExecutor executor = executorMap.get(stepType);
+        if (executor == null) {
+            throw new UnsupportedOperationException("不支持的步骤类型: " + stepType);
+        }
+        return executor;
+    }
+
+    /**
+     * 获取API执行器
+     *
+     * @return API执行器实例
+     */
+    public ApiStepExecutor getApiExecutor() {
+        return (ApiStepExecutor) getExecutor(MdModelStep.StepType.API);
+    }
+
+    /**
+     * 获取条件分支执行器
+     *
+     * @return 条件分支执行器实例
+     */
+    public ConditionStepExecutor getConditionExecutor() {
+        return (ConditionStepExecutor) getExecutor(MdModelStep.StepType.CONDITION);
+    }
+
+}

+ 157 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/WorkflowEngine.java

@@ -0,0 +1,157 @@
+package com.ruoyi.interfaces.workflow;
+
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.domain.MdModelWorkflowExecution;
+import com.ruoyi.interfaces.exception.StepExecutionException;
+import com.ruoyi.interfaces.service.MdModelStepService;
+import com.ruoyi.interfaces.service.MdModelWorkflowExecutionService;
+import com.ruoyi.interfaces.workflow.executor.StepExecutor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/15 16:23
+ */
+
+@Service
+public class WorkflowEngine {
+
+    @Autowired
+    private MdModelStepService stepService;
+
+    @Autowired
+    private MdModelWorkflowExecutionService executionService;
+
+    @Autowired
+    private ExecutorFactory executorFactory;
+
+    public ExecutionResult execute(Long workflowId, Map<String, Object> input) {
+        // 获取工作流步骤
+        List<MdModelStep> steps = stepService.listByWorkflowIdOrderBySequence(workflowId);
+
+        // 创建执行上下文
+        ExecutionContext context = new ExecutionContext(input);
+
+        // 创建执行记录
+        MdModelWorkflowExecution workflowExecution = new MdModelWorkflowExecution();
+        workflowExecution.setWorkflowId(workflowId);
+        workflowExecution.setStatus(MdModelWorkflowExecution.ExecutionStatus.RUNNING);
+        workflowExecution.setInput(JsonUtils.objectToJson(input));
+        workflowExecution.setStartTime(new Date());
+        executionService.save(workflowExecution);
+
+        // 创建执行结果
+        ExecutionResult result = new ExecutionResult(String.valueOf(workflowId), input);
+        result.setStatus(ExecutionResult.ExecutionStatus.RUNNING);
+
+        try {
+            for (int i = 0; i < steps.size(); i++) {
+                MdModelStep currentStep = steps.get(i);
+
+                // 执行当前步骤
+                StepExecutor executor = executorFactory.getExecutor(currentStep.getType());
+                MdModelStepExecution stepExecution = executor.execute(currentStep, context);
+
+                // 保存步骤执行结果
+                result.addStepExecution(stepExecution);
+
+                // 处理步骤执行结果
+                if (stepExecution.isFailed()) {
+                    // 根据错误策略处理
+                    switch (currentStep.getErrorPolicy()) {
+                        case ABORT:
+                            throw new StepExecutionException(stepExecution.getErrorMsg());
+                        case IGNORE:
+                            // 继续执行下一步
+                            break;
+                        case RETRY:
+                            // 实现重试逻辑
+                            handleRetry(currentStep, context, stepExecution);
+                            break;
+                    }
+                }
+
+                // 处理条件分支
+                if (currentStep.getType() == MdModelStep.StepType.CONDITION) {
+                    // 实现条件分支逻辑
+                    handleConditionBranch(steps, i, context, result);
+                }
+            }
+
+            // 所有步骤成功完成
+            result.markSuccess(context.getAllContextData());
+            workflowExecution.setStatus(MdModelWorkflowExecution.ExecutionStatus.SUCCESS);
+        } catch (StepExecutionException e) {
+            result.markFailed(e.getMessage());
+            workflowExecution.setStatus(MdModelWorkflowExecution.ExecutionStatus.FAILED);
+        }
+
+        // 持久化执行结果
+        workflowExecution.setEndTime(new Date());
+        executionService.updateById(workflowExecution);
+        return result;
+    }
+
+    private void handleRetry(MdModelStep step, ExecutionContext context, MdModelStepExecution stepExecution) {
+        StepExecutor executor = executorFactory.getExecutor(step.getType());
+        int maxRetries = step.getRetryCount();
+
+        for (int retry = 1; retry <= maxRetries; retry++) {
+            stepExecution.increaseRetryCount();
+
+            try {
+                MdModelStepExecution newExecution = executor.execute(step, context);
+                stepExecution.setStatus(newExecution.getStatus());
+                stepExecution.setOutput(newExecution.getOutput());
+
+                if (newExecution.isSuccess()) {
+                    stepExecution.setErrorMsg(null);
+                    return; // 重试成功
+                } else {
+                    stepExecution.setErrorMsg(newExecution.getErrorMsg());
+                }
+            } catch (Exception e) {
+                stepExecution.setErrorMsg(e.getMessage());
+            }
+        }
+    }
+
+    private void handleConditionBranch(List<MdModelStep> steps, int currentIndex,
+                                       ExecutionContext context,
+                                       ExecutionResult result) {
+        // 获取条件判断结果
+        Boolean conditionResult = (Boolean) context.getAllContextData().get("CONDITION_RESULT");
+
+        if (conditionResult != null && !conditionResult) {
+            // 跳过后续步骤直到下一个分支节点或结束
+            while (currentIndex + 1 < steps.size()) {
+                MdModelStep nextStep = steps.get(currentIndex + 1);
+
+                // 如果遇到结束条件或新分支,停止跳过
+                if (nextStep.getType() == MdModelStep.StepType.CONDITION) {
+                    break;
+                }
+
+                // 创建被跳过的步骤执行记录
+                MdModelStepExecution skippedExecution = new MdModelStepExecution();
+                skippedExecution.setStepId(nextStep.getId());
+                skippedExecution.setStepName(nextStep.getName());
+                skippedExecution.setStepType(nextStep.getType());
+                skippedExecution.markSkipped();
+
+                // 添加到执行结果
+                result.addStepExecution(skippedExecution);
+
+                currentIndex++;
+            }
+        }
+    }
+
+}

+ 39 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/WorkflowUtils.java

@@ -0,0 +1,39 @@
+package com.ruoyi.interfaces.workflow;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/17 10:11
+ */
+public class WorkflowUtils {
+
+    public static String renderTemplate(String template, ExecutionContext context) {
+        // 正则表达式匹配 {{placeholder}}
+        Pattern pattern = Pattern.compile("\\{\\{\\s*(.*?)\\s*\\}\\}");
+        Matcher matcher = pattern.matcher(template);
+        StringBuffer result = new StringBuffer();
+
+        while (matcher.find()) {
+            // 提取占位符中的 key(去掉空格)
+            String key = matcher.group(1);
+            Object value = context.getAllContextData().get(key);  // 从上下文获取值
+
+            // 处理值不存在或异常情况
+            String replacement;
+            if (value == null) {
+                replacement = "";
+            } else {
+                replacement = Matcher.quoteReplacement(value.toString());
+            }
+
+            // 替换匹配到的占位符
+            matcher.appendReplacement(result, replacement);
+        }
+
+        matcher.appendTail(result);
+        return result.toString();
+    }
+
+}

+ 112 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/ApiStepExecutor.java

@@ -0,0 +1,112 @@
+package com.ruoyi.interfaces.workflow.executor;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/16 9:13
+ */
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.utils.HttpRequestUtil;
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.service.MdModelStepExecutionService;
+import com.ruoyi.interfaces.service.MdModelWorkflowExecutionService;
+import com.ruoyi.interfaces.workflow.ExecutionContext;
+import com.ruoyi.interfaces.workflow.WorkflowUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * API调用步骤执行器
+ */
+@Component
+public class ApiStepExecutor implements StepExecutor {
+
+    @Autowired
+    private MdModelStepExecutionService stepExecutionService;
+
+    @Override
+    public MdModelStep.StepType getSupportedStepType() {
+        return MdModelStep.StepType.API;
+    }
+
+    @Override
+    public MdModelStepExecution execute(MdModelStep step, ExecutionContext context) {
+        MdModelStepExecution stepExecution = new MdModelStepExecution();
+        stepExecution.setStepId(step.getId());
+        stepExecution.setStepName(step.getName());
+        stepExecution.setStepType(MdModelStep.StepType.API);
+        stepExecution.markStart();
+        stepExecutionService.save(stepExecution);
+
+        try {
+            // 准备请求
+            Map<String, Object> config = (Map<String, Object>) JsonUtils.jsonToPojo(step.getConfig(), Map.class);
+            HttpMethod method = HttpMethod.valueOf(config.get("method").toString());
+            String url = WorkflowUtils.renderTemplate(config.get("url").toString(), context);
+            Map<String, String> headers = (Map<String, String>) config.get("headers");
+            String requestBody = buildRequestBody(config.get("body"), context);
+
+            // 执行API调用
+            JSONObject response = HttpRequestUtil.getJSONObjectByRequest(url, method.toString(), headers, requestBody);
+
+            // 处理响应
+            if (response == null) {
+                stepExecution.markFailed("API请求失败");
+            } else {
+                Map<String, Object> responseMap = new HashMap<>(response);
+                stepExecution.markSuccess(responseMap);
+                stepExecutionService.save(stepExecution);
+                context.setStepOutput(String.valueOf(step.getId()), responseMap);
+
+                // 保存到全局上下文
+                saveToGlobalContext(step, context, response);
+            }
+        } catch (Exception e) {
+            stepExecution.markFailed(e.getMessage());
+            stepExecutionService.save(stepExecution);
+        }
+
+        return stepExecution;
+    }
+
+    private String buildRequestBody(Object bodyTemplate, ExecutionContext context) {
+        // 构建请求体
+        if (bodyTemplate instanceof String) {
+            return WorkflowUtils.renderTemplate(bodyTemplate.toString(), context);
+        } else if (bodyTemplate instanceof Map) {
+            ((Map<?, ?>) bodyTemplate).forEach((key, value) -> {
+                if (value instanceof String) {
+                    ((Map) bodyTemplate).put(key, WorkflowUtils.renderTemplate(value.toString(), context));
+                }
+            });
+            return JsonUtils.objectToJson(bodyTemplate);
+        }
+        return null; // 实际实现
+    }
+
+    private void saveToGlobalContext(MdModelStep step, ExecutionContext context, Map<String, Object> response) {
+        // 根据配置保存特定字段到全局上下文
+        Map<String, Object> outputMapping = JsonUtils.jsonToPojo(step.getOutputMapping(), JSONObject.class);
+        if (outputMapping != null) {
+            outputMapping.forEach((responsePath, contextKey) -> {
+                Object value = extractFromResponse(response, responsePath);
+                if (value != null) {
+                    context.setContextValue(contextKey.toString(), value);
+                }
+            });
+        }
+    }
+
+    private Object extractFromResponse(Map<String, Object> response, String path) {
+        // 使用JSONPath提取特定字段
+
+        return null;
+    }
+}

+ 21 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/ConditionStepExecutor.java

@@ -0,0 +1,21 @@
+package com.ruoyi.interfaces.workflow.executor;
+
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.workflow.ExecutionContext;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/16 9:26
+ */
+public class ConditionStepExecutor implements StepExecutor {
+    @Override
+    public MdModelStep.StepType getSupportedStepType() {
+        return MdModelStep.StepType.CONDITION;
+    }
+
+    @Override
+    public MdModelStepExecution execute(MdModelStep step, ExecutionContext context) {
+        return null;
+    }
+}

+ 49 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/DbStepExecutor.java

@@ -0,0 +1,49 @@
+package com.ruoyi.interfaces.workflow.executor;
+
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.workflow.ExecutionContext;
+import com.ruoyi.interfaces.workflow.WorkflowUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author LinQiLong
+ * @date 2025/7/17 10:08
+ */
+@Component
+public class DbStepExecutor implements StepExecutor {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Override
+    public MdModelStep.StepType getSupportedStepType() {
+        return MdModelStep.StepType.DB;
+    }
+
+    @Override
+    public MdModelStepExecution execute(MdModelStep step, ExecutionContext context) {
+        Map<String, Object> config = (Map<String, Object>) JsonUtils.jsonToPojo(step.getConfig(), Map.class);
+
+        // 1. 准备SQL
+        String sql = WorkflowUtils.renderTemplate(config.get("sql").toString(), context);
+
+        // 2. 执行SQL
+        if (isQuery(sql)) {
+            return (MdModelStepExecution) jdbcTemplate.queryForMap(sql);
+        } else {
+            int affectedRows = jdbcTemplate.update(sql);
+            return (MdModelStepExecution) Collections.singletonMap("affectedRows", affectedRows);
+        }
+    }
+
+    private boolean isQuery(String sql) {
+        return sql.trim().toLowerCase().startsWith("select");
+    }
+}

+ 29 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/workflow/executor/StepExecutor.java

@@ -0,0 +1,29 @@
+package com.ruoyi.interfaces.workflow.executor;
+
+import com.ruoyi.interfaces.domain.MdModelStep;
+import com.ruoyi.interfaces.domain.MdModelStepExecution;
+import com.ruoyi.interfaces.workflow.ExecutionContext;
+
+/**
+ * 步骤执行器接口
+ */
+public interface StepExecutor {
+
+    /**
+     * 获取支持的步骤类型
+     *
+     * @return 步骤类型
+     */
+    MdModelStep.StepType getSupportedStepType();
+
+    /**
+     * 执行步骤逻辑
+     *
+     * @param step    步骤配置
+     * @param context 执行上下文
+     * @return 步骤执行结果
+     */
+    MdModelStepExecution execute(MdModelStep step, ExecutionContext context);
+
+
+}

+ 9 - 0
ruoyi-common/pom.xml

@@ -74,6 +74,11 @@
             <artifactId>fastjson2</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+        </dependency>
+
         <!-- io常用工具类 -->
         <dependency>
             <groupId>commons-io</groupId>
@@ -144,6 +149,10 @@
             <groupId>com.squareup.okhttp3</groupId>
             <artifactId>okhttp</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 47 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataType.java

@@ -0,0 +1,47 @@
+package com.ruoyi.common.enums;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.common.utils.StringDataTypeUtils;
+import com.ruoyi.common.utils.Xml2JsonUtils;
+
+/**
+ * @author LinQiLong
+ * @date 2023/4/26 10:38
+ */
+public enum DataType {
+
+    /**
+     * json 格式
+     */
+    JSON {
+        @Override
+        public JSONObject toJSONObject(String dataStr) {
+            return JsonUtils.jsonToPojo(dataStr, JSONObject.class);
+        }
+    },
+    XML {
+        @Override
+        public JSONObject toJSONObject(String dataStr) {
+            JSONObject resObj = Xml2JsonUtils.xml2Json(dataStr);
+            // 2. 查询指定数据
+            return resObj;
+        }
+    },
+    STRING, NUMBER;
+
+    public static JSONObject getDataByJSON(String dataStr) {
+        // 判断类型
+        String type = StringDataTypeUtils.getType(dataStr);
+        DataType dataType = DataType.valueOf(type);
+        // 转换成 JSONObject
+        return dataType.toJSONObject(dataStr);
+    }
+
+    public JSONObject toJSONObject(String dataStr) {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("data", dataStr);
+        return jsonObject;
+    }
+
+}

+ 139 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpRequestUtil.java

@@ -0,0 +1,139 @@
+package com.ruoyi.common.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.enums.DataType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * http请求--该工具类只适用于报表设计-rest接口数据源
+ */
+public class HttpRequestUtil {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(HttpRequestUtil.class);
+
+    /**
+     * 发送请求查询数据
+     *
+     * @param requestUrl    请求路径
+     * @param requestMethod 请求方式
+     * @param requestBody   请求体
+     * @param requestHeader 请求头
+     * @return 响应结果
+     * @throws IOException 异常
+     */
+    public static String getDataByRequest(String requestUrl, String requestMethod, Map<String, String> requestHeader, String requestBody) throws IOException {
+        LOGGER.info(">> 访问接口:\n请求地址:{}\n请求方式:{}\n请求头:{}\n请求参数:{}", requestUrl, requestMethod, requestHeader != null ? JSON.toJSON(requestHeader) : "", requestBody);
+        String result;
+        BufferedReader in = null;
+        DataOutputStream out = null;
+        if ("GET".equalsIgnoreCase(requestMethod) && !StringUtils.isEmpty(requestBody)) {
+            Map<String, Object> bodyMap = JsonUtils.jsonToPojo(requestBody, Map.class);
+            StringBuilder urlSb = new StringBuilder(requestUrl).append("?");
+            for (String key : bodyMap.keySet()) {
+                urlSb.append(key).append("=").append(bodyMap.get(key)).append("&");
+            }
+            urlSb.deleteCharAt(urlSb.length() - 1);
+            requestUrl = urlSb.toString().replaceAll(" ", "%20");
+        }
+        HttpURLConnection conn = getHttpConnection(requestUrl, requestMethod);
+        try {
+            if (!"GET".equalsIgnoreCase(requestMethod) && !StringUtils.isEmpty(requestBody)) {
+                conn.setDoInput(true);
+                conn.setDoOutput(true);
+                if (requestHeader != null && !requestHeader.isEmpty()) {
+                    requestHeader.forEach(conn::setRequestProperty);
+                }
+                out = new DataOutputStream(conn.getOutputStream());
+                out.writeBytes(requestBody);
+            }
+            conn.connect();
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+            StringBuilder sb = new StringBuilder();
+            String temp;
+            while ((temp = in.readLine()) != null) {
+                sb.append(temp).append(" ");
+            }
+            result = sb.toString();
+            LOGGER.info(">> 访问结果:{}", result);
+            return result;
+        } catch (IOException ex) {
+            LOGGER.error(">> 访问接口失败:{}", ex.getMessage(), ex);
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+            if (out != null) {
+                out.flush();
+                out.close();
+            }
+        }
+        return null;
+    }
+
+    public static JSONObject getRequest(String requestUrl) {
+        return getRequest(requestUrl, null);
+    }
+
+    public static JSONObject getRequest(String requestUrl, Map<String, Object> requestBody) {
+        return getJSONObjectByRequest(requestUrl, "GET", null, requestBody != null ? JsonUtils.objectToJson(requestBody) : null);
+    }
+
+    public static JSONObject getRequest(String requestUrl, Map<String, String> requestHeader, Map<String, Object> requestBody) {
+        return getJSONObjectByRequest(requestUrl, "GET", requestHeader, requestBody != null ? JsonUtils.objectToJson(requestBody) : null);
+    }
+
+    public static JSONObject postRequest(String requestUrl, Map<String, Object> requestBody) {
+        Map<String, String> requestHeader = new HashMap<>(1);
+        requestHeader.put("Content-Type", "application/json");
+        return getJSONObjectByRequest(requestUrl, "POST", requestHeader, requestBody != null ? JsonUtils.objectToJson(requestBody) : null);
+    }
+
+    public static JSONObject getJSONObjectByRequest(String requestUrl, String requestMethod, Map<String, String> requestHeader, String requestBody) {
+        try {
+            // 请求服务
+            String resStr = HttpRequestUtil.getDataByRequest(requestUrl, requestMethod, requestHeader, requestBody);
+            // 返回结果转换格式
+            return Optional.ofNullable(resStr).map(DataType::getDataByJSON).orElse(null);
+        } catch (IOException ignored) {
+        }
+        return null;
+    }
+
+    /**
+     * 查询HTTP请求连接
+     *
+     * @param requestUrl    请求路径
+     * @param requestMethod 请求方式
+     * @return 返回请求连接
+     */
+    private static HttpURLConnection getHttpConnection(String requestUrl, String requestMethod) {
+        HttpURLConnection conn;
+        try {
+            URL uri = new URL(requestUrl);
+            conn = (HttpURLConnection) uri.openConnection();
+            // requestMethod: POST, PUT, DELETE, GET
+            conn.setRequestMethod(requestMethod);
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            conn.setConnectTimeout(120000); //60 secs
+            conn.setReadTimeout(120000); //60 secs
+            conn.setRequestProperty("Accept-Charset", "UTF-8");
+            conn.setRequestProperty("contentType", "UTF-8");
+        } catch (Exception ex) {
+            throw new RuntimeException("connection failed.");
+        }
+        return conn;
+    }
+}

+ 99 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringDataTypeUtils.java

@@ -0,0 +1,99 @@
+package com.ruoyi.common.utils;
+
+import com.alibaba.fastjson2.JSON;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.StringReader;
+
+/**
+ * @author LinQiLong
+ * @date 2023/4/26 10:19
+ */
+public class StringDataTypeUtils {
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        String str1 = "45";
+        String str2 = "wangningwei";
+        String str3 = "{cd:\"wangningwei\"}";
+        String str4 = "<av>fvbfdyr</av>";
+        System.out.println("string1::" + getType(str1));
+        System.out.println("string2::" + getType(str2));
+        System.out.println("string3::" + getType(str3));
+        System.out.println("string4::" + getType(str4));
+    }
+
+    public static String getType(String string) {
+        if (isNumber(string)) {
+            return "NUMBER";
+        } else if (isJson(string)) {
+            return "JSON";
+        } else if (isXML(string)) {
+            return "XML";
+        } else {
+            return "STRING";
+        }
+    }
+
+    /**
+     * 判断字符串是否是数字
+     */
+    public static boolean isNumber(String value) {
+        return isInteger(value) || isDouble(value);
+    }
+
+    /**
+     * 判断字符串是否是整数
+     */
+    public static boolean isInteger(String value) {
+        try {
+            Integer.parseInt(value);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 判断字符串是否是浮点数
+     */
+    public static boolean isDouble(String value) {
+        try {
+            Double.parseDouble(value);
+            return value.contains(".");
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 判断是否是json结构
+     */
+    public static boolean isJson(String value) {
+        try {
+            JSON.parse(value);
+        } catch (Throwable e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 判断是否是xml结构
+     */
+    public static boolean isXML(String value) {
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            InputSource is = new InputSource(new StringReader(value));
+            builder.parse(is);
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+}

+ 99 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/Xml2JsonUtils.java

@@ -0,0 +1,99 @@
+package com.ruoyi.common.utils;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.dom4j.*;
+
+import java.util.List;
+
+/**
+ * * @ClassName XmlToJsonUtil
+ * * @Author gxy
+ * * @Desc xml转json
+ *
+ * @author LinQiLong
+ * @date 2023/3/27 17:36
+ */
+@Slf4j
+public class Xml2JsonUtils {
+
+    /**
+     * xml转json
+     *
+     * @param xmlStr
+     * @return
+     * @throws DocumentException
+     */
+    public static JSONObject xml2Json(String xmlStr) {
+        try {
+            Document doc = DocumentHelper.parseText(xmlStr);
+            return dom4j2Json(doc.getRootElement());
+        } catch (DocumentException e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * xml转json
+     *
+     * @param element
+     */
+    private static JSONObject dom4j2Json(Element element) {
+        JSONObject json = new JSONObject();
+        // 如果是属性
+        for (Object o : element.attributes()) {
+            Attribute attr = (Attribute) o;
+            if (StringUtils.isNotBlank(attr.getValue())) {
+                json.put("@" + attr.getName(), attr.getValue());
+            }
+        }
+        List<Element> chdEl = element.elements();
+        if (chdEl.isEmpty() && StringUtils.isNotBlank(element.getText())) {
+            // 如果没有子元素,只有一个值
+            json.put(element.getName(), element.getText());
+        }
+        for (Element e : chdEl) {
+            // 有子元素
+            if (!e.elements().isEmpty()) {
+                // 子元素也有子元素
+                JSONObject chdjson = dom4j2Json(e);
+                Object o = json.get(e.getName());
+                if (o != null) {
+                    JSONArray jsona = null;
+                    if (o instanceof JSONObject) {
+                        // 如果此元素已存在,则转为jsonArray
+                        JSONObject jsono = (JSONObject) o;
+                        json.remove(e.getName());
+                        jsona = new JSONArray();
+                        jsona.add(jsono);
+                        jsona.add(chdjson);
+                    }
+                    if (o instanceof JSONArray) {
+                        jsona = (JSONArray) o;
+                        jsona.add(chdjson);
+                    }
+                    json.put(e.getName(), jsona);
+                } else {
+                    if (!chdjson.isEmpty()) {
+                        json.put(e.getName(), chdjson);
+                    }
+                }
+            } else {
+                // 子元素没有子元素
+                for (Object o : element.attributes()) {
+                    Attribute attr = (Attribute) o;
+                    if (StringUtils.isNotBlank(attr.getValue())) {
+                        json.put("@" + attr.getName(), attr.getValue());
+                    }
+                }
+                if (!e.getText().isEmpty()) {
+                    json.put(e.getName(), e.getText());
+                }
+            }
+        }
+        return json;
+    }
+}

+ 1 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@@ -110,6 +110,7 @@ public class SecurityConfig {
                             // 静态资源,可匿名访问
                             .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                             .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
+                            .antMatchers("/workflows/**").permitAll()
                             // 除上面外的所有请求全部需要鉴权认证
                             .anyRequest().authenticated();
                 })

+ 4 - 4
ruoyi-ui/src/views/service/info/index.vue

@@ -13,7 +13,7 @@
           <el-input v-model="query.data.unitName"></el-input>
         </el-form-item>
         <el-form-item label="服务状态">
-          <el-select clearable v-model="query.data.applyState" style="width:100px ;">
+          <el-select clearable v-model="query.data.applyState" style="width:100px;">
             <el-option label="待审核" value="0"></el-option>
             <el-option label="同意" value="1"></el-option>
           </el-select>
@@ -111,7 +111,7 @@
       </el-table>
     </el-row>
     <el-row>
-      
+
     </el-row>
     <div style="text-align: center;" class="123">
         <el-pagination
@@ -140,7 +140,7 @@
     </el-dialog>
   </div>
   <div>
-    
+
   </div>
 </template>
 <script setup name="Menu">
@@ -231,7 +231,7 @@ export default {
       else{
         return
       }
-      
+
     },
     // 更新状态
     updateStatus(service) {