Преглед изворни кода

Merge branch 'master' of http://39.98.38.2:13000/dumingliang/sh-model-platform

Hua пре 9 месеци
родитељ
комит
a415856bc2
60 измењених фајлова са 2578 додато и 793 уклоњено
  1. 1 0
      plugins/disabled.txt
  2. BIN
      plugins/plugin-biz-http-3.8.8.jar
  3. 0 7
      pom.xml
  4. BIN
      ruoyi-admin/libs/gw-plugin-common-3.8.8.jar
  5. BIN
      ruoyi-admin/libs/gw-plugin-starter-3.8.8.jar
  6. 14 24
      ruoyi-admin/pom.xml
  7. 260 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/PluginController.java
  8. 2 2
      ruoyi-admin/src/main/resources/application.yml
  9. 3 3
      ruoyi-admin/src/main/resources/kms_client.yml
  10. 4 13
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/BizDataShowConfigController.java
  11. 40 20
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdDataSetController.java
  12. 0 1
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdFlowDispatchController.java
  13. 1 1
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/PtTreeCateController.java
  14. 35 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/web/SnailJobLogController.java
  15. 21 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/JobLogVo.java
  16. 22 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdDataSet.java
  17. 23 3
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/vo/JobRequestVo.java
  18. 15 4
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/BizDataShowConfigMapper.java
  19. 5 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/SnailJobMapper.java
  20. 21 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/BizDataShowConfigService.java
  21. 1 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdDataCateService.java
  22. 6 1
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdDataSetService.java
  23. 2 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdModelInfoService.java
  24. 16 8
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/SnailJobService.java
  25. 26 4
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/BizDataShowConfigServiceImpl.java
  26. 9 7
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdDataCateServiceImpl.java
  27. 31 26
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdDataSetServiceImpl.java
  28. 10 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelInfoServiceImpl.java
  29. 8 18
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/PtServiceMonitorServiceImpl.java
  30. 2 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/PtServiceServiceImpl.java
  31. 26 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/SnailJobServiceImpl.java
  32. 30 27
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/snailJob/CleanDataJob.java
  33. 124 0
      ruoyi-api-patform/src/main/resources/mapper/interfaces/BizDataShowConfigMapper.xml
  34. 27 16
      ruoyi-api-patform/src/main/resources/mapper/interfaces/MdDataSetMapper.xml
  35. 87 55
      ruoyi-api-patform/src/main/resources/mapper/interfaces/SnailJobMapper.xml
  36. 43 7
      ruoyi-common/src/main/java/com/ruoyi/common/core/api/BizDataApiRequest.java
  37. 0 5
      ruoyi-quartz/pom.xml
  38. 0 28
      ruoyi-sh/pom.xml
  39. 333 260
      ruoyi-ui/src/api/register/regCom.js
  40. 17 1
      ruoyi-ui/src/api/service/timing.js
  41. 2 1
      ruoyi-ui/src/api/standardization/bizDataShowConfig.js
  42. 94 0
      ruoyi-ui/src/api/system/plugin/index.js
  43. 7 0
      ruoyi-ui/src/assets/icons/svg/play2.svg
  44. 6 0
      ruoyi-ui/src/assets/icons/svg/stop2.svg
  45. 1 1
      ruoyi-ui/src/views/container/index.vue
  46. 0 149
      ruoyi-ui/src/views/containerImage/index.vue
  47. 1 1
      ruoyi-ui/src/views/docker/index.vue
  48. 1 2
      ruoyi-ui/src/views/dpp/task/integratioTask/index2.vue
  49. 9 3
      ruoyi-ui/src/views/map/index.vue
  50. 1 0
      ruoyi-ui/src/views/map/utils/jsonToGeojson.js
  51. 5 0
      ruoyi-ui/src/views/map/utils/styleParser.js
  52. 16 3
      ruoyi-ui/src/views/monitor/service/GwStatistic.vue
  53. 3 3
      ruoyi-ui/src/views/monitor/service/index.vue
  54. 52 26
      ruoyi-ui/src/views/monitor/taskMonitor/index.vue
  55. 386 0
      ruoyi-ui/src/views/register/modelData/dataQuery.vue
  56. 10 5
      ruoyi-ui/src/views/standardization/bizDataShowConfig/index.vue
  57. 144 28
      ruoyi-ui/src/views/standardization/bizDataShowConfig/list/index.vue
  58. 109 0
      ruoyi-ui/src/views/standardization/bizDataShowConfig/show/BizTable.vue
  59. 110 18
      ruoyi-ui/src/views/standardization/bizDataShowConfig/show/index.vue
  60. 356 0
      ruoyi-ui/src/views/system/plugin/index.vue

+ 1 - 0
plugins/disabled.txt

@@ -0,0 +1 @@
+biz-plugin

BIN
plugins/plugin-biz-http-3.8.8.jar


+ 0 - 7
pom.xml

@@ -230,12 +230,6 @@
                 <artifactId>ruoyi-common</artifactId>
                 <version>${ruoyi.version}</version>
             </dependency>
-            <!-- 生态流量业务代码模块-->
-            <dependency>
-                <groupId>com.ruoyi</groupId>
-                <artifactId>ruoyi-sh</artifactId>
-                <version>${ruoyi.version}</version>
-            </dependency>
             <!-- 接口服务模块 -->
             <dependency>
                 <groupId>com.ruoyi</groupId>
@@ -285,7 +279,6 @@
 
     <modules>
         <module>ruoyi-admin</module>
-        <module>ruoyi-sh</module>
         <module>ruoyi-api-patform</module>
         <module>ruoyi-framework</module>
         <module>ruoyi-system</module>

BIN
ruoyi-admin/libs/js-gw-plugin-common-3.8.8.jar → ruoyi-admin/libs/gw-plugin-common-3.8.8.jar


BIN
ruoyi-admin/libs/gw-plugin-starter-3.8.8.jar


+ 14 - 24
ruoyi-admin/pom.xml

@@ -37,13 +37,6 @@
             <version>1.6.2</version>
         </dependency>
 
-        <!-- DM8驱动包 -->
-        <dependency>
-            <groupId>com.dameng</groupId>
-            <artifactId>DmJdbcDriver18</artifactId>
-            <version>8.1.3.140</version>
-        </dependency>
-
         <!-- Mysql驱动包 -->
         <dependency>
             <groupId>mysql</groupId>
@@ -89,11 +82,6 @@
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
-        <!-- 业务代码 -->
-        <dependency>
-            <groupId>com.ruoyi</groupId>
-            <artifactId>ruoyi-sh</artifactId>
-        </dependency>
         <!-- 接口服务 -->
         <dependency>
             <groupId>com.ruoyi</groupId>
@@ -109,18 +97,20 @@
             <version>3.0.5</version>
         </dependency>
 
-<!--        <dependency>-->
-<!--            <groupId>com.ruoyi</groupId>-->
-<!--            <artifactId>gw-plugin-common</artifactId>-->
-<!--            <version>3.8.8</version>-->
-<!--            <systemPath>${project.basedir}/libs/js-gw-plugin-common-3.8.8.jar</systemPath>-->
-<!--        </dependency>-->
-<!--        <dependency>-->
-<!--            <groupId>com.ruoyi</groupId>-->
-<!--            <artifactId>gw-plugin-starter</artifactId>-->
-<!--            <version>3.8.8</version>-->
-<!--            <systemPath>${project.basedir}/libs/gw-plugin-starter-3.8.8.jar</systemPath>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>gw-plugin-common</artifactId>
+            <version>3.8.8</version>
+            <!--            <scope>system</scope>-->
+            <!--            <systemPath>${project.basedir}/libs/js-gw-plugin-common-3.8.8.jar</systemPath>-->
+        </dependency>
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>gw-plugin-starter</artifactId>
+            <version>3.8.8</version>
+            <!--            <scope>system</scope>-->
+            <!--            <systemPath>${project.basedir}/libs/gw-plugin-starter-3.8.8.jar</systemPath>-->
+        </dependency>
         <dependency>
             <groupId>org.pf4j</groupId>
             <artifactId>pf4j</artifactId>

+ 260 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/PluginController.java

@@ -0,0 +1,260 @@
+package com.ruoyi.web.controller.system;
+
+import cn.com.goldenwater.common.plugin.SpringPluginManager;
+import cn.com.goldenwater.common.plugin.autoconfigure.PluginProperties;
+import com.ruoyi.common.core.domain.R;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.pf4j.PluginState;
+import org.pf4j.PluginWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * 插件管理
+ *
+ * @author Lipujun
+ */
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/plugin/")
+//@ConditionalOnProperty(value = "golden.plugin.enabled", havingValue = "true")
+public class PluginController {
+
+    private PluginProperties pluginProperties;
+
+    @Autowired
+    private SpringPluginManager pluginManager;
+
+    @Autowired
+    public void setPluginProperties(PluginProperties pluginProperties) {
+        this.pluginProperties = pluginProperties;
+    }
+
+    @Autowired
+    public void setPluginManager(SpringPluginManager pluginManager) {
+        this.pluginManager = pluginManager;
+    }
+
+    /**
+     * 上传插件
+     *
+     * @param file
+     * @return
+     */
+    @PostMapping("upload")
+    public R<Void> uploadPlugin(@RequestParam("file") MultipartFile file) {
+        String path = pluginProperties.getPath();
+
+        String fileName = file.getOriginalFilename();
+        String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
+        // 检查文件格式是否合法
+        if (StringUtils.isEmpty(suffixName)) {
+            return R.fail("文件格式不合格,请上传jar或zip文件");
+        }
+        if (!"jar".equalsIgnoreCase(suffixName) && !"zip".equalsIgnoreCase(suffixName)) {
+            return R.fail("文件格式不合格,请上传jar或zip文件");
+        }
+
+        // 第一步: 上传文件
+        try {
+            // 同名文件存在时,会出现 FileAlreadyExistsException 异常。
+            Files.copy(file.getInputStream(), Paths.get(path).resolve(file.getOriginalFilename()));
+        } catch (IOException e) {
+            return R.fail("文件重复上传!");
+        }
+
+        // 第二步: 启动插件
+        try {
+            // 加载并启动插件
+            String pluginId = pluginManager.loadPlugin(Paths.get(path, fileName));
+            PluginState state = pluginManager.startPlugin(pluginId);
+            if (state != PluginState.STARTED) {
+                return R.fail("插件启动失败");
+            }
+        } catch (Exception e) {
+            return R.fail("插件加载失败: " + e.getMessage());
+        }
+        return R.ok();
+    }
+
+    /**
+     *
+     */
+    @GetMapping("reload")
+    public void reloadPlugins() {
+        System.out.println("Reloading plugins...");
+        pluginManager.unloadPlugins();
+        pluginManager.loadPlugins();
+        pluginManager.startPlugins();
+        System.out.println("Reloading plugins success.");
+    }
+
+    /**
+     * 加载指定插件
+     *
+     * @param pluginId 插件唯一标识符
+     * @return 加载结果信息,成功时返回插件加载路径,失败时返回错误信息
+     */
+    @GetMapping("load/{pluginId}")
+    public String loadPlugin(@RequestParam() String pluginId) {
+        // 获取指定ID的插件信息
+        PluginWrapper plugin = pluginManager.getPlugin(pluginId);
+        if (plugin != null) {
+            // 如果插件存在,则加载该插件并返回加载结果
+            return pluginManager.loadPlugin(plugin.getPluginPath());
+        } else {
+            // 如果插件不存在,返回错误提示
+            return "Plugin not found";
+        }
+    }
+
+
+    /**
+     * 卸载指定的插件
+     *
+     * @param pluginId 插件唯一标识符,用于指定要卸载的插件
+     * @return 卸载操作的结果,true表示卸载成功,false表示卸载失败
+     */
+    @GetMapping("unload/{pluginId}")
+    public boolean unloadPlugin(@PathVariable String pluginId) {
+        return pluginManager.unloadPlugin(pluginId);
+    }
+
+
+    /**
+     * 启动指定插件
+     *
+     * @param pluginId 插件唯一标识符
+     * @return 插件启动后的状态信息
+     */
+    @GetMapping("start/{pluginId}")
+    public PluginState startPlugin(@PathVariable String pluginId) {
+        return pluginManager.startPlugin(pluginId);
+    }
+
+
+    /**
+     * 停止指定插件的运行
+     *
+     * @param pluginId 插件唯一标识符,用于指定要停止的插件
+     * @return PluginState 插件停止后的状态信息
+     */
+    @GetMapping("stop/{pluginId}")
+    public PluginState stopPlugin(@PathVariable String pluginId) {
+        return pluginManager.stopPlugin(pluginId);
+    }
+
+
+    /**
+     * 禁用指定的插件
+     *
+     * @param pluginId 插件的唯一标识符
+     * @return true表示禁用成功,false表示禁用失败
+     */
+    @GetMapping("disable/{pluginId}")
+    public boolean disablePlugin(@PathVariable String pluginId) {
+        return pluginManager.disablePlugin(pluginId);
+    }
+
+
+    /**
+     * 启用指定的插件
+     *
+     * @param pluginId 插件唯一标识符
+     * @return 启用操作是否成功
+     */
+    @GetMapping("enable/{pluginId}")
+    public boolean enablePlugin(@PathVariable String pluginId) {
+        return pluginManager.enablePlugin(pluginId);
+    }
+
+
+    /**
+     * 删除指定插件
+     *
+     * @param pluginId 插件唯一标识符
+     * @return 删除操作是否成功
+     */
+    @GetMapping("delete/{pluginId}")
+    public boolean deletePlugin(@PathVariable String pluginId) {
+        Arrays.stream(pluginId.split(",")).forEach(id -> pluginManager.deletePlugin(id));
+        return true;
+    }
+
+
+    /**
+     * 获取插件列表
+     *
+     * @return 插件信息列表,每个元素为包含插件详细信息的JSONObject对象
+     */
+    @GetMapping("/list")
+    public List<Map<String, Object>> listPlugin(String name) {
+        ArrayList<Map<String, Object>> list = new ArrayList<>();
+        List<PluginWrapper> plugins = pluginManager.getPlugins();
+
+        if (StringUtils.isNotBlank(name)) {
+            plugins = plugins.stream().filter(plugin -> plugin.getPluginId().contains(name)).collect(Collectors.toList());
+        }
+        for (PluginWrapper plugin : plugins) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("id", plugin.getPluginId());
+//            map.put("name", plugin.getDescriptor().getPluginName());
+            map.put("version", plugin.getDescriptor().getVersion());
+            map.put("path", plugin.getPluginPath().toString());
+            map.put("state", plugin.getPluginState());
+            map.put("class", plugin.getDescriptor().getPluginClass());
+            map.put("requires", plugin.getDescriptor().getRequires());
+            map.put("provider", plugin.getDescriptor().getProvider());
+            map.put("license", plugin.getDescriptor().getLicense());
+            map.put("dependencies", plugin.getDescriptor().getDependencies());
+            list.add(map);
+        }
+        String path = pluginProperties.getPath().toString();
+        System.out.println("plugin work path = " + path);
+        return list;
+    }
+
+    @GetMapping("/info/{pluginId}")
+    public R<Map> info(@PathVariable String pluginId) {
+        // 获取指定ID的插件信息
+        System.out.println("pluginId = " + pluginId);
+
+        PluginWrapper plugin = pluginManager.getPlugin(pluginId);
+
+
+        if (plugin == null) {
+            return R.ok("未获取到插件信息。");
+        } else {
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("id", plugin.getPluginId());
+            map.put("version", plugin.getDescriptor().getVersion());
+            map.put("path", plugin.getPluginPath().toString());
+            map.put("state", plugin.getPluginState());
+            map.put("class", plugin.getDescriptor().getPluginClass());
+            map.put("requires", plugin.getDescriptor().getRequires());
+            map.put("provider", plugin.getDescriptor().getProvider());
+            map.put("license", plugin.getDescriptor().getLicense());
+            map.put("dependencies", plugin.getDescriptor().getDependencies());
+
+            return R.ok(map);
+        }
+    }
+
+//    @RequestMapping(value = "/extensions/list")
+//    public List<String> listExtensions() {
+//        List<PluginRegister> registers = pluginManager.getExtensions(PluginRegister.class);
+//        return registers.stream().map(PluginRegister::name).collect(Collectors.toList());
+//    }
+
+
+}

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

@@ -61,7 +61,7 @@ spring:
     # 国际化资源文件路径
     basename: i18n/messages
   profiles:
-    active: test
+    active: dev
   # 文件上传
   servlet:
     multipart:
@@ -134,7 +134,7 @@ xss:
 
 sys:
   gateway:
-    url: http://localhost:8080
+    url: http://localhost:8081
   report:
     upload:
       path: D:/tmp/report/

+ 3 - 3
ruoyi-admin/src/main/resources/kms_client.yml

@@ -1,11 +1,11 @@
 # 服务地址 格式 ip:port
-address: 10.0.249.142:19190
+address: 172.20.155.26:19190
 ## 以下两项使用kcsp平台的kms必填,均由kcsp平台提供。非kcsp平台可为空
 #contextPath: /KMSapi/kcspKMSv1RSFFkoal
 #kid: 166086164895733350518985b591e713
 #以下两项必填(由kms aksk 管理解密生成)
-accessKey: scj2D1OC4EqnnHYf
-secretKey: ZNdERcfhEoFJuSlI
+accessKey: tJtvUpcSyEJlZIgT
+secretKey: Q6HBcMqrZtfdkbEQ
 
 #是否使用tls
 useTLS: false

+ 4 - 13
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/BizDataShowConfigController.java

@@ -1,10 +1,8 @@
 package com.ruoyi.interfaces.controller;
 
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.interfaces.domain.BizDataShowConfig;
 import com.ruoyi.interfaces.service.BizDataShowConfigService;
 import io.swagger.annotations.Api;
@@ -51,12 +49,9 @@ public class BizDataShowConfigController extends BaseController {
      * 查询分页列表
      */
     @GetMapping("/listOfPage")
-    public TableDataInfo listOfPage(BizDataShowConfig ptServiceAlarm) {
+    public TableDataInfo listOfPage(BizDataShowConfig bizDataShowConfig) {
         startPage();
-        QueryWrapper<BizDataShowConfig> queryWrapper = new QueryWrapper<>();
-        queryWrapper
-                .like(StringUtils.isNotBlank(ptServiceAlarm.getName()), "NAME", ptServiceAlarm.getName());
-        List<BizDataShowConfig> list = bizDataShowConfigService.list(queryWrapper);
+        List<BizDataShowConfig> list = bizDataShowConfigService.list(bizDataShowConfig);
         return getDataTable(list);
     }
 
@@ -64,12 +59,8 @@ public class BizDataShowConfigController extends BaseController {
      * 查询列表
      */
     @GetMapping("/list")
-    public AjaxResult list(BizDataShowConfig ptServiceAlarm) {
-        QueryWrapper<BizDataShowConfig> queryWrapper = new QueryWrapper<>();
-        queryWrapper
-                .like(StringUtils.isNotBlank(ptServiceAlarm.getName()), "NAME", ptServiceAlarm.getName())
-                .orderByAsc("SORT");
-        List<BizDataShowConfig> list = bizDataShowConfigService.list(queryWrapper);
+    public AjaxResult list(BizDataShowConfig bizDataShowConfig) {
+        List<BizDataShowConfig> list = bizDataShowConfigService.list(bizDataShowConfig);
         return AjaxResult.success(list);
     }
 

+ 40 - 20
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdDataSetController.java

@@ -1,28 +1,27 @@
 package com.ruoyi.interfaces.controller;
 
-import java.util.HashMap;
-import java.util.List;
-import javax.servlet.http.HttpServletResponse;
-
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.interfaces.domain.MdDataField;
-import com.ruoyi.interfaces.service.*;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.interfaces.domain.MdDataSet;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.interfaces.domain.MdDataCate;
+import com.ruoyi.interfaces.domain.MdDataField;
+import com.ruoyi.interfaces.domain.MdDataSet;
+import com.ruoyi.interfaces.service.IMdDataCateService;
+import com.ruoyi.interfaces.service.IMdDataFieldService;
+import com.ruoyi.interfaces.service.IMdDataSetService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 数据集Controller
@@ -37,7 +36,8 @@ public class MdDataSetController extends BaseController {
     private IMdDataSetService mdDataSetService;
     @Autowired
     private IMdDataFieldService mdDataFieldService;
-
+    @Autowired
+    private IMdDataCateService mdDataCateService;
 
     /**
      * 查询数据集列表
@@ -50,7 +50,6 @@ public class MdDataSetController extends BaseController {
     }
 
 
-
     /**
      * 导出数据集列表
      */
@@ -103,6 +102,27 @@ public class MdDataSetController extends BaseController {
         return toAjax(mdDataSetService.deleteMdDataSetByDsCodes(dsCodes));
     }
 
+    /**
+     * 构建数据项树状列表
+     *
+     * @param mdDataCate
+     * @return
+     */
+    @GetMapping(value = "/treeselect")
+    public AjaxResult treeselect(MdDataCate mdDataCate) {
+        List<MdDataCate> mdDataCates = mdDataCateService.selectMdDataCateList(mdDataCate).stream().peek(item -> item.setIcon("TREE")).collect(Collectors.toList());
+        List<MdDataSet> mdDataSets = mdDataSetService.selectMdDataSetList(new MdDataSet());
+        for (MdDataSet mdDataSet : mdDataSets) {
+            mdDataCates.add(new MdDataCate(mdDataSet.getDcCode(), mdDataSet.getCateId(), mdDataSet.getDcName(), "SET"));
+        }
+        return success(mdDataCateService.buildDataTreeSelect(mdDataCates));
+    }
+
+    @GetMapping("/getBizData/{id}")
+    public AjaxResult getBizDataById(@PathVariable String id) {
+        Map<String, String> params = ServletUtils.getParamMap(ServletUtils.getRequest());
+        return AjaxResult.success(mdDataSetService.getBizDataById(id,params));
+    }
 
 }
 

+ 0 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdFlowDispatchController.java

@@ -42,7 +42,6 @@ public class MdFlowDispatchController extends BaseController {
 
     @PostMapping
     public AjaxResult add(@RequestBody MdFlowDispatchVo flowDispatchVo) {
-        flowDispatchVo.setExecutorInfo(ExecutorName.runFolwJob.toString());
         flowDispatchVo.setCreateBy(getUsername());
         flowDispatchVo.setJobName(flowDispatchVo.getFlowJobName());
         return success(mdFlowDispatchService.add(flowDispatchVo));

+ 1 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/PtTreeCateController.java

@@ -159,7 +159,7 @@ public class PtTreeCateController extends BaseController {
      */
     @GetMapping(value = "/modelTreeSelect")
     public AjaxResult modelTreeSelect(PtTreeCate ptTreeCate) {
-        ptTreeCate.setTreeType("MODEL");
+        ptTreeCate.setTreeType("APP");
         String level = StringUtils.isNull(ptTreeCate.getParams().get("level")) ? "3" : ptTreeCate.getParams().get("level").toString();
         String devkind = StringUtils.isNull(ptTreeCate.getParams().get("devkind")) ? null : ptTreeCate.getParams().get("devkind").toString();
 

+ 35 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/web/SnailJobLogController.java

@@ -0,0 +1,35 @@
+package com.ruoyi.interfaces.controller.web;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.interfaces.domain.JobLogVo;
+import com.ruoyi.interfaces.domain.vo.JobRequestVo;
+import com.ruoyi.interfaces.service.SnailJobService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/snail/job/log")
+public class SnailJobLogController extends BaseController {
+
+    @Autowired
+    private SnailJobService snailJobService;
+
+    @GetMapping("/listOfPage")
+    public TableDataInfo listOfPage(JobRequestVo jobRequestVo) {
+        startPage();
+        List<JobLogVo> list = snailJobService.listOfJobLog(jobRequestVo);
+        return getDataTable(list);
+    }
+
+    @GetMapping("/statistics")
+    public AjaxResult statistics() {
+        return AjaxResult.success(snailJobService.statistics());
+    }
+
+}

+ 21 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/JobLogVo.java

@@ -0,0 +1,21 @@
+package com.ruoyi.interfaces.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class JobLogVo extends Job {
+
+    /**
+     * 任务状态
+     */
+    private String taskStatus;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date executionTime;
+
+    private String resultMessage;
+
+}

+ 22 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/MdDataSet.java

@@ -1,9 +1,9 @@
 package com.ruoyi.interfaces.domain;
 
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 
 /**
  * 数据集对象 md_data_set
@@ -41,6 +41,26 @@ public class MdDataSet extends BaseEntity
     @Excel(name = "数据集说明")
     private String dcNote;
 
+    private String queryOptions;
+
+    private String renderingOptions;
+
+    public String getQueryOptions() {
+        return queryOptions;
+    }
+
+    public void setQueryOptions(String queryOptions) {
+        this.queryOptions = queryOptions;
+    }
+
+    public String getRenderingOptions() {
+        return renderingOptions;
+    }
+
+    public void setRenderingOptions(String renderingOptions) {
+        this.renderingOptions = renderingOptions;
+    }
+
     public void setDcCode(String dcCode)
     {
         this.dcCode = dcCode;

+ 23 - 3
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/vo/JobRequestVo.java

@@ -1,17 +1,37 @@
 package com.ruoyi.interfaces.domain.vo;
 
 import com.aizuda.snailjob.model.request.base.JobRequest;
-import com.ruoyi.common.core.domain.BaseEntity;
-import lombok.Data;
 
+import java.util.Date;
 import java.util.HashMap;
 
 public class JobRequestVo extends JobRequest {
-    private HashMap<String,Object> argsStrMap;
+    private HashMap<String, Object> argsStrMap;
+
     private String[] shardingArgs;
 
     private Integer shardNum;
 
+    private Date startTime;
+
+    private Date endTime;
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
     public Integer getShardNum() {
         return shardNum;
     }

+ 15 - 4
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/BizDataShowConfigMapper.java

@@ -1,11 +1,12 @@
 package com.ruoyi.interfaces.mapper;
 
-import com.baomidou.dynamic.datasource.annotation.DS;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.common.annotation.DataSource;
 import com.ruoyi.common.enums.DataSourceType;
 import com.ruoyi.interfaces.domain.BizDataShowConfig;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * 模型文件信息Mapper接口
  *
@@ -13,7 +14,17 @@ import org.apache.ibatis.annotations.Mapper;
  * @date 2025-07-15
  */
 @Mapper
-@DS("SLAVE")
-public interface BizDataShowConfigMapper extends BaseMapper<BizDataShowConfig> {
+@DataSource(DataSourceType.SLAVE)
+public interface BizDataShowConfigMapper {
+
+    int insert(BizDataShowConfig entity);
+
+    int update(BizDataShowConfig entity);
+
+    BizDataShowConfig get(String id);
+
+    int delete(String id);
+
+    List<BizDataShowConfig> findList(BizDataShowConfig entity);
 
 }

+ 5 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/SnailJobMapper.java

@@ -3,6 +3,7 @@ package com.ruoyi.interfaces.mapper;
 import com.ruoyi.common.annotation.DataSource;
 import com.ruoyi.common.enums.DataSourceType;
 import com.ruoyi.interfaces.domain.Job;
+import com.ruoyi.interfaces.domain.JobLogVo;
 import com.ruoyi.interfaces.domain.vo.JobRequestVo;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -12,4 +13,8 @@ import java.util.List;
 @DataSource(DataSourceType.JOB)
 public interface SnailJobMapper {
     List<Job> selectJobList(Job jobRequestVo);
+
+    List<JobLogVo> listOfJobLog(JobRequestVo jobRequestVo);
+
+    Long listOfJobLogCount(JobRequestVo jobRequestVo);
 }

+ 21 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/BizDataShowConfigService.java

@@ -1,11 +1,30 @@
 package com.ruoyi.interfaces.service;
 
-import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.interfaces.domain.BizDataShowConfig;
 
 import java.io.IOException;
+import java.util.List;
 
-public interface BizDataShowConfigService extends IService<BizDataShowConfig> {
+public interface BizDataShowConfigService {
+
+    default boolean saveOrUpdate(BizDataShowConfig bizDataShowConfig) {
+        if (StringUtils.isNotBlank(bizDataShowConfig.getId())) {
+            return updateById(bizDataShowConfig);
+        }
+
+        return save(bizDataShowConfig);
+    }
+
+    boolean save(BizDataShowConfig bizDataShowConfig);
+
+    boolean updateById(BizDataShowConfig bizDataShowConfig);
+
+    BizDataShowConfig getById(String id);
+
+    boolean removeById(String id);
+
+    List<BizDataShowConfig> list(BizDataShowConfig bizDataShowConfig);
 
     boolean testApi(String apiConfig) throws IOException;
 

+ 1 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdDataCateService.java

@@ -1,7 +1,5 @@
 package com.ruoyi.interfaces.service;
 
-import com.ruoyi.common.core.domain.TreeSelect;
-import com.ruoyi.common.core.domain.entity.SysMenu;
 import com.ruoyi.interfaces.domain.MdDataCate;
 import com.ruoyi.interfaces.domain.TreeSelectString;
 
@@ -63,4 +61,5 @@ public interface IMdDataCateService
      */
     public int deleteMdDataCateByCateId(String cateId);
     public List<TreeSelectString> buildDataTreeSelect(List<MdDataCate> menus);
+
 }

+ 6 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdDataSetService.java

@@ -1,8 +1,10 @@
 package com.ruoyi.interfaces.service;
 
-import java.util.List;
 import com.ruoyi.interfaces.domain.MdDataSet;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 数据集Service接口
  * 
@@ -58,4 +60,7 @@ public interface IMdDataSetService
      * @return 结果
      */
     public int deleteMdDataSetByDsCode(String dsCode);
+
+    Object getBizDataById(String id, Map<String, String> params);
+
 }

+ 2 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdModelInfoService.java

@@ -68,4 +68,6 @@ public interface IMdModelInfoService
     int modelSetRela(MdModelSetRelaVo modelSet);
 
     int deleteModelSetRela(MdModelSetRelaVo modelSet);
+
+    Long count();
 }

+ 16 - 8
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/SnailJobService.java

@@ -1,27 +1,31 @@
 package com.ruoyi.interfaces.service;
 
 import com.ruoyi.interfaces.domain.Job;
+import com.ruoyi.interfaces.domain.JobLogVo;
 import com.ruoyi.interfaces.domain.vo.JobRequestVo;
 
 import java.util.List;
+import java.util.Map;
 
 public interface SnailJobService {
     List<Job> selectJobList(Job jobRequestVo);
 
     public Long addClusterJob(JobRequestVo jobRequest);
-    public Boolean updateClusterJob(JobRequestVo jobRequest) ;
+
+    public Boolean updateClusterJob(JobRequestVo jobRequest);
+
     /**
      * 新增广播模式的任务
      *
      * @param jobRequest
      * @return
      */
-    public Long addBroadcastJob(JobRequestVo jobRequest) ;
+    public Long addBroadcastJob(JobRequestVo jobRequest);
 
     /**
      * 修改广播任务
      */
-    public Boolean updateBroadcastJob(JobRequestVo jobRequest) ;
+    public Boolean updateBroadcastJob(JobRequestVo jobRequest);
 
     /**
      * 新增Sharding模式的任务
@@ -29,7 +33,7 @@ public interface SnailJobService {
      * @param jobRequest 任务名称
      * @return 任务id
      */
-    public Long addShardingJob(JobRequestVo jobRequest) ;
+    public Long addShardingJob(JobRequestVo jobRequest);
 
     /**
      * 修改Sharding模式的任务
@@ -37,7 +41,7 @@ public interface SnailJobService {
      * @param jobRequest 任务名称
      * @return 任务id
      */
-    public Boolean updateShardingJob(JobRequestVo jobRequest) ;
+    public Boolean updateShardingJob(JobRequestVo jobRequest);
 
     /**
      * 新增Map模式的任务
@@ -45,7 +49,7 @@ public interface SnailJobService {
      * @param jobRequest 任务名称
      * @return 任务id
      */
-    public Long addMapJob(JobRequestVo jobRequest) ;
+    public Long addMapJob(JobRequestVo jobRequest);
 
 
     /**
@@ -54,7 +58,7 @@ public interface SnailJobService {
      * @param jobRequest
      * @return
      */
-    public Boolean updateMapJob(JobRequestVo jobRequest) ;
+    public Boolean updateMapJob(JobRequestVo jobRequest);
 
     /**
      * 新增MapReduce模式的任务
@@ -62,7 +66,7 @@ public interface SnailJobService {
      * @param jobRequest
      * @return 任务id
      */
-    public Long addMapReduceJob(JobRequestVo jobRequest) ;
+    public Long addMapReduceJob(JobRequestVo jobRequest);
 
     /**
      * 修改MapReduce模式的任务
@@ -71,4 +75,8 @@ public interface SnailJobService {
      * @return 任务id
      */
     public Boolean updateMapReduceJob(JobRequestVo jobRequest);
+
+    List<JobLogVo> listOfJobLog(JobRequestVo jobRequestVo);
+
+    Map<String, Long> statistics();
 }

+ 26 - 4
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/BizDataShowConfigServiceImpl.java

@@ -1,6 +1,5 @@
 package com.ruoyi.interfaces.service.impl;
 
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.core.api.BizDataApiRequest;
 import com.ruoyi.common.exception.CheckException;
 import com.ruoyi.common.utils.uuid.IdUtils;
@@ -10,24 +9,47 @@ import com.ruoyi.interfaces.service.BizDataShowConfigService;
 import org.springframework.stereotype.Service;
 
 import java.util.Date;
+import java.util.List;
 import java.util.Optional;
 
 @Service
-public class BizDataShowConfigServiceImpl extends ServiceImpl<BizDataShowConfigMapper, BizDataShowConfig> implements BizDataShowConfigService {
+public class BizDataShowConfigServiceImpl implements BizDataShowConfigService {
+
+    private final BizDataShowConfigMapper bizDataShowConfigMapper;
+
+    public BizDataShowConfigServiceImpl(BizDataShowConfigMapper bizDataShowConfigMapper) {
+        this.bizDataShowConfigMapper = bizDataShowConfigMapper;
+    }
 
     @Override
     public boolean save(BizDataShowConfig entity) {
         entity.setId(IdUtils.simpleUUID());
         entity.setCreateTime(new Date());
-        return super.save(entity);
+        return bizDataShowConfigMapper.insert(entity) > 0;
     }
 
     @Override
     public boolean updateById(BizDataShowConfig entity) {
         entity.setUpdateTime(new Date());
-        return super.updateById(entity);
+        return bizDataShowConfigMapper.update(entity) > 0;
     }
 
+    @Override
+    public BizDataShowConfig getById(String id) {
+        return bizDataShowConfigMapper.get(id);
+    }
+
+    @Override
+    public boolean removeById(String id) {
+        return bizDataShowConfigMapper.delete(id) > 0;
+    }
+
+    @Override
+    public List<BizDataShowConfig> list(BizDataShowConfig bizDataShowConfig) {
+        return bizDataShowConfigMapper.findList(bizDataShowConfig);
+    }
+
+
     @Override
     public boolean testApi(String apiConfig) {
         Object result = BizDataApiRequest.requestByQueryOptions(apiConfig);

+ 9 - 7
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdDataCateServiceImpl.java

@@ -1,14 +1,10 @@
 package com.ruoyi.interfaces.service.impl;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import com.ruoyi.common.core.domain.TreeSelect;
-import com.ruoyi.common.core.domain.entity.SysMenu;
+import com.ruoyi.common.core.api.BizDataApiRequest;
+import com.ruoyi.common.exception.CheckException;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.uuid.IdUtils;
+import com.ruoyi.interfaces.domain.BizDataShowConfig;
 import com.ruoyi.interfaces.domain.MdDataCate;
 import com.ruoyi.interfaces.domain.TreeSelectString;
 import com.ruoyi.interfaces.mapper.MdDataCateMapper;
@@ -16,6 +12,12 @@ import com.ruoyi.interfaces.service.IMdDataCateService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
 /**
  * 数据项分类管理Service业务层处理
  * 

+ 31 - 26
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdDataSetServiceImpl.java

@@ -1,96 +1,101 @@
 package com.ruoyi.interfaces.service.impl;
 
-import java.util.List;
-
+import com.ruoyi.common.core.api.BizDataApiRequest;
+import com.ruoyi.common.exception.CheckException;
 import com.ruoyi.common.utils.uuid.IdUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import com.ruoyi.interfaces.mapper.MdDataSetMapper;
 import com.ruoyi.interfaces.domain.MdDataSet;
+import com.ruoyi.interfaces.mapper.MdDataSetMapper;
 import com.ruoyi.interfaces.service.IMdDataSetService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 
 /**
  * 数据集Service业务层处理
- * 
+ *
  * @author 朱得糠
  * @date 2025-07-30
  */
 @Service
-public class MdDataSetServiceImpl implements IMdDataSetService 
-{
+public class MdDataSetServiceImpl implements IMdDataSetService {
     @Autowired
     private MdDataSetMapper mdDataSetMapper;
 
     /**
      * 查询数据集
-     * 
+     *
      * @param dsCode 数据集主键
      * @return 数据集
      */
     @Override
-    public MdDataSet selectMdDataSetByDsCode(String dsCode)
-    {
+    public MdDataSet selectMdDataSetByDsCode(String dsCode) {
         return mdDataSetMapper.selectMdDataSetByDsCode(dsCode);
     }
 
     /**
      * 查询数据集列表
-     * 
+     *
      * @param mdDataSet 数据集
      * @return 数据集
      */
     @Override
-    public List<MdDataSet> selectMdDataSetList(MdDataSet mdDataSet)
-    {
+    public List<MdDataSet> selectMdDataSetList(MdDataSet mdDataSet) {
         return mdDataSetMapper.selectMdDataSetList(mdDataSet);
     }
 
     /**
      * 新增数据集
-     * 
+     *
      * @param mdDataSet 数据集
      * @return 结果
      */
     @Override
-    public int insertMdDataSet(MdDataSet mdDataSet)
-    {
+    public int insertMdDataSet(MdDataSet mdDataSet) {
         mdDataSet.setDcCode(IdUtils.fastUUID());
         return mdDataSetMapper.insertMdDataSet(mdDataSet);
     }
 
     /**
      * 修改数据集
-     * 
+     *
      * @param mdDataSet 数据集
      * @return 结果
      */
     @Override
-    public int updateMdDataSet(MdDataSet mdDataSet)
-    {
+    public int updateMdDataSet(MdDataSet mdDataSet) {
         return mdDataSetMapper.updateMdDataSet(mdDataSet);
     }
 
     /**
      * 批量删除数据集
-     * 
+     *
      * @param dsCodes 需要删除的数据集主键
      * @return 结果
      */
     @Override
-    public int deleteMdDataSetByDsCodes(String[] dsCodes)
-    {
+    public int deleteMdDataSetByDsCodes(String[] dsCodes) {
         return mdDataSetMapper.deleteMdDataSetByDsCodes(dsCodes);
     }
 
     /**
      * 删除数据集信息
-     * 
+     *
      * @param dsCode 数据集主键
      * @return 结果
      */
     @Override
-    public int deleteMdDataSetByDsCode(String dsCode)
-    {
+    public int deleteMdDataSetByDsCode(String dsCode) {
         return mdDataSetMapper.deleteMdDataSetByDsCode(dsCode);
     }
+
+    @Override
+    public Object getBizDataById(String id, Map<String, String> params) {
+        MdDataSet mdDataSet = selectMdDataSetByDsCode(id);
+        Optional.ofNullable(mdDataSet).orElseThrow(() -> new CheckException("id.no"));
+        return BizDataApiRequest.requestByQueryOptions(mdDataSet.getQueryOptions(), params);
+    }
+
 }

+ 10 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdModelInfoServiceImpl.java

@@ -1,7 +1,5 @@
 package com.ruoyi.interfaces.service.impl;
 
-import java.util.List;
-
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.SM4Util;
 import com.ruoyi.common.utils.StringUtils;
@@ -16,6 +14,8 @@ import com.ruoyi.interfaces.service.PtServiceService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * 模型信息Service业务层处理
  *
@@ -155,4 +155,12 @@ public class MdModelInfoServiceImpl implements IMdModelInfoService {
         }
         return i;
     }
+
+    @Override
+    public Long count() {
+        MdModelInfo mdModelInfo = new MdModelInfo();
+        mdModelInfo.setDevkind("APP");
+        List<MdModelInfo> mdModelInfoList = mdModelInfoMapper.selectMdModelInfoList(mdModelInfo);
+        return mdModelInfoList.stream().count();
+    }
 }

+ 8 - 18
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/PtServiceMonitorServiceImpl.java

@@ -3,9 +3,10 @@ package com.ruoyi.interfaces.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ruoyi.common.core.builder.MapBuilder;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.interfaces.domain.MdModelInfo;
 import com.ruoyi.interfaces.domain.PtService;
-import com.ruoyi.interfaces.domain.PtServiceLog;
 import com.ruoyi.interfaces.mapper.PtServiceMonitorMapper;
+import com.ruoyi.interfaces.service.IMdModelInfoService;
 import com.ruoyi.interfaces.service.IPtServiceLogService;
 import com.ruoyi.interfaces.service.IPtServiceMonitorService;
 import com.ruoyi.interfaces.service.PtServiceService;
@@ -24,6 +25,9 @@ import java.util.stream.Collectors;
 @Service
 public class PtServiceMonitorServiceImpl implements IPtServiceMonitorService {
 
+    @Autowired
+    private IMdModelInfoService mdModelInfoService;
+
     @Autowired
     private PtServiceService ptServiceService;
 
@@ -37,28 +41,14 @@ public class PtServiceMonitorServiceImpl implements IPtServiceMonitorService {
     public List<Map<String, Object>> statisticData() {
         List<Map<String, Object>> statisticData = new ArrayList<>(5);
 
+        statisticData.add(MapBuilder.of("name", "模型总数", "value", mdModelInfoService.count(), "color", "#477ACF"));
         QueryWrapper<PtService> wrapper = new QueryWrapper<>();
         wrapper.eq("status", "1");
         long count = ptServiceService.count(wrapper);
-        statisticData.add(MapBuilder.of("name", "当前服务总数", "value", count, "color", "#477ACF"));
+        statisticData.add(MapBuilder.of("name", "当前服务总数", "value", count, "color", "#40B0D7"));
         long total = ptServiceService.count();
         BigDecimal result = new BigDecimal(count).multiply(new BigDecimal(100)).divide(new BigDecimal(total), 2, RoundingMode.HALF_UP);
-        statisticData.add(MapBuilder.of("name", "服务在线比率", "value", result.setScale(1, RoundingMode.HALF_UP) + "%", "color", "#40B0D7"));
-        // TODO: 模型服务支撑健康指数
-        QueryWrapper<PtServiceLog> logWrapper = new QueryWrapper<>();
-        logWrapper.eq("status_code", "200");
-        long goodCount = ptServiceLogService.count(logWrapper);
-        long logTotal = ptServiceLogService.count();
-        BigDecimal logResult = new BigDecimal(goodCount).multiply(new BigDecimal(100)).divide(new BigDecimal(logTotal), 2, RoundingMode.HALF_UP);
-        String index = "优";
-        if (logResult.compareTo(new BigDecimal(80)) < 0) {
-            index = "差";
-        } else if (logResult.compareTo(new BigDecimal(90)) < 0) {
-            index = "良";
-        } else {
-            index = "优";
-        }
-        statisticData.add(MapBuilder.of("name", "模型服务支撑健康指数", "value", index, "color", "#2DBEA2"));
+        statisticData.add(MapBuilder.of("name", "服务在线比率", "value", result.setScale(1, RoundingMode.HALF_UP) + "%", "color", "#2DBEA2"));
         // TODO: 当月热点服务
         List<Map<String, Object>> monthModelCallMaxCount = getMonthModelCallMaxCount();
         String monthModelCallMaxCountName = monthModelCallMaxCount.stream().map(item -> (String) item.get("NAME")).collect(Collectors.joining(","));

+ 2 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/PtServiceServiceImpl.java

@@ -144,8 +144,8 @@ public class PtServiceServiceImpl extends ServiceImpl<PtServiceMapper, PtService
     @Override
     public HashMap<String, String> testRun(PtService ptService) throws IOException {
 
-        //String url = RuoYiConfig.getGatewayUrl() + ptService.getProxyPath() + ptService.getUrl();
-        String url = ptService.getUrl();
+        String url = RuoYiConfig.getGatewayUrl() + ptService.getProxyPath() + ptService.getUrl();
+        //String url = ptService.getUrl();
         HashMap<String, String> headers = new HashMap<>();
         List<PtServiceParam> params = ptService.getParams();
         HttpUtils.setGatewayHeaders(headers);

+ 26 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/SnailJobServiceImpl.java

@@ -9,8 +9,10 @@ import com.aizuda.snailjob.client.job.core.handler.update.ClusterUpdateHandler;
 import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
 import com.aizuda.snailjob.common.core.enums.JobBlockStrategyEnum;
 import com.aizuda.snailjob.common.core.enums.StatusEnum;
+import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.interfaces.domain.Job;
+import com.ruoyi.interfaces.domain.JobLogVo;
 import com.ruoyi.interfaces.domain.vo.JobRequestVo;
 import com.ruoyi.interfaces.mapper.SnailJobMapper;
 import com.ruoyi.interfaces.service.SnailJobService;
@@ -18,8 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 
 @Service
 public class SnailJobServiceImpl implements SnailJobService {
@@ -271,6 +272,29 @@ public class SnailJobServiceImpl implements SnailJobService {
 
     }
 
+    @Override
+    public List<JobLogVo> listOfJobLog(JobRequestVo jobRequestVo) {
+        return snailJobMapper.listOfJobLog(jobRequestVo);
+    }
+
+    @Override
+    public Map<String, Long> statistics() {
+        Map<String, Long> ret = new HashMap<>();
+        List<Job> jobs = snailJobMapper.selectJobList(null);
+        ret.put("total", jobs.stream().count());
+        Long jobRuns = snailJobMapper.listOfJobLogCount(null);
+        ret.put("runTotal", jobRuns);
+        JobRequestVo jobRequestVo = new JobRequestVo();
+        jobRequestVo.setStartTime(new Date());
+        Long nowJobRuns = snailJobMapper.listOfJobLogCount(jobRequestVo);
+        ret.put("nowRunTotal", nowJobRuns);
+        JobRequestVo jobRequestVo2 = new JobRequestVo();
+        jobRequestVo2.setStartTime(DateUtils.parseDate(DateUtils.dateTimeBeforeDays(DateUtils.YYYY_MM_DD, 30)));
+        Long monthJobRuns = snailJobMapper.listOfJobLogCount(jobRequestVo2);
+        ret.put("monthRunTotal", monthJobRuns);
+        return ret;
+    }
+
 
     public AllocationAlgorithmEnum getAllocationAlgorithmEnum(Integer type) {
         for (AllocationAlgorithmEnum algorithm : AllocationAlgorithmEnum.values()) {

+ 30 - 27
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/snailJob/CleanDataJob.java

@@ -4,19 +4,9 @@ import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
 import com.aizuda.snailjob.client.job.core.dto.JobArgs;
 import com.aizuda.snailjob.model.dto.ExecuteResult;
 import com.alibaba.fastjson2.JSONObject;
-import com.ruoyi.common.core.api.BizDataApiRequest;
-import com.ruoyi.common.core.domain.BizDataApiParam;
-import com.ruoyi.common.dataobject.DataTable;
-import com.ruoyi.common.exception.CheckException;
-import com.ruoyi.common.transition.CleanTransition;
 import com.ruoyi.common.utils.JsonUtils;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
 @Component
 public class CleanDataJob {
 
@@ -26,22 +16,35 @@ public class CleanDataJob {
     public ExecuteResult cleanDataJob(JobArgs jobArgs) {
         String paramsString = String.valueOf(jobArgs.getJobParams());
         JSONObject configObject = JsonUtils.jsonToPojo(paramsString, JSONObject.class);
-
-        // 1. 获取数据
-        String queryOptions = Optional.ofNullable(configObject).map(a -> a.getString("queryOptions")).orElseThrow(() -> new CheckException("请检查数据源配置!"));
-        String ruleListString = Optional.ofNullable(configObject).map(a -> a.getString("ruleList")).orElseThrow(() -> new CheckException("请检查数据源配置!"));
-        List<Map<String, Object>> data = (List<Map<String, Object>>) BizDataApiRequest.requestByQueryOptions(queryOptions);
-        List<BizDataApiParam> columnList = BizDataApiRequest.getColumns(queryOptions);
-        Optional.ofNullable(columnList).orElseThrow(() -> new CheckException("请检查数据源配置!"));
-        List<String> columns = columnList.stream().map(BizDataApiParam::getKey).collect(Collectors.toList());
-        DataTable dataTable = new DataTable(columns, data);
-
-        List<Map> ruleList = JsonUtils.jsonToList(ruleListString, Map.class);
-        // 2. 清洗转换数据
-        CleanTransition cleanTransition = new CleanTransition();
-        com.alibaba.fastjson2.JSONObject parameter = new com.alibaba.fastjson2.JSONObject();
-        parameter.put("tableFields", ruleList);
-        cleanTransition.transition(dataTable, parameter);
-        return ExecuteResult.success("执行成功");
+        return ExecuteResult.failure("参数配置异常!");
+//        if (configObject == null) {
+//            return ExecuteResult.failure("参数配置异常!");
+//        }
+//        // 1. 获取数据
+//        String queryOptions = Optional.ofNullable(configObject).map(a -> a.getString("queryOptions")).orElse(null);
+//        if (StringUtils.isNotBlank(queryOptions)) {
+//            return ExecuteResult.failure("数据源配置异常!");
+//        }
+//        String ruleListString = Optional.ofNullable(configObject).map(a -> a.getString("ruleList")).orElse(null);
+//        if (StringUtils.isNotBlank(ruleListString)) {
+//            return ExecuteResult.failure("清洗规则配置异常!");
+//        }
+//        List<Map<String, Object>> data = (List<Map<String, Object>>) BizDataApiRequest.requestByQueryOptions(queryOptions);
+//        if (data == null || data.isEmpty()) {
+//            return ExecuteResult.failure("未获取到数据!");
+//        }
+//        List<BizDataApiParam> columnList = BizDataApiRequest.getColumns(queryOptions);
+//        if (columnList == null || columnList.isEmpty()) {
+//            return ExecuteResult.failure("参数列表获取异常!");
+//        }
+//        List<String> columns = columnList.stream().map(BizDataApiParam::getKey).collect(Collectors.toList());
+//        DataTable dataTable = new DataTable(columns, data);
+//        List<Map> ruleList = JsonUtils.jsonToList(ruleListString, Map.class);
+//        // 2. 清洗转换数据
+//        CleanTransition cleanTransition = new CleanTransition();
+//        com.alibaba.fastjson2.JSONObject parameter = new com.alibaba.fastjson2.JSONObject();
+//        parameter.put("tableFields", ruleList);
+//        DataTable resultTable = cleanTransition.transition(dataTable, parameter);
+//        return ExecuteResult.success(JsonUtils.objectToJson(resultTable));
     }
 }

+ 124 - 0
ruoyi-api-patform/src/main/resources/mapper/interfaces/BizDataShowConfigMapper.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.interfaces.mapper.BizDataShowConfigMapper">
+
+    <resultMap type="com.ruoyi.interfaces.domain.BizDataShowConfig" id="bizDataShowConfigResultMap">
+        <result column="ID" property="id"/>
+        <result column="NAME" property="name"/>
+        <result column="TYPE" property="type"/>
+        <result column="POSITION" property="position"/>
+        <result column="QUERY_OPTIONS" property="queryOptions"/>
+        <result column="RENDERING_OPTIONS" property="renderingOptions"/>
+        <result column="SORT" property="sort"/>
+        <result column="APP_ID" property="appId"/>
+        <result column="CREATE_TIME" property="createTime"/>
+        <result column="UPDATE_TIME" property="updateTime"/>
+    </resultMap>
+
+    <sql id="table_columns">
+        ID , NAME, TYPE, POSITION, QUERY_OPTIONS, RENDERING_OPTIONS, SORT, APP_ID, CREATE_TIME, UPDATE_TIME
+    </sql>
+    <sql id="entity_properties">
+        #{id}, #{name}, #{type}, #{position}, #{queryOptions}, #{renderingOptions}, #{sort}, #{appId}, #{createTime}, #{updateTime}
+    </sql>
+
+    <!-- 使用like用法:columnName like concat('%',#columnName#,'%') -->
+    <sql id="page_where">
+        <trim prefix="where" prefixOverrides="and | or ">
+            <if test="id != null  and id != ''">and ID = #{id}</if>
+            <if test="name != null  and name != ''">and NAME like CONCAT('%', #{name}, '%')</if>
+            <if test="type != null  and type != ''">and TYPE = #{type}</if>
+            <if test="position != null  and position != ''">and POSITION = #{position}</if>
+            <if test="appId != null  and appId != ''">and APP_ID = #{appId}</if>
+            <if test="createTime != null ">and CREATE_TIME = #{createTime}</if>
+            <if test="updateTime != null ">and UPDATE_TIME = #{updateTime}</if>
+        </trim>
+    </sql>
+
+
+    <select id="get" resultMap="bizDataShowConfigResultMap" parameterType="String">
+        select
+        <include refid="table_columns"/>
+        from BIZ_DATA_SHOW_CONFIG where ID = #{id}
+    </select>
+
+    <select id="getBy" resultMap="bizDataShowConfigResultMap">
+        select
+        <include refid="table_columns"/>
+        from BIZ_DATA_SHOW_CONFIG
+        <include refid="page_where"/>
+    </select>
+
+    <select id="findAll" resultMap="bizDataShowConfigResultMap">
+        select
+        <include refid="table_columns"/>
+        from BIZ_DATA_SHOW_CONFIG
+    </select>
+
+    <select id="findList" resultMap="bizDataShowConfigResultMap">
+        select
+        <include refid="table_columns"/>
+        from BIZ_DATA_SHOW_CONFIG
+        <include refid="page_where"/>
+        ORDER BY SORT ASC
+    </select>
+
+    <select id="selectCount" resultType="int">
+        select count(ID) from BIZ_DATA_SHOW_CONFIG
+        <include refid="page_where"/>
+    </select>
+
+
+    <insert id="insert" parameterType="com.ruoyi.interfaces.domain.BizDataShowConfig">
+        insert into BIZ_DATA_SHOW_CONFIG(
+        <include refid="table_columns"/>
+        )
+        values (
+        <include refid="entity_properties"/>
+        )
+    </insert>
+
+    <delete id="delete" parameterType="java.lang.String">
+        delete BIZ_DATA_SHOW_CONFIG
+        where ID = #{id}
+    </delete>
+
+    <delete id="deleteBy" parameterType="com.ruoyi.interfaces.domain.BizDataShowConfig">
+        update BIZ_DATA_SHOW_CONFIG set DATA_STAT='9'
+        <include refid="page_where"/>
+    </delete>
+
+    <update id="deleteInFlag" parameterType="java.lang.String">
+        update BIZ_DATA_SHOW_CONFIG
+        set DATA_STAT = '9'
+        where ID = #{id}
+    </update>
+
+    <update id="update" parameterType="com.ruoyi.interfaces.domain.BizDataShowConfig">
+        update BIZ_DATA_SHOW_CONFIG
+        <trim prefix="set" suffixOverrides=",">
+            <if test="name != null  and name != ''">NAME = #{name},</if>
+            <if test="type != null  and type != ''">TYPE = #{type},</if>
+            <if test="position != null  and position != ''">POSITION = #{position},</if>
+            <if test="queryOptions != null  and queryOptions != ''">QUERY_OPTIONS = #{queryOptions},</if>
+            <if test="renderingOptions != null  and renderingOptions != ''">RENDERING_OPTIONS = #{renderingOptions},</if>
+            <if test="sort != null ">SORT = #{sort},</if>
+            <if test="appId != null  and appId != ''">APP_ID = #{appId},</if>
+            <if test="createTime != null ">CREATE_TIME = #{createTime},</if>
+            <if test="updateTime != null ">UPDATE_TIME = #{updateTime},</if>
+        </trim>
+        <where>ID = #{id}</where>
+    </update>
+
+    <update id="updateBy" parameterType="com.ruoyi.interfaces.domain.BizDataShowConfig">
+        update BIZ_DATA_SHOW_CONFIG
+        <trim prefix="set" suffixOverrides=",">
+        </trim>
+        <include refid="page_where"/>
+    </update>
+
+
+    <!-- 其他自定义SQL -->
+
+
+</mapper>

+ 27 - 16
ruoyi-api-patform/src/main/resources/mapper/interfaces/MdDataSetMapper.xml

@@ -5,17 +5,20 @@
 <mapper namespace="com.ruoyi.interfaces.mapper.MdDataSetMapper">
 
     <resultMap type="com.ruoyi.interfaces.domain.MdDataSet" id="MdDataSetResult">
-        <result property="dcCode"    column="DC_CODE"    />
-        <result property="cateId"    column="CATE_ID"    />
-        <result property="dcName"    column="DC_NAME"    />
-        <result property="dcType"    column="DC_TYPE"    />
-        <result property="dcSort"    column="DC_SORT"    />
-        <result property="dcIcon"    column="DC_ICON"    />
-        <result property="dcNote"    column="DC_NOTE"    />
+        <result property="dcCode" column="DC_CODE"/>
+        <result property="cateId" column="CATE_ID"/>
+        <result property="dcName" column="DC_NAME"/>
+        <result property="dcType" column="DC_TYPE"/>
+        <result property="dcSort" column="DC_SORT"/>
+        <result property="dcIcon" column="DC_ICON"/>
+        <result property="dcNote" column="DC_NOTE"/>
+        <result property="queryOptions" column="QUERY_OPTIONS"/>
+        <result property="renderingOptions" column="RENDERING_OPTIONS"/>
     </resultMap>
 
     <sql id="selectMdDataSetVo">
-        select DC_CODE, CATE_ID, DC_NAME, DC_TYPE, DC_SORT, DC_ICON, DC_NOTE from md_data_set
+        select DC_CODE, CATE_ID, DC_NAME, DC_TYPE, DC_SORT, DC_ICON, DC_NOTE, QUERY_OPTIONS, RENDERING_OPTIONS
+        from md_data_set
     </sql>
 
     <select id="selectMdDataSetList" parameterType="com.ruoyi.interfaces.domain.MdDataSet" resultMap="MdDataSetResult">
@@ -32,7 +35,7 @@
                 select DC_CODE FROM md_model_set_rela WHERE MDID = #{params.notInMdid}
                 )
             </if>
-            <if test="params.inMdid != null ">and DC_CODE  in (
+            <if test="params.inMdid != null ">and DC_CODE in (
                 select DC_CODE FROM md_model_set_rela WHERE MDID = #{params.inMdid}
                 )
             </if>
@@ -56,6 +59,8 @@
             <if test="dcSort != null">DC_SORT,</if>
             <if test="dcIcon != null">DC_ICON,</if>
             <if test="dcNote != null">DC_NOTE,</if>
+            <if test="queryOptions != null">QUERY_OPTIONS,</if>
+            <if test="renderingOptions != null">RENDERING_OPTIONS,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="dcCode != null">#{dcCode},</if>
@@ -65,24 +70,30 @@
             <if test="dcSort != null">#{dcSort},</if>
             <if test="dcIcon != null">#{dcIcon},</if>
             <if test="dcNote != null">#{dcNote},</if>
+            <if test="queryOptions != null">#{queryOptions},</if>
+            <if test="renderingOptions != null">#{renderingOptions},</if>
         </trim>
     </insert>
 
     <update id="updateMdDataSet" parameterType="com.ruoyi.interfaces.domain.MdDataSet">
         update md_data_set
         <trim prefix="SET" suffixOverrides=",">
-            CATE_ID = #{cateId,jdbcType=VARCHAR},
-            DC_NAME = #{dcName,jdbcType=VARCHAR},
-            DC_TYPE = #{dcType,jdbcType=VARCHAR},
-            DC_SORT = #{dcSort,jdbcType=INTEGER},
-            DC_ICON = #{dcIcon,jdbcType=VARCHAR},
-            DC_NOTE = #{dcNote,jdbcType=VARCHAR},
+            <if test="cateId != null">CATE_ID = #{cateId,jdbcType=VARCHAR},</if>
+            <if test="dcName != null">DC_NAME = #{dcName,jdbcType=VARCHAR},</if>
+            <if test="dcType != null">DC_TYPE = #{dcType,jdbcType=VARCHAR},</if>
+            <if test="dcSort != null">DC_SORT = #{dcSort,jdbcType=INTEGER},</if>
+            <if test="dcIcon != null">DC_ICON = #{dcIcon,jdbcType=VARCHAR},</if>
+            <if test="dcNote != null">DC_NOTE = #{dcNote,jdbcType=VARCHAR},</if>
+            <if test="queryOptions != null">QUERY_OPTIONS =  #{queryOptions, jdbcType=VARCHAR},</if>
+            <if test="renderingOptions != null">RENDERING_OPTIONS =  #{renderingOptions, jdbcType=VARCHAR},</if>
         </trim>
         where DC_CODE = #{dcCode}
     </update>
 
     <delete id="deleteMdDataSetByDsCode" parameterType="String">
-        delete from md_data_set where DC_CODE = #{dcCode}
+        delete
+        from md_data_set
+        where DC_CODE = #{dcCode}
     </delete>
 
     <delete id="deleteMdDataSetByDsCodes" parameterType="String">

+ 87 - 55
ruoyi-api-patform/src/main/resources/mapper/interfaces/SnailJobMapper.xml

@@ -5,63 +5,62 @@
 <mapper namespace="com.ruoyi.interfaces.mapper.SnailJobMapper">
 
     <resultMap type="com.ruoyi.interfaces.domain.Job" id="JobResult">
-    <result property="id"    column="id"    />
-    <result property="namespaceId"    column="namespace_id"    />
-    <result property="groupName"    column="group_name"    />
-    <result property="jobName"    column="job_name"    />
-    <result property="argsStr"    column="args_str"    />
-    <result property="argsType"    column="args_type"    />
-    <result property="nextTriggerAt"    column="next_trigger_at"    />
-    <result property="jobStatus"    column="job_status"    />
-    <result property="taskType"    column="task_type"    />
-    <result property="routeKey"    column="route_key"    />
-    <result property="executorType"    column="executor_type"    />
-    <result property="executorInfo"    column="executor_info"    />
-    <result property="triggerType"    column="trigger_type"    />
-    <result property="triggerInterval"    column="trigger_interval"    />
-    <result property="blockStrategy"    column="block_strategy"    />
-    <result property="executorTimeout"    column="executor_timeout"    />
-    <result property="maxRetryTimes"    column="max_retry_times"    />
-    <result property="parallelNum"    column="parallel_num"    />
-    <result property="retryInterval"    column="retry_interval"    />
-    <result property="bucketIndex"    column="bucket_index"    />
-    <result property="resident"    column="resident"    />
-    <result property="notifyIds"    column="notify_ids"    />
-    <result property="ownerId"    column="owner_id"    />
-    <result property="labels"    column="labels"    />
-    <result property="description"    column="description"    />
-    <result property="extAttrs"    column="ext_attrs"    />
-    <result property="deleted"    column="deleted"    />
+        <result property="id" column="id"/>
+        <result property="namespaceId" column="namespace_id"/>
+        <result property="groupName" column="group_name"/>
+        <result property="jobName" column="job_name"/>
+        <result property="argsStr" column="args_str"/>
+        <result property="argsType" column="args_type"/>
+        <result property="nextTriggerAt" column="next_trigger_at"/>
+        <result property="jobStatus" column="job_status"/>
+        <result property="taskType" column="task_type"/>
+        <result property="routeKey" column="route_key"/>
+        <result property="executorType" column="executor_type"/>
+        <result property="executorInfo" column="executor_info"/>
+        <result property="triggerType" column="trigger_type"/>
+        <result property="triggerInterval" column="trigger_interval"/>
+        <result property="blockStrategy" column="block_strategy"/>
+        <result property="executorTimeout" column="executor_timeout"/>
+        <result property="maxRetryTimes" column="max_retry_times"/>
+        <result property="parallelNum" column="parallel_num"/>
+        <result property="retryInterval" column="retry_interval"/>
+        <result property="bucketIndex" column="bucket_index"/>
+        <result property="resident" column="resident"/>
+        <result property="notifyIds" column="notify_ids"/>
+        <result property="ownerId" column="owner_id"/>
+        <result property="labels" column="labels"/>
+        <result property="description" column="description"/>
+        <result property="extAttrs" column="ext_attrs"/>
+        <result property="deleted" column="deleted"/>
     </resultMap>
     <sql id="selectJobVo">
-        select
-            id,
-            namespace_id,
-            group_name,
-            job_name,
-            args_str,
-            args_type,
-            next_trigger_at,
-            job_status,
-            task_type,
-            route_key,
-            executor_type,
-            executor_info,
-            trigger_type,
-            trigger_interval,
-            block_strategy,
-            executor_timeout,
-            max_retry_times,
-            parallel_num,
-            retry_interval,
-            bucket_index,
-            resident,
-            notify_ids,
-            owner_id,
-            labels,
-            description,
-            ext_attrs,
-            deleted
+        select id,
+               namespace_id,
+               group_name,
+               job_name,
+               args_str,
+               args_type,
+               next_trigger_at,
+               job_status,
+               task_type,
+               route_key,
+               executor_type,
+               executor_info,
+               trigger_type,
+               trigger_interval,
+               block_strategy,
+               executor_timeout,
+               max_retry_times,
+               parallel_num,
+               retry_interval,
+               bucket_index,
+               resident,
+               notify_ids,
+               owner_id,
+               labels,
+               description,
+               ext_attrs,
+               deleted
         from SNAIL_JOB.sj_job
     </sql>
     <select id="selectJobList" resultType="com.ruoyi.interfaces.domain.Job">
@@ -81,4 +80,37 @@
         </where>
         order by id
     </select>
+    <select id="listOfJobLog" resultType="com.ruoyi.interfaces.domain.JobLogVo">
+        SELECT
+        A.*,
+        B.TASK_STATUS,
+        B.CREATE_DT AS EXECUTION_TIME
+        FROM SJ_JOB A
+        JOIN SJ_JOB_TASK B ON A.ID = B.JOB_ID
+        <where>
+            <if test="jobName != null  and jobName != ''">and A.JOB_NAME = #{jobName}</if>
+            <if test="startTime != null  and startTime != ''">
+                and B.CREATE_DT &gt;= TO_DATE(TO_CHAR(startTime, 'YYYY-MM-DD'), 'YYYY-MM-DD')
+            </if>
+            <if test="endTime != null  and endTime != ''">
+                and B.CREATE_DT &lt;= TO_DATE(TO_CHAR(endTime, 'YYYY-MM-DD'), 'YYYY-MM-DD')
+            </if>
+        </where>
+        ORDER BY B.CREATE_DT DESC
+    </select>
+    <select id="listOfJobLogCount" resultType="java.lang.Long">
+        SELECT
+        count(*)
+        FROM SJ_JOB A
+        JOIN SJ_JOB_TASK B ON A.ID = B.JOB_ID
+        <where>
+            <if test="jobName != null  and jobName != ''">and A.job_name = #{jobName}</if>
+            <if test="startTime != null">
+                and B.CREATE_DT &gt;= TO_DATE(TO_CHAR(#{startTime}, 'YYYY-MM-DD'), 'YYYY-MM-DD')
+            </if>
+            <if test="endTime != null">
+                and B.CREATE_DT &lt;= TO_DATE(TO_CHAR(#{endTime}, 'YYYY-MM-DD'), 'YYYY-MM-DD')
+            </if>
+        </where>
+    </select>
 </mapper>

+ 43 - 7
ruoyi-common/src/main/java/com/ruoyi/common/core/api/BizDataApiRequest.java

@@ -7,18 +7,31 @@ import com.ruoyi.common.core.domain.BizDataApiParam;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.OkHttpUtils;
 import com.ruoyi.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
 
 import java.util.*;
 
+@Slf4j
 public class BizDataApiRequest {
 
     public static Object requestByQueryOptions(String queryOptions) {
+        return requestByQueryOptions(queryOptions, null);
+    }
+
+    public static Object requestByQueryOptions(String queryOptions, Map<String, String> updateParams) {
         BizDataApi apiConfigObj = JsonUtils.jsonToPojo(queryOptions, BizDataApi.class);
         // 解析参数格式
         Map<String, Object> params = parseKeyValueList(apiConfigObj.getParams());
+        if (updateParams != null) {
+            for (String key : params.keySet()) {
+                if (updateParams.containsKey(key)) {
+                    params.put(key, updateParams.get(key));
+                }
+            }
+        }
         Map<String, Object> cookies = parseKeyValueList(apiConfigObj.getCookies());
         Map<String, Object> headers = parseKeyValueList(apiConfigObj.getHeaders());
-        String body = parseBody(apiConfigObj.getBody());
+        String body = parseBody(apiConfigObj.getBody(), updateParams);
         String responseResolution = apiConfigObj.getResponseResolution();
 
         String responseString = OkHttpUtils.executeRequest(
@@ -30,12 +43,26 @@ public class BizDataApiRequest {
                 headers
         );
 
-        JSONObject responseObj = JsonUtils.jsonToPojo(responseString, JSONObject.class);
-        Object result = responseObj;
-        if (StringUtils.isNotBlank(responseResolution)) {
-            result = JSONPath.eval(responseObj, responseResolution);
+        if (StringUtils.isNotBlank(responseString)) {
+            Object result = null;
+            if (responseString.startsWith("[")) {
+                List<JSONObject> responseObj = JsonUtils.jsonToList(responseString, JSONObject.class);
+                result = responseObj;
+                if (StringUtils.isNotBlank(responseResolution)) {
+                    result = JSONPath.eval(responseObj, responseResolution);
+                }
+            } else {
+                JSONObject responseObj = JsonUtils.jsonToPojo(responseString, JSONObject.class);
+                result = responseObj;
+                if (StringUtils.isNotBlank(responseResolution)) {
+                    result = JSONPath.eval(responseObj, responseResolution);
+                }
+            }
+            return result;
         }
-        return result;
+
+        log.warn("返回数据异常:{}", responseString);
+        return responseString;
     }
 
     /**
@@ -65,7 +92,7 @@ public class BizDataApiRequest {
     /**
      * 解析body字段
      */
-    private static String parseBody(List<BizDataApiParam> bodyArray) {
+    private static String parseBody(List<BizDataApiParam> bodyArray, Map<String, String> updateParams) {
         if (bodyArray == null) {
             return null;
         }
@@ -83,6 +110,15 @@ public class BizDataApiRequest {
                 bodyObj.put(key, value);
             }
         });
+
+        if (updateParams != null) {
+            for (String key : bodyObj.keySet()) {
+                if (updateParams.containsKey(key)) {
+                    bodyObj.put(key, updateParams.get(key));
+                }
+            }
+        }
+
         return bodyObj.toJSONString();
     }
 

+ 0 - 5
ruoyi-quartz/pom.xml

@@ -38,11 +38,6 @@
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
-        <!-- 业务代码-->
-        <dependency>
-            <groupId>com.ruoyi</groupId>
-            <artifactId>ruoyi-sh</artifactId>
-        </dependency>
     </dependencies>
 
 </project>

+ 0 - 28
ruoyi-sh/pom.xml

@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>sh-platform</artifactId>
-        <groupId>com.ruoyi</groupId>
-        <version>3.8.8</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>ruoyi-sh</artifactId>
-
-    <description>
-        上海市水务海洋数字孪生模型服务管理系统
-    </description>
-
-    <dependencies>
-
-        <!-- 通用工具-->
-        <dependency>
-            <groupId>com.ruoyi</groupId>
-            <artifactId>ruoyi-common</artifactId>
-        </dependency>
-
-    </dependencies>
-
-</project>

+ 333 - 260
ruoyi-ui/src/api/register/regCom.js

@@ -1,51 +1,52 @@
 import request from "@/utils/request";
 
 export function getModelList(query) {
-  return request({
-    url: '/model/info/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/model/info/list',
+        method: 'get',
+        params: query
+    })
 }
 
 export function getModelList2(data) {
-  return request({
-    url: "model/info/list2",
-    method: 'get',
-    params:data
-  });
+    return request({
+        url: "model/info/list2",
+        method: 'get',
+        params: data
+    });
 }
 
 export function addModel(data) {
-  return request.post("model/info", data);
+    return request.post("model/info", data);
 }
 
 export function getServiceApply(id) {
-  return request.get(`/pt/service/apply/${id}`);
+    return request.get(`/pt/service/apply/${id}`);
 }
 
 export function delMdid(id) {
-  return request({
-    url: '/model/info/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/model/info/' + id,
+        method: 'delete'
+    })
 }
 
 export function modelFile(data) {
-  return request({
-    url: '/model/file',
-    method: 'post',
-    data: data
-  })
+    return request({
+        url: '/model/file',
+        method: 'post',
+        data: data
+    })
 }
 
 export function getModelFile(data) {
-  return request.post("common/upload", data);
+    return request.post("common/upload", data);
 }
 
 export function getModelDetail(id) {
-  return request.get(`/model/info/${id}`);
+    return request.get(`/model/info/${id}`);
 }
+
 // export function getModelDetail(data) {
 //   return request({
 //     url: "/model/params/allList",
@@ -54,343 +55,415 @@ export function getModelDetail(id) {
 //   });
 // }
 export function updateModel(data) {
-  return request({
-    url: '/model/info',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/model/info',
+        method: 'put',
+        data: data
+    })
 }
+
 export function getParamsList(query) {
-  return request({
-    url: '/model/params/allList',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/model/params/allList',
+        method: 'get',
+        params: query
+    })
 }
+
 export function changePar(data) {
-  return request({
-    url: "/model/params/addList",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/model/params/addList",
+        method: 'post',
+        data: data
+    });
 }
+
 export function delAllPar(id) {
-  return request({
-    url: '/model/params/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/model/params/' + id,
+        method: 'delete'
+    })
 }
+
 export function getModelParList(query) {
-  return request({
-    url: '/model/pargroup/allList',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/model/pargroup/allList',
+        method: 'get',
+        params: query
+    })
 }
 
 export function getDataSourceList(query) {
-  return request({
-    url: '/meta/datasource/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/meta/datasource/list',
+        method: 'get',
+        params: query
+    })
 }
+
 export function getDataSourceDe(id) {
-  return request.get(`/meta/datasource/${id}`);
+    return request.get(`/meta/datasource/${id}`);
 }
+
 export function editDataSource(data) {
-  return request({
-    url: '/meta/datasource',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/meta/datasource',
+        method: 'put',
+        data: data
+    })
 }
+
 export function delDataSource(id) {
-  return request({
-    url: '/meta/datasource/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/meta/datasource/' + id,
+        method: 'delete'
+    })
 }
+
 export function addDataSource(data) {
-  return request({
-    url: "/meta/datasource",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/meta/datasource",
+        method: 'post',
+        data: data
+    });
 }
 
 export function getDataMetaList(query) {
-  return request({
-    url: '/meta/table/allList',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/meta/table/allList',
+        method: 'get',
+        params: query
+    })
 }
+
 export function editMeta(data) {
-  return request({
-    url: '/meta/table',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/meta/table',
+        method: 'put',
+        data: data
+    })
 }
+
 export function delMeta(id) {
-  return request({
-    url: '/meta/table',
-    method: 'delete',
-    params: id
-  })
+    return request({
+        url: '/meta/table',
+        method: 'delete',
+        params: id
+    })
 }
+
 export function addMeta(data) {
-  return request({
-    url: "/meta/table",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/meta/table",
+        method: 'post',
+        data: data
+    });
 }
+
 export function addPeiList(data) {
-  return request({
-    url: "/meta/field/addList",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/meta/field/addList",
+        method: 'post',
+        data: data
+    });
 }
+
 export function getMetaFiele(query) {
-  return request({
-    url: '/meta/field/allList',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/meta/field/allList',
+        method: 'get',
+        params: query
+    })
 }
+
 export function delMetaFiled(id) {
-  return request({
-    url: '/meta/field',
-    method: 'delete',
-    params: id
-  })
+    return request({
+        url: '/meta/field',
+        method: 'delete',
+        params: id
+    })
 }
+
 export function cateTree(query) {
-  return request({
-    url: '/data/cate/treeselect',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/data/cate/treeselect',
+        method: 'get',
+        params: query
+    })
 }
+
 export function addCate(data) {
-  return request({
-    url: "/data/cate",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/cate",
+        method: 'post',
+        data: data
+    });
 }
+
 export function delCate(id) {
-  return request({
-    url: '/data/cate/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/data/cate/' + id,
+        method: 'delete'
+    })
 }
+
 export function fieldAll(query) {
-  return request({
-    url: '/data/field/allList',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/data/field/allList',
+        method: 'get',
+        params: query
+    })
+}
+
+export function getSetById(id) {
+    return request({
+        url: `/data/set/${id}`,
+        method: 'get',
+    });
 }
+
 export function addSet(data) {
-  return request({
-    url: "/data/set",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/set",
+        method: 'post',
+        data: data
+    });
+}
+
+export function dataSetTree(query) {
+    return request({
+        url: '/data/set/treeselect',
+        method: 'get',
+        params: query
+    })
 }
+
 export function dataSetList(query) {
-  return request({
-    url: '/data/set/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/data/set/list',
+        method: 'get',
+        params: query
+    })
 }
+
 export function editSet(data) {
-  return request({
-    url: '/data/set',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/data/set',
+        method: 'put',
+        data: data
+    })
 }
+
 export function delSet(id) {
-  return request({
-    url: '/data/set/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/data/set/' + id,
+        method: 'delete'
+    })
 }
+
 export function fieldAdd(data) {
-  return request({
-    url: "/data/field/addList",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/field/addList",
+        method: 'post',
+        data: data
+    });
 }
+
 export function delField(id) {
-  return request({
-    url: '/data/field/removeOne',
-    method: 'delete',
-    params: id
-  })
+    return request({
+        url: '/data/field/removeOne',
+        method: 'delete',
+        params: id
+    })
 }
+
 export function getModelLiu(query) {
-  return request({
-    url: '/interfaces/cate/treeFlow',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/interfaces/cate/treeFlow',
+        method: 'get',
+        params: query
+    })
 }
+
 export function addLiu(data) {
-  return request({
-    url: "/model/flow",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/model/flow",
+        method: 'post',
+        data: data
+    });
 }
+
 export function getFlow(id) {
-  return request.get(`/model/flow//${id}`);
+    return request.get(`/model/flow//${id}`);
 }
+
 export function delFlow(id) {
-  return request({
-    url: '/model/flow/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/model/flow/' + id,
+        method: 'delete'
+    })
 }
+
 export function editFlow(data) {
-  return request({
-    url: '/model/flow',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/model/flow',
+        method: 'put',
+        data: data
+    })
 }
+
 export function delFile(id) {
-  return request({
-    url: '/common/deleteFile',
-    method: 'delete',
-    params: id
-  })
+    return request({
+        url: '/common/deleteFile',
+        method: 'delete',
+        params: id
+    })
 }
+
 export function addGroup(data) {
-  return request({
-    url: "/model/pargroup",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/model/pargroup",
+        method: 'post',
+        data: data
+    });
 }
+
 export function editGroup(data) {
-  return request({
-    url: '/model/pargroup',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/model/pargroup',
+        method: 'put',
+        data: data
+    })
 }
+
 export function delFen(id) {
-  return request({
-    url: '/model/pargroup/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/model/pargroup/' + id,
+        method: 'delete'
+    })
 }
+
 export function testDatasource(id) {
-  return request({
-    url: '/meta/datasource/testConnection/' + id,
-    method: 'get'
-  })
+    return request({
+        url: '/meta/datasource/testConnection/' + id,
+        method: 'get'
+    })
 }
+
 export function dataJisql(data) {
-  return request({
-    url: "/meta/datasource/query",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/meta/datasource/query",
+        method: 'post',
+        data: data
+    });
 }
+
 export function dataJiApi(data) {
-  return request({
-    url: "/data/set/api",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/set/api",
+        method: 'post',
+        data: data
+    });
 }
+
 export function dataJiFile(data) {
-  return request({
-    url: "/data/set/file",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/set/file",
+        method: 'post',
+        data: data
+    });
 }
+
 export function dataJiSql(data) {
-  return request({
-    url: "/data/set/sql",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/set/sql",
+        method: 'post',
+        data: data
+    });
 }
+
 export function getDataJiSql(id) {
-  return request({
-    url: '/data/set/sql/' + id,
-    method: 'get'
-  })
+    return request({
+        url: '/data/set/sql/' + id,
+        method: 'get'
+    })
 }
+
 export function getDataJiApi(id) {
-  return request({
-    url: '/data/set/api/' + id,
-    method: 'get'
-  })
+    return request({
+        url: '/data/set/api/' + id,
+        method: 'get'
+    })
 }
+
 export function getDataJiFile(id) {
-  return request({
-    url: '/data/set/file/' + id,
-    method: 'get'
-  })
+    return request({
+        url: '/data/set/file/' + id,
+        method: 'get'
+    })
 }
+
 export function getDataJiParam(query) {
-  return request({
-    url: '/data/set/param/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/data/set/param/list',
+        method: 'get',
+        params: query
+    })
 }
+
 export function getDataJiParamRe(query) {
-  return request({
-    url: '/data/field/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/data/field/list',
+        method: 'get',
+        params: query
+    })
 }
+
 export function addJiFan(data) {
-  return request({
-    url: "/data/field/addList",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/field/addList",
+        method: 'post',
+        data: data
+    });
 }
+
 export function addJiCan(data) {
-  return request({
-    url: "/data/set/param/addList",
-    method: 'post',
-    data:data
-  });
+    return request({
+        url: "/data/set/param/addList",
+        method: 'post',
+        data: data
+    });
 }
+
 export function changeShenhe(query) {
-  return request({
-    url: '/model/info/audit',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/model/info/audit',
+        method: 'get',
+        params: query
+    })
 }
+
 export function changeSerShenhe(query) {
-  return request({
-    url: '/pt/service/audit',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/pt/service/audit',
+        method: 'get',
+        params: query
+    })
 }
+
 export function delModelPar(id) {
-  return request({
-    url: '/model/params/removeAll',
-    method: 'delete',
-    params: id
-  })
+    return request({
+        url: '/model/params/removeAll',
+        method: 'delete',
+        params: id
+    })
+}
+
+export function getDataSetBizDataById(id, params) {
+    return request({
+        url: `/data/set/getBizData/${id}`,
+        method: 'get',
+        params,
+        timeout: 60 * 1000 * 10
+    })
 }

+ 17 - 1
ruoyi-ui/src/api/service/timing.js

@@ -48,4 +48,20 @@ export function snailTri(query) {
     method: 'get',
     params: query
   })
-}
+}
+
+export function snailJobStatistics() {
+  return request({
+    url: '/snail/job/log/statistics',
+    method: 'get',
+  })
+}
+
+export function snailJobLogList(query) {
+  return request({
+    url: '/snail/job/log/listOfPage',
+    method: 'get',
+    params: query
+  })
+}
+

+ 2 - 1
ruoyi-ui/src/api/standardization/bizDataShowConfig.js

@@ -32,10 +32,11 @@ export function testBizDataApi(data) {
     })
 }
 
-export function getBizDataById(id) {
+export function getBizDataById(id, params) {
     return request({
         url: `/biz/data/show/config/getBizData/${id}`,
         method: 'get',
+        params,
         timeout: 60 * 1000 * 10
     })
 }

+ 94 - 0
ruoyi-ui/src/api/system/plugin/index.js

@@ -0,0 +1,94 @@
+import request from '@/utils/request.js';
+
+// 查询插件列表
+export function pluginList(query) {
+    return request({
+        url: '/plugin/list',
+        method: 'get',
+        params: query
+    });
+}
+
+// 查询插件详细信息
+export function pluginInfo(pluginId) {
+    return request({
+        url: '/plugin/info/' + pluginId,
+        method: 'get'
+    });
+}
+
+// 上传插件
+export function pluginUpload(data) {
+    return request({
+        url: '/plugin/upload',
+        method: 'post',
+        data: data
+    });
+}
+
+// 启动插件
+export function pluginReload() {
+    return request({
+        url: '/plugin/reload',
+        method: 'get'
+    });
+}
+
+// 启动插件
+export function pluginLoad(pluginId) {
+    return request({
+        url: '/plugin/load/' + pluginId,
+        method: 'get'
+    });
+}
+
+// 启动插件
+export function pluginUnload(pluginId) {
+    return request({
+        url: '/plugin/unload/' + pluginId,
+        method: 'get'
+    });
+}
+
+// 启动插件
+export function pluginStart(pluginId) {
+    return request({
+        url: '/plugin/start/' + pluginId,
+        method: 'get'
+    });
+}
+
+export function pluginStop(pluginId) {
+    return request({
+        url: '/plugin/stop/' + pluginId,
+        method: 'get'
+    });
+}
+
+export function pluginDisable(pluginId) {
+    return request({
+        url: '/plugin/disable/' + pluginId,
+        method: 'get'
+    });
+}
+
+export function pluginDelete(pluginId) {
+    return request({
+        url: '/plugin/delete/' + pluginId,
+        method: 'get'
+    });
+}
+
+export function pluginEnable(pluginId) {
+    return request({
+        url: '/plugin/enable/' + pluginId,
+        method: 'get'
+    });
+}
+
+export function pluginTest(pluginId) {
+    return request({
+        url: '/plugin/test/' + pluginId,
+        method: 'get'
+    });
+}

+ 7 - 0
ruoyi-ui/src/assets/icons/svg/play2.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+        "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1719815179960" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4042"
+     width="200" height="200">
+    <path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024zM383.232 287.616v448l384-223.104-384-224.896z" fill=""
+          p-id="4043"></path>
+</svg>

+ 6 - 0
ruoyi-ui/src/assets/icons/svg/stop2.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+        "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1719814980981" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4240"
+     width="200" height="200">
+  <path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024zM320 320v384h384V320H320z" fill="" p-id="4241"></path>
+</svg>

+ 1 - 1
ruoyi-ui/src/views/container/index.vue

@@ -6,7 +6,7 @@
     <el-tab-pane label="镜像管理">
       <container-image></container-image>
     </el-tab-pane>
-    <el-tab-pane label="Docker服务管理">
+    <el-tab-pane label="容器服务器管理">
       <docker-server></docker-server>
     </el-tab-pane>
   </el-tabs>

+ 0 - 149
ruoyi-ui/src/views/containerImage/index.vue

@@ -1,149 +0,0 @@
-<template>
-  <div class="app-container">
-    <el-row justify="space-between">
-      <el-col :span="12">
-        <el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
-          <el-form-item label="镜像名称">
-            <el-input v-model="queryParams.imageName" placeholder="请输入镜像名称" clearable style="width: 240px"
-                      @keyup.enter="getModelListTable"/>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" icon="Search" @click="getModelListTable">查询</el-button>
-          </el-form-item>
-        </el-form>
-      </el-col>
-      <el-button type="primary" @click="reg" icon="Plus">创建镜像</el-button>
-    </el-row>
-    <el-table
-        :data="tableData"
-        height="69vh"
-        :cell-style="{ padding: '5px' }"
-        :header-cell-style="{ height: heightAll * 0.01 + 'px' }"
-        :row-style="{ fontSize: '1rem', textAlign:'center' }"
-        border>
-      <el-table-column type="index" label="序号" width="80">
-        <template #default="{ $index }">
-          <div style="text-align: center;">{{ $index + 1 }}</div>
-        </template>
-      </el-table-column>
-      <el-table-column prop="imageName" label="镜像名称"/>
-      <el-table-column prop="tag" label="版本" width="160"/>
-      <el-table-column show-overflow-tooltip prop="imageId" label="镜像ID" width="160">
-        <template #default="scope">
-          {{ scope.row.imageId }}
-        </template>
-      </el-table-column>
-      <el-table-column prop="createTime" align="center" label="创建时间" width="220">
-        <template #default="scope">
-          {{ scope.row.createTime }}
-        </template>
-      </el-table-column>
-      <el-table-column prop="size" align="center" label="镜像大小" width="120">
-        <template #default="scope">
-          {{ formatDiskSize(scope.row.size) }}
-        </template>
-      </el-table-column>
-      <el-table-column align="center" label="操作" width="200">
-        <template #default="scope">
-          <div style="display: flex;justify-content: space-between;">
-            <el-button type="danger" @click="handleDelete(scope.row)" text size="small">删除</el-button>
-          </div>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" @close="clearForm" destroy-on-close>
-      <el-form :model="form" label-position="right" ref="formRef" label-width="120px" :rules="rules">
-        <el-form-item label="镜像名称:" prop="imageName">
-          <el-input v-model="form.imageName"/>
-        </el-form-item>
-        <el-form-item label="镜像版本:" prop="tag">
-          <el-input v-model="form.tag"/>
-        </el-form-item>
-        <el-form-item label="镜像文件:">
-          <file-upload v-model="form.modelFilePath" :limit="1" :file-type="['zip']" :file-size="20"></file-upload>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submit">提交</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-<script setup>
-import {onMounted, reactive, ref} from 'vue';
-import {
-  deleteModelContainerImage,
-  getModelContainerImageList,
-  saveModelContainerImage
-} from "@/api/container/modelContainerImage.js";
-import {formatDiskSize} from "@/utils/index.js";
-import FileUpload from "@/components/FileUpload/index.vue";
-
-const {proxy} = getCurrentInstance();
-
-const queryParams = ref({
-  imageName: '',
-})
-let tableData = ref([])
-
-function handleDelete(row) {
-  proxy.$modal.confirm('是否确认删除?').then(function () {
-    return deleteModelContainerImage(row.imageId);
-  }).then(() => {
-    getModelListTable();
-    proxy.$modal.msgSuccess("删除成功");
-  }).catch(() => {
-  });
-}
-
-const dialogVisible = ref(false)
-const dialogTitle = ref('新建镜像')
-
-const formRef = ref();
-const form = ref({
-  imageName: '',
-  tag: '',
-  modelFilePath: '',
-});
-const rules = reactive({
-  imageName: [{required: true, message: '必填', trigger: 'blur'}],
-});
-
-const heightAll = window.innerHeight
-
-async function submit() {
-  formRef.value.validate((valid) => {
-    if (valid) {
-      saveModelContainerImage(form.value).then(() => {
-        proxy.$modal.msgSuccess("开始创建镜像");
-        dialogVisible.value = false
-        getModelListTable()
-      })
-    }
-  })
-}
-
-function clearForm() {
-  form.value = {
-    imageName: '',
-    tag: '',
-    modelFilePath: '',
-  }
-}
-
-function reg() {
-  dialogVisible.value = true
-}
-
-function getModelListTable() {
-  getModelContainerImageList(queryParams.value).then(res => {
-    tableData.value = res.data
-  })
-}
-
-onMounted(() => {
-  getModelListTable()
-});
-</script>

+ 1 - 1
ruoyi-ui/src/views/docker/index.vue

@@ -12,7 +12,7 @@
           </el-form-item>
         </el-form>
       </el-col>
-      <el-button type="primary" @click="reg" icon="Plus">创建服务</el-button>
+      <el-button type="primary" @click="reg" icon="Plus">创建连接</el-button>
     </el-row>
     <el-table
         :data="tableData"

+ 1 - 2
ruoyi-ui/src/views/dpp/task/integratioTask/index2.vue

@@ -27,7 +27,6 @@
     </el-row>
     <el-table
         :data="tableData"
-        style="width: 100%;margin-left: 0;margin-top:1%;"
         :cell-style="{ paddingTop:'3px',paddingBottom:'3px' }"
         :header-cell-style="{height: heightAll*0.01+'px',}"
         @selection-change="handleSelectionChange"
@@ -456,7 +455,7 @@ const {proxy} = getCurrentInstance();
 const title = ref([])
 const total = ref(1)
 const tableData = ref([])
-const tableheight = window.innerHeight * 0.7
+const tableheight = window.innerHeight * 0.8
 const heightAll = window.innerHeight
 const dialogVisible = ref(false)
 const dialogVisibleTree = ref(false)

+ 9 - 3
ruoyi-ui/src/views/map/index.vue

@@ -10,8 +10,6 @@ import {getBizDataShowConfigList} from "@/api/standardization/bizDataShowConfig.
 import BizDataCard from "@/views/map/components/bizDataCard.vue";
 import GwMap from "./components/map.vue"
 
-const bizDataShowConfigList = ref([]);
-
 const mapRef = ref(null);
 const mapConfig = ref({
   zoom: 9,
@@ -48,6 +46,7 @@ const mapConfig = ref({
               latitude: 'lttd'   // 接口中的纬度字段名
             }
           },
+          textField: 'stationName',
           // 定义要保留的属性字段
           properties: {
             '测站编码': 'stationCode',     // 目标属性名: 源数据字段名
@@ -76,6 +75,13 @@ const mapConfig = ref({
             radius: 8,
             fill: {color: '#FF5722'},
             stroke: {color: '#fff', width: 2}
+          },
+          text: {
+            field: 'stationName',
+            font: '12px Arial',
+            fill: {color: '#000000'},
+            stroke: {color: '#ffffff', width: 2},
+            offsetY: -20
           }
         },
         // 线要素样式
@@ -108,6 +114,7 @@ const mapConfig = ref({
     },
   ]
 });
+const bizDataShowConfigList = ref([]);
 
 function getBizDataConfigList() {
   getBizDataShowConfigList().then(res => {
@@ -119,7 +126,6 @@ onMounted(() => {
   getBizDataConfigList();
 });
 </script>
-
 <style scoped lang="scss">
 .map-container {
   height: 100%;

+ 1 - 0
ruoyi-ui/src/views/map/utils/jsonToGeojson.js

@@ -13,6 +13,7 @@ export function jsonToFeatures(data, mappingConfig) {
         const properties = buildProperties(item, mappingConfig.properties);
         const feature = new Feature({
             geometry: geometry,
+            name: item[mappingConfig.textField],
             ...properties,  // 直接将属性展开到 Feature 中
         });
         features.push(feature)

+ 5 - 0
ruoyi-ui/src/views/map/utils/styleParser.js

@@ -113,6 +113,11 @@ function createTextStyle(textConfig, properties) {
 
     const config = {...textConfig};
 
+    // 如果没有显式设置文本内容,则使用 textField 指定的属性
+    if (!config.text) {
+        config.text = properties[config.field];
+    }
+
     // 动态设置文本内容
     if (config.property) {
         config.text = properties[config.property];

+ 16 - 3
ruoyi-ui/src/views/monitor/service/GwStatistic.vue

@@ -1,7 +1,6 @@
 <template>
   <div shadow="always" class="gw-statistic boxShadow">
-    <div class="gw-statistic-value" :style="{'color': color}">
-      {{ value }}
+    <div class="gw-statistic-value" :style="{'color': color}" v-html="showValue">
     </div>
     <div class="gw-statistic-name" :style="{'background-color': color}">
       <slot name="name">
@@ -11,6 +10,8 @@
   </div>
 </template>
 <script setup>
+import {isString} from "@/utils/validate.js";
+
 const emit = defineEmits(['update'])
 const props = defineProps({
   name: {
@@ -25,6 +26,18 @@ const props = defineProps({
     default: "#477ACF"
   },
 })
+const showValue = ref("")
+
+watch(() => props.value, v => {
+  if (v && isString(v)) {
+    if (v.length > 11) {
+      const index = Math.floor(v.length / 2)
+      showValue.value = v.substring(0, index + 1) + '<br/>' + v.substring(index + 1)
+      return
+    }
+  }
+  showValue.value = v
+}, {immediate: true})
 </script>
 <style scoped lang="scss">
 .gw-statistic {
@@ -51,7 +64,7 @@ const props = defineProps({
     display: flex;
     align-items: center;
     justify-content: center;
-    font-size: 35px;
+    font-size: 1.8rem;
     font-weight: bold;
     height: 70%
   }

+ 3 - 3
ruoyi-ui/src/views/monitor/service/index.vue

@@ -172,9 +172,9 @@ import {parseTime} from "@/utils/ruoyi.js";
 import {getAlarmList} from "@/api/service/alarm.js";
 
 const statisticData = ref([
-  {name: '当前服务总数', value: 0, color: '#477ACF'},
-  {name: '服务在线比率', value: '0%', color: '#40B0D7'},
-  {name: '模型服务支撑健康指数', value: '无', color: '#2DBEA2'},
+  {name: '模型总数', value: 0, color: '#477ACF'},
+  {name: '当前服务总数', value: 0, color: '#40B0D7'},
+  {name: '服务在线比率', value: '0%', color: '#2DBEA2'},
   {name: '当月热点服务', value: '无', color: '#487ACF'},
   {name: '累计调用次数', value: '0', color: '#4BBA9B'},
 ])

+ 52 - 26
ruoyi-ui/src/views/monitor/taskMonitor/index.vue

@@ -12,19 +12,43 @@
           :header-cell-style="{ height: heightAll * 0.01 + 'px' }"
           :row-style="{ fontSize: '1rem', textAlign:'center' }"
           border>
-        <el-table-column align="center" width="200" prop="name" label="任务名称"></el-table-column>
-        <el-table-column align="center" width="240" prop="modelName" label="关联模型"/>
-        <el-table-column align="center" width="240" prop="time" label="执行时间"/>
-        <el-table-column align="center" prop="status" label="执行状态" width="80">
+        <el-table-column prop="jobName" label="任务名称" width="160"></el-table-column>
+        <el-table-column prop="taskType" label="任务类型" width="170">
           <template #default="scope">
-            <el-tag v-if="scope.row.status==1">成功</el-tag>
+            <el-tag v-if="scope.row.taskType==1" type="success">集群</el-tag>
+            <el-tag v-if="scope.row.taskType==2" type="info">广播</el-tag>
+            <el-tag v-if="scope.row.taskType==3" type="warning">Sharding</el-tag>
+            <el-tag v-if="scope.row.taskType==4" type="danger">Map</el-tag>
+            <el-tag v-if="scope.row.taskType==5">MapReduce</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="triggerType" label="触发类型" width="170">
+          <template #default="scope">
+            <el-tag v-if="scope.row.triggerType==2">固定时间</el-tag>
+            <el-tag v-if="scope.row.triggerType==3">CRON 表达式</el-tag>
+            <el-tag v-if="scope.row.triggerType==99">工作流</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="retryInterval" label="间隔时长" width="170"/>
+        <el-table-column prop="blockStrategy" label="阻塞策略" width="170">
+          <template #default="scope">
+            <el-tag v-if="scope.row.blockStrategy==1">丢弃</el-tag>
+            <el-tag v-if="scope.row.blockStrategy==2">覆盖</el-tag>
+            <el-tag v-if="scope.row.blockStrategy==3">并行</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="executorTimeout" label="超时时间(秒)" width="170"/>
+        <el-table-column prop="executionTime" label="执行时间" width="240"/>
+        <el-table-column align="center" prop="taskStatus" label="执行状态" width="80">
+          <template #default="scope">
+            <el-tag v-if="scope.row.taskStatus == 3">成功</el-tag>
             <el-tag v-else type="danger">失败</el-tag>
           </template>
         </el-table-column>
-        <el-table-column align="center" prop="remark" label="执行说明"/>
+        <el-table-column prop="resultMessage" label="执行结果" width="170"/>
       </el-table>
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-                  v-model:limit="queryParams.pageSize"/>
+                  v-model:limit="queryParams.pageSize" @pagination="getTaskData"/>
     </el-card>
   </div>
 </template>
@@ -32,12 +56,13 @@
 import {onMounted} from 'vue';
 import GwStatistic from "@/views/monitor/service/GwStatistic.vue";
 import Pagination from "@/components/Pagination/index.vue";
+import {snailJobLogList, snailJobStatistics} from "@/api/service/timing.js";
 
 const statisticData = ref([
-  {name: '任务数据总数', value: 10, color: '#477ACF'},
-  {name: '任务被执行次数', value: 145, color: '#40B0D7'},
-  {name: '日执行量', value: 10, color: '#2DBEA2'},
-  {name: '月执行量', value: 145, color: '#487ACF'},
+  {name: '任务数据总数', value: 0, color: '#477ACF'},
+  {name: '任务被执行次数', value: 0, color: '#40B0D7'},
+  {name: '日执行量', value: 0, color: '#2DBEA2'},
+  {name: '月执行量', value: 0, color: '#487ACF'},
 ])
 const queryParams = ref({
   pageNum: 1,
@@ -47,24 +72,25 @@ const tableData = ref([])
 const total = ref(0)
 
 function getTaskData() {
-  tableData.value = [
-    {
-      time: '2023-05-05 09:09:09',
-      name: '防洪预演',
-      modelName: '模型1',
-      alertContent: '模型1的报警内容'
-    },
-    {
-      time: '2023-05-05 09:09:09',
-      name: '风暴潮预演',
-      modelName: '模型2',
-      alertContent: '模型2的报警内容'
-    }
-  ]
-  total.value = 2
+  snailJobLogList(queryParams.value).then(res => {
+    tableData.value = res.rows
+    total.value = res.total
+  })
+}
+
+function getStatistics() {
+  snailJobStatistics().then(res => {
+    statisticData.value = [
+      {name: '任务数据总数', value: res.data.total, color: '#477ACF'},
+      {name: '任务被执行次数', value: res.data.runTotal, color: '#40B0D7'},
+      {name: '日执行量', value: res.data.nowRunTotal, color: '#2DBEA2'},
+      {name: '月执行量', value: res.data.monthRunTotal, color: '#487ACF'},
+    ]
+  })
 }
 
 onMounted(() => {
+  getStatistics()
   getTaskData()
 });
 </script>

+ 386 - 0
ruoyi-ui/src/views/register/modelData/dataQuery.vue

@@ -0,0 +1,386 @@
+<template>
+  <el-row class="app-container" style="width: 100%;height: 100%;">
+    <el-col :span="4" style="height: 100%">
+      <el-card style="height: 100%;overflow: auto;overflow-x: hidden;" shadow="never">
+        <template #header>
+          <div class="card-header" style="display: flex;align-items: center;justify-content: space-between;">
+            <span>目录:</span>
+            <div>
+              <el-icon @click="addDir" style="cursor: pointer;color: #409EFF;">
+                <Plus/>
+              </el-icon>
+            </div>
+          </div>
+        </template>
+        <el-tree :expand-on-click-node="false" ref="treeRef"
+                 :current-node-key="currentNodeKey" class="treeLeft"
+                 :data="data" @node-click="handleNodeClick" node-key="id"
+                 style="margin-left: -10%;margin-top: 0;width: 120%;background-color: transparent;" default-expand-all>
+          <template #default="{ node, data }">
+            <span style="justify-content: space-between;display: flex;width: 20%;align-items: center;margin-left: 1%;">
+              <div class="custom-tree-node" style="align-items: center;line-height: 1.5;">
+                <svg-icon icon-class="model" v-if="data.nodeType=='SET'"/>
+                <svg-icon icon-class="model2" v-if="data.nodeType=='TREE'"/>
+              </div>
+              <span style="font-size: 1rem;">{{ node.label }}</span>
+              <!--              <div style="margin-left: auto;position: absolute;left:90%;">-->
+              <!--                <el-dropdown trigger="hover" @click.stop v-if="currentNodeKey === data.id">-->
+              <!--                    &lt;!&ndash; <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
+              <!--                      <plus />-->
+              <!--                    </el-icon> &ndash;&gt;-->
+              <!--                    <svg-icon icon-class="zhankai"/>-->
+              <!--                  <template #dropdown>-->
+              <!--                    <el-dropdown-menu>-->
+              <!--                      <el-dropdown-item style="display: flex;" @click="add1Level">-->
+              <!--                        <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
+              <!--                          <CirclePlus/>-->
+              <!--                        </el-icon>-->
+              <!--                        <div>-->
+              <!--                          添加同级-->
+              <!--                        </div>-->
+              <!--                      </el-dropdown-item>-->
+              <!--                      <el-dropdown-item style="display: flex;" @click="addNextLevel">-->
+              <!--                        <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
+              <!--                          <Connection/>-->
+              <!--                        </el-icon>-->
+              <!--                        <div>-->
+              <!--                          新建下级-->
+              <!--                        </div>-->
+              <!--                      </el-dropdown-item>-->
+              <!--                      <divider/>-->
+              <!--                      <el-dropdown-item style="display: flex;" @click="delAll" divided>-->
+              <!--                        <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
+              <!--                          <CircleClose/>-->
+              <!--                        </el-icon>-->
+              <!--                        <div>-->
+              <!--                          删除节点-->
+              <!--                        </div>-->
+              <!--                      </el-dropdown-item>-->
+              <!--                    </el-dropdown-menu>-->
+              <!--                  </template>-->
+              <!--                </el-dropdown>-->
+              <!--              </div>-->
+            </span>
+          </template>
+        </el-tree>
+      </el-card>
+    </el-col>
+    <el-col :span="20" style="height: 100%">
+      <div style="height: 40px;display: flex;justify-content: right;align-items: center;">
+        <el-button @click="handleEdit">编辑配置</el-button>
+      </div>
+      <div style="height: calc(100% - 40px);">
+        <biz-display ref="bizDisplayRef" source="DATA_SET" :config="dataSetConfig"></biz-display>
+      </div>
+    </el-col>
+
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" destroy-on-close>
+      <el-form :model="form" label-position="right" ref="formRef" label-width="120px" :rules="rules">
+        <el-form-item label="数据集中文名:" prop="dcName" style="">
+          <el-input v-model="form.dcName" style="width: 75%;" placeholder="" resize="none"/>
+        </el-form-item>
+        <el-form-item label="数据集说明:" prop="dcNote" style="">
+          <el-input v-model="form.dcNote" style="width: 75%;" placeholder="" type="textarea" :row="2" resize="none"/>
+        </el-form-item>
+        <el-form-item label="数据集类型:" prop="dcType" style="">
+          <el-select
+              v-model="form.dcType"
+              class="noBorSel"
+              placeholder=""
+              style="width: 75%;margin-left: 0%;"
+          >
+            <el-option
+                v-for="item in optionsShuLei"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="排序:" prop="dcSort">
+          <el-input-number v-model="form.dcSort" :min="1" style="width: 50%;"/>
+        </el-form-item>
+        <el-form-item label="请求数据" style="max-height: 300px;">
+          <div style="width: 100%;height: 100%;background-color: #FAFAFA;overflow: auto;border-radius: 10px;">
+            <biz-data-show-config-api v-model="form.queryOptions"></biz-data-show-config-api>
+          </div>
+        </el-form-item>
+        <el-form-item label="展示类型">
+          <div
+              style="width:100%;max-height: 300px;overflow: auto;background-color: #FAFAFA;border-radius: 10px;padding: 10px;">
+            <biz-data-show-config-list :columns="columns"
+                                       v-model="form.renderingOptions"></biz-data-show-config-list>
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="close">取消</el-button>
+        <el-button type="primary" @click="submit">提交</el-button>
+      </template>
+    </el-dialog>
+  </el-row>
+</template>
+<script setup>
+import {nextTick, onMounted, ref} from 'vue'
+import {modelTreeSelect} from "@/api/service/info";
+import {dataSetTree, editSet, getDataSourceList, getSetById} from "@/api/register/regCom.js";
+import {cloneDeep} from 'lodash'
+import BizDataShowConfigApi from "@/views/standardization/bizDataShowConfig/api/index.vue";
+import BizDataShowConfigList from "@/views/standardization/bizDataShowConfig/list/index.vue";
+import BizDisplay from "@/views/standardization/bizDataShowConfig/show/index.vue";
+
+const total = ref(0)
+
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formRef = ref()
+const form = ref({})
+const rules = ref({})
+const columns = ref([])
+const bizDisplayRef = ref(null)
+const dataSetConfig = ref(null)
+
+const optionsShuLei = ref([
+  {
+    label: "SQL数据集",
+    value: 'SQL'
+  },
+  {
+    label: "API数据集",
+    value: 'API'
+  },
+  {
+    label: "文件数据集",
+    value: 'FILE'
+  },
+])
+const valueNode = ref('')
+const {proxy} = getCurrentInstance();
+const data = ref([])
+const dialogVisibleData = ref(false)
+
+const parTree = ref(null)
+const byNode = ref(false)
+const optionsDs = ref([])
+const currentNodeKey = ref('')
+const dataSel = ref([])
+const parAr = ref([])
+
+function handleEdit() {
+  getSetById(parTree.value.id).then(res => {
+    form.value = res.data.dataSet
+  })
+  dialogTitle.value = `编辑【${parTree.value.label}】`
+  dialogVisible.value = true
+}
+
+function submit() {
+  proxy.$refs.formRef.validate(valid => {
+    if (valid) {
+      editSet(form.value).then(response => {
+        proxy.$modal.msgSuccess("修改成功");
+      });
+    }
+  });
+}
+
+function close() {
+  form.value = {}
+  dialogTitle.value = ''
+  dialogVisible.value = false
+}
+
+function addDir() {
+  byNode.value = true
+  valueNode.value = data.value[0].value
+  dialogVisibleData.value = true
+}
+
+async function getTreeSel() {
+  await modelTreeSelect().then(res => {
+    dataSel.value = renameTreeProperties(res.data)
+    console.log(dataSel.value)
+  })
+}
+
+function getOptionsDs() {
+  getDataSourceList().then(res => {
+    optionsDs.value = res.rows
+    optionsDs.value.forEach(item => {
+      item.label = item.dsDbname
+      item.value = item.dsCode
+    })
+    total.value = res.total
+  })
+}
+
+function addValueField(nodes) {
+  if (!Array.isArray(nodes)) return;
+
+  for (const node of nodes) {
+    // 添加value字段,其值等于id
+    node.value = node.id;
+
+    // 如果存在子节点,递归处理
+    if (node.children && Array.isArray(node.children)) {
+      addValueField(node.children);
+    }
+  }
+}
+
+const flattenTree = (nodes) => {
+  const result = [];
+  const stack = [...nodes];
+  while (stack.length) {
+    const node = stack.pop();
+    result.push({id: node.id, pid: node.pid}); // 只提取 id 和 pid
+    if (node.children && node.children.length) {
+      stack.push(...node.children);
+    }
+  }
+  return result;
+};
+
+function getTree() {
+  dataSetTree().then(res => {
+    data.value = res.data
+    data.value.forEach(item => {
+      item.value = item.id
+      item.id = item.id
+    })
+    addValueField(data.value)
+    parAr.value = flattenTree(data.value);
+  })
+}
+
+function handleNodeClick(node, data) {
+  currentNodeKey.value = data.data.id
+  parTree.value = data.data
+
+  getSetById(data.data.id).then(res => {
+    nextTick(() => {
+      let data = res.data.dataSet
+      data.id = data.dcCode
+      dataSetConfig.value = data
+    })
+  })
+
+}
+
+function renameTreeProperties(tree) {
+  // 深拷贝避免修改原始数据(可选,根据需求)
+  const newTree = cloneDeep(tree); // 使用 lodash
+  // 或:const newTree = JSON.parse(JSON.stringify(tree)); // 原生方法(不兼容函数等)
+
+  // 递归处理函数
+  const processNode = (node) => {
+    // 重命名属性
+    if ('cateName' in node) {
+      node.label = node.cateName;
+      delete node.cateName;
+    }
+    if ('id' in node) {
+      node.value = node.id;
+      delete node.id;
+    }
+
+    // 递归处理子节点
+    if (node.children && node.children.length > 0) {
+      node.children.forEach(child => processNode(child));
+    }
+  };
+
+  // 遍历根节点
+  newTree.forEach(rootNode => processNode(rootNode));
+  return newTree;
+}
+
+onMounted(() => {
+  getTree()
+  getOptionsDs()
+  getTreeSel()
+  // getTable()
+});
+
+watch(() => form.value.queryOptions, queryOptions => {
+  if (queryOptions) {
+    const api = JSON.parse(queryOptions)
+    columns.value = api.response
+  }
+}, {immediate: true, deep: true})
+</script>
+<style scoped>
+:deep(.svg-icon) {
+  outline: none;
+}
+
+:deep(.svg-icon svg) {
+  stroke: none;
+}
+
+:deep(.el-tree-node__expand-icon) {
+  margin-left: 5%; /* 向右推动图标 */
+}
+
+:deep(.el-table__body tr:hover > td) {
+  background-color: #eaf7ff !important;
+}
+
+:deep(.treeLeft) .el-tree-node__content {
+  width: 100%;
+  display: flex !important;
+  height: 30px !important;
+  align-items: center;
+  padding-top: 0 !important;
+}
+
+:deep(.treeLeft) .el-tree-node__content:hover {
+  background-color: #ecf5ff;
+}
+
+:deep(.treeLeft) .el-tree-node__content:active {
+  background-color: #d9ecff !important;
+}
+
+/* 选中态(Active) */
+:deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
+  background-color: #c6e2ff !important;
+}
+
+.drag-handle {
+  cursor: move;
+}
+
+.ghost {
+  opacity: 0.5;
+  background: #c8ebfb;
+}
+
+/* 防止文字选中 */
+:deep(.el-table__row) {
+  user-select: none;
+  -webkit-user-select: none;
+}
+</style>
+<style scoped lang="scss">
+
+
+.el-table .el-table__row td {
+  height: 60px !important; /* 行高 */
+}
+
+.custom-tree-node {
+  display: flex; /* 启用 Flex 布局 */
+  align-items: center; /* 垂直居中 */
+  gap: 6px; /* 图标与文字间距 */
+}
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+</style>

+ 10 - 5
ruoyi-ui/src/views/standardization/bizDataShowConfig/index.vue

@@ -2,7 +2,7 @@
   <div class="app-container">
     <el-row justify="space-between" style="margin-bottom: 10px;">
       <el-col :span="12">
-        <el-page-header title="" @back="jumpPage('/standardization/map')">
+        <el-page-header title="" @back="jumpPage('/standardization/modelUsing')">
           <template #content>
             <span class="text-large font-600 mr-3"> {{ homeTitle }} </span>
           </template>
@@ -77,14 +77,18 @@
           <el-col :span="24">
             <el-form-item label="展示类型">
               <el-select v-model="form.type" clearable placeholder="请选择" style="margin-bottom: 10px;">
-                <el-option v-for="dict in typeOptions" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                <el-option v-for="dict in typeOptions" :key="dict.value" :label="dict.label"
+                           :value="dict.value"></el-option>
               </el-select>
-              <div style="width:100%;max-height: 300px;overflow: auto;background-color: #FAFAFA;border-radius: 10px;padding: 10px;">
+              <div
+                  style="width:100%;max-height: 300px;overflow: auto;background-color: #FAFAFA;border-radius: 10px;padding: 10px;">
                 <template v-if="form.type === 'chart'">
-                  <biz-data-show-config-chart :columns="columns" v-model="form.renderingOptions"></biz-data-show-config-chart>
+                  <biz-data-show-config-chart :columns="columns"
+                                              v-model="form.renderingOptions"></biz-data-show-config-chart>
                 </template>
                 <template v-if="form.type === 'list'">
-                  <biz-data-show-config-list :columns="columns" v-model="form.renderingOptions"></biz-data-show-config-list>
+                  <biz-data-show-config-list :columns="columns"
+                                             v-model="form.renderingOptions"></biz-data-show-config-list>
                 </template>
                 <template v-if="form.type === 'table'">
                   <biz-data-show-config-table :columns="columns"
@@ -163,6 +167,7 @@ const bizDisplayRef = ref(null)
 
 /** 获取模型服务调用次数 */
 function getData() {
+  queryParams.value.appId = route.params.id
   getBizDataShowConfigList(queryParams.value).then(res => {
     tableData.value = res.data
   })

+ 144 - 28
ruoyi-ui/src/views/standardization/bizDataShowConfig/list/index.vue

@@ -5,39 +5,126 @@
         <el-option v-for="column in columns" :key="column.key" :label="column.value" :value="column"/>
       </el-select>
     </el-form-item>
+
+    <!-- 是否分页配置 -->
+    <el-form-item label="是否分页">
+      <el-switch v-model="form.pagination.enabled"/>
+    </el-form-item>
+
+    <!-- 分页配置项 -->
+    <template v-if="form.pagination.enabled">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="每页条数">
+            <el-input-number v-model="form.pagination.pageSize" :min="1" :max="100"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="显示总条数">
+            <el-switch v-model="form.pagination.showTotal"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="显示跳转">
+            <el-switch v-model="form.pagination.showJump"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="页码参数名">
+            <el-input v-model="form.pagination.pageNumParam" placeholder="例如: pageNum"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="页数参数名">
+            <el-input v-model="form.pagination.pageSizeParam" placeholder="例如: pageSize"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="数据列表字段">
+            <el-input v-model="form.pagination.dataListField" placeholder="例如: data.records"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="总数返回字段">
+            <el-input v-model="form.pagination.totalField" placeholder="例如: data.total"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </template>
+
     <!-- 添加行点击配置 -->
     <el-form-item label="点击配置">
       <el-radio-group v-model="form.clickAction.type">
         <el-radio label="none">无操作</el-radio>
         <el-radio label="map">跳转地图</el-radio>
+        <el-radio label="detail">查看详情</el-radio>
+        <el-radio label="link">跳转链接</el-radio>
       </el-radio-group>
     </el-form-item>
+
     <!-- 地图配置项 -->
-    <el-form-item v-if="form.clickAction.type === 'map'" label="经纬度字段">
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-select v-model="form.clickAction.longitudeField" placeholder="请选择经度字段" style="width: 120px;">
-            <el-option
-                v-for="column in columns"
-                :key="column.key"
-                :label="column.value"
-                :value="column.key"/>
-          </el-select>
-        </el-col>
-        <el-col :span="12">
-          <el-select v-model="form.clickAction.latitudeField" placeholder="请选择纬度字段" style="width: 120px;">
-            <el-option
-                v-for="column in columns"
-                :key="column.key"
-                :label="column.value"
-                :value="column.key"/>
-          </el-select>
-        </el-col>
-      </el-row>
+    <template v-if="form.clickAction.type === 'map'">
+      <el-form-item label="经纬度字段">
+        <el-row :gutter="10">
+          <el-col :span="12">
+            <el-select v-model="form.clickAction.longitudeField" placeholder="请选择经度字段" style="width: 100%;">
+              <el-option
+                  v-for="column in columns"
+                  :key="column.key"
+                  :label="column.value"
+                  :value="column.key"/>
+            </el-select>
+          </el-col>
+          <el-col :span="12">
+            <el-select v-model="form.clickAction.latitudeField" placeholder="请选择纬度字段" style="width: 100%;">
+              <el-option
+                  v-for="column in columns"
+                  :key="column.key"
+                  :label="column.value"
+                  :value="column.key"/>
+            </el-select>
+          </el-col>
+        </el-row>
+      </el-form-item>
+      <el-form-item label="地图标题字段">
+        <el-select v-model="form.clickAction.mapTitleField" placeholder="请选择地图标题字段" clearable>
+          <el-option
+              v-for="column in columns"
+              :key="column.key"
+              :label="column.value"
+              :value="column.key"/>
+        </el-select>
+      </el-form-item>
+    </template>
+
+    <!-- 详情配置项 -->
+    <el-form-item v-if="form.clickAction.type === 'detail'" label="详情页面路径">
+      <el-input v-model="form.clickAction.detailPath" placeholder="请输入详情页面路径"/>
     </el-form-item>
+
+    <!-- 链接配置项 -->
+    <template v-if="form.clickAction.type === 'link'">
+      <el-form-item label="链接地址">
+        <el-input v-model="form.clickAction.linkUrl" placeholder="请输入链接地址"/>
+      </el-form-item>
+      <el-form-item label="链接目标">
+        <el-radio-group v-model="form.clickAction.linkTarget">
+          <el-radio label="_self">当前窗口</el-radio>
+          <el-radio label="_blank">新窗口</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </template>
+
   </el-form>
 </template>
 <script setup>
+import {ref, watch} from 'vue'
+import {fa} from "element-plus/es/locales.mjs";
+
 const props = defineProps({
   columns: {
     type: Array,
@@ -46,12 +133,27 @@ const props = defineProps({
   modelValue: String,
 });
 const emit = defineEmits(['update:modelValue'])
+
 const form = ref({
   columns: [],
+  pagination: {
+    enabled: true,
+    pageSize: 10,
+    showTotal: true,
+    showJump: false,
+    pageNumParam: 'pageNum',
+    pageSizeParam: 'pageSize',
+    dataListField: 'data.records',  // 新增:数据列表字段
+    totalField: 'data.total'        // 修改:总数字段支持嵌套路径
+  },
   clickAction: {
-    type: 'none', // none, map
-    latitudeField: '',  // 纬度字段
-    longitudeField: ''  // 经度字段
+    type: 'none', // none, map, detail, link
+    latitudeField: '',     // 纬度字段
+    longitudeField: '',    // 经度字段
+    mapTitleField: '',     // 地图标题字段
+    detailPath: '',        // 详情页面路径
+    linkUrl: '',           // 链接地址
+    linkTarget: '_blank'   // 链接目标
   }
 })
 const rules = ref({})
@@ -61,18 +163,32 @@ watch(() => props.modelValue, modelValue => {
     const parsed = JSON.parse(modelValue)
     form.value = {
       columns: parsed.columns || [],
+      pagination: {
+        enabled: parsed.pagination?.enabled !== undefined ? parsed.pagination.enabled : true,
+        pageSize: parsed.pagination?.pageSize || 10,
+        showTotal: parsed.pagination?.showTotal !== undefined ? parsed.pagination.showTotal : true,
+        showJump: parsed.pagination?.showJump !== undefined ? parsed.pagination.showJump : true,
+        pageNumParam: parsed.pagination?.pageNumParam || 'pageNum',
+        pageSizeParam: parsed.pagination?.pageSizeParam || 'pageSize',
+        dataListField: parsed.pagination?.dataListField || 'data.records',  // 新增字段处理
+        totalField: parsed.pagination?.totalField || 'data.total'          // 修改默认值
+      },
       clickAction: {
         type: parsed.clickAction?.type || 'none',
         latitudeField: parsed.clickAction?.latitudeField || '',
-        longitudeField: parsed.clickAction?.longitudeField || ''
+        longitudeField: parsed.clickAction?.longitudeField || '',
+        mapTitleField: parsed.clickAction?.mapTitleField || '',
+        detailPath: parsed.clickAction?.detailPath || '',
+        linkUrl: parsed.clickAction?.linkUrl || '',
+        linkTarget: parsed.clickAction?.linkTarget || '_blank'
       }
     }
   }
 }, {immediate: true})
 
-watch(() => form, form => {
-  emit('update:modelValue', JSON.stringify(form.value))
+watch(() => form.value, formVal => {
+  emit('update:modelValue', JSON.stringify(formVal))
 }, {deep: true})
 </script>
 <style scoped lang="scss">
-</style>
+</style>

+ 109 - 0
ruoyi-ui/src/views/standardization/bizDataShowConfig/show/BizTable.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="biz-table-container">
+    <el-table
+        :data="tableData"
+        :height="paginationConfig?.enabled ? 'calc(100% - 30px)':'100%'"
+        stripe
+        @row-click="handleRowClick"
+    >
+      <el-table-column
+          v-for="column in columns"
+          :key="column.key"
+          :label="column.value"
+          :prop="column.key"
+      >
+        <template #default="scope">
+          {{ getValueByKey(scope.row, column.key) }}
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页组件 -->
+    <el-pagination
+        v-if="paginationConfig?.enabled"
+        :layout="paginationLayout"
+        :current-page="currentPage"
+        :page-size="paginationConfig.pageSize"
+        :total="total"
+        :pager-count="5"
+        @current-change="handlePageChange"
+        style="margin-top: 10px; justify-content: center;"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue'
+import { filterData } from "@/utils/data.js";
+import { isDate, isNumber } from "@/utils/validate.js";
+import { parseTime } from "@/utils/ruoyi.js";
+import bus from '@/utils/bus'
+
+const props = defineProps({
+  tableData: {
+    type: Array,
+    default: () => []
+  },
+  columns: {
+    type: Array,
+    default: () => []
+  },
+  clickConfig: {
+    type: Object,
+    default: () => ({ type: 'none' })
+  },
+  paginationConfig: {
+    type: Object,
+    default: () => ({ enabled: false })
+  },
+  currentPage: {
+    type: Number,
+    default: 1
+  },
+  total: {
+    type: Number,
+    default: 0
+  }
+})
+
+const emit = defineEmits(['row-click', 'page-change'])
+
+const paginationLayout = computed(() => {
+  let layout = 'prev, pager, next'
+  if (props.paginationConfig?.showJump) {
+    layout += ', jumper'
+  }
+  layout += ', ->'
+  if (props.paginationConfig?.showTotal) {
+    layout += ', total'
+  }
+  return layout
+})
+
+// 处理行点击事件
+function handleRowClick(row, column, event) {
+  emit('row-click', { row, column, event })
+}
+
+// 处理分页变化
+function handlePageChange(page) {
+  emit('page-change', page)
+}
+
+function getValueByKey(obj, key) {
+  let value = filterData(obj, key);
+  if (isNumber(value)) {
+    value = value.toFixed(2)
+  } else if (isDate(value)) {
+    value = parseTime(new Date(value), '{m}-{d} {h}')
+  }
+  return value
+}
+</script>
+
+<style scoped lang="scss">
+.biz-table-container {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 110 - 18
ruoyi-ui/src/views/standardization/bizDataShowConfig/show/index.vue

@@ -10,16 +10,16 @@
         <gw-chart :option="data"></gw-chart>
       </template>
       <template v-if="displayType === 'list'">
-        <el-table :data="data" height="100%" stripe
-                  @row-click="handleRowClick">
-          <el-table-column v-for="column in columns" :key="column.key" :label="column.value"
-                           :prop="column.key">
-            <template #default="scope">
-
-              {{ getValueByKey(scope.row, column.key) }}
-            </template>
-          </el-table-column>
-        </el-table>
+        <biz-table
+            :table-data="tableData"
+            :columns="columns"
+            :click-config="clickConfig"
+            :pagination-config="paginationConfig"
+            :current-page="currentPage"
+            :total="total"
+            @row-click="handleRowClick"
+            @page-change="handlePageChange"
+        />
       </template>
       <template v-if="displayType === 'table'">
         <gw-table-two :columnNumber="columnNumber" :data="data"></gw-table-two>
@@ -30,14 +30,18 @@
     </div>
   </div>
 </template>
+
 <script setup>
 import GwChart from "@/components/chart/GwEchart.vue";
-import {getChartOption, getFormListData, getTableData, getText} from '@/utils/biz'
+import {getChartOption, getFormListData, getText} from '@/utils/biz'
 import GwTableTwo from "./GwTableTwo.vue";
+import BizTable from "./BizTable.vue"; // 引入新组件
 import bus from '@/utils/bus'
 import {filterData} from "@/utils/data.js";
 import {isDate, isNumber} from "@/utils/validate.js";
 import {parseTime} from "@/utils/ruoyi.js";
+import {getBizDataById} from "@/api/standardization/bizDataShowConfig.js";
+import {getDataSetBizDataById} from "@/api/register/regCom.js";
 
 defineExpose({
   loadData
@@ -50,7 +54,11 @@ const props = defineProps({
   },
   showTitle: {
     type: Boolean,
-    default: true
+    default: false
+  },
+  source: {
+    type: String,
+    default: "BIZ_DATA_SHOW"
   },
 });
 
@@ -62,26 +70,35 @@ const bodyHeight = computed(() => {
     return '100%'
   }
 })
+
 const data = ref(null)
 const title = ref(null)
 const displayType = ref(null)
 const columnNumber = ref(1)
 const columns = ref([])
 const clickConfig = ref(null)
+const paginationConfig = ref({})
+
+// 分页相关数据
+const tableData = ref([])
+const currentPage = ref(1)
+const total = ref(0)
 
 function loadData(config) {
+  debugger
   if (!config) {
     return
   }
   config.value = config
-  displayType.value = config.type
+  displayType.value = config.type || 'list'
   title.value = config.name
 
-  // 解析点击配置
+  // 解析渲染配置
   const renderingOptions = JSON.parse(config.renderingOptions || '{}')
   clickConfig.value = renderingOptions.clickAction || {type: 'none'}
+  paginationConfig.value = renderingOptions.pagination || {enabled: false}
 
-  switch (config.type) {
+  switch (displayType.value) {
     case 'chart':
       nextTick(() => {
         getChartOption(config).then(r => data.value = r);
@@ -89,7 +106,9 @@ function loadData(config) {
       return;
     case 'list':
       columns.value = renderingOptions.columns
-      return getTableData(config).then(r => data.value = r);
+      // 加载表格数据(支持分页)
+      loadTableData(config, 1)
+      return;
     case 'table':
       columnNumber.value = renderingOptions.columnNumber
       return getFormListData(config).then(r => data.value = r);
@@ -98,8 +117,62 @@ function loadData(config) {
   }
 }
 
+// 加载表格数据(支持分页)
+async function loadTableData(config, page = 1) {
+  try {
+    const params = {}
+    if (paginationConfig.value.enabled) {
+      params[paginationConfig.value.pageNumParam || 'pageNum'] = page
+      params[paginationConfig.value.pageSizeParam || 'pageSize'] = paginationConfig.value.pageSize || 10
+    }
+
+    let res;
+    switch (props.source) {
+      case "DATA_SET":
+        res = await getDataSetBizDataById(config.id, params)
+        break
+      case "BIZ_DATA_SHOW":
+      default:
+        res = await getBizDataById(config.id, params)
+        break
+    }
+
+    if (res && res.data) {
+      if (paginationConfig.value.enabled) {
+        // 提取数据列表
+        const dataList = getDataByPath(res.data, paginationConfig.value.dataListField)
+        // 提取总数
+        total.value = getDataByPath(res.data, paginationConfig.value.totalField || 'total') || 0
+
+        tableData.value = Array.isArray(dataList) ? dataList : []
+        currentPage.value = page
+      } else {
+        // 不分页情况
+        tableData.value = Array.isArray(res.data) ? res.data : []
+        total.value = tableData.value.length
+      }
+    }
+  } catch (error) {
+    console.error('加载数据失败:', error)
+    tableData.value = []
+    total.value = 0
+  }
+}
+
+// 根据路径获取数据
+function getDataByPath(obj, path) {
+  if (!obj || !path) return obj
+  const paths = path.split('.')
+  let result = obj
+  for (const p of paths) {
+    result = result[p]
+    if (result === undefined) break
+  }
+  return result
+}
+
 // 处理行点击事件
-function handleRowClick(row, column, event) {
+function handleRowClick({row, column, event}) {
   if (!clickConfig.value || clickConfig.value.type === 'none') {
     return
   }
@@ -112,6 +185,25 @@ function handleRowClick(row, column, event) {
       // 跳转到地图页面,传递经纬度参数
       bus.emit('show-map-position', {latitude, longitude, highlight: true})
     }
+  } else if (clickConfig.value.type === 'detail') {
+    // 查看详情逻辑
+    if (clickConfig.value.detailPath) {
+      // 可以在这里实现跳转到详情页的逻辑
+      console.log('跳转到详情页:', clickConfig.value.detailPath, row)
+    }
+  } else if (clickConfig.value.type === 'link') {
+    // 跳转链接逻辑
+    if (clickConfig.value.linkUrl) {
+      window.open(clickConfig.value.linkUrl, clickConfig.value.linkTarget || '_blank')
+    }
+  }
+}
+
+// 处理分页变化
+function handlePageChange(page) {
+  // if (props.config && props.config.type === 'list') {
+  if (displayType.value === 'list') {
+    loadTableData(props.config, page)
   }
 }
 
@@ -131,6 +223,7 @@ watch(() => props.config, (config) => {
   }
 }, {immediate: true})
 </script>
+
 <style lang="scss" scoped>
 .biz-display-container {
   width: 100%;
@@ -148,6 +241,5 @@ watch(() => props.config, (config) => {
   .biz-display-body {
     height: calc(100% - 1rem - 20px);
   }
-
 }
 </style>

+ 356 - 0
ruoyi-ui/src/views/system/plugin/index.vue

@@ -0,0 +1,356 @@
+<template>
+  <div class="app-container">
+    <el-row>
+      <el-col :span="18">
+        <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+          <el-form-item label="插件名">
+            <el-input v-model="queryParams.name" placeholder="请输入插件名" clearable @keyup.enter="handleQuery"/>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-col>
+      <el-col :span="6" style="display: flex;justify-content: right;">
+        <el-button v-hasPermi="['system:plugin:upload']" type="primary" plain icon="Upload" @click="handleFile">
+          上传插件
+        </el-button>
+      </el-col>
+    </el-row>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button v-hasPermi="['system:plugin:remove']" type="primary" plain icon="Delete" :disabled="single"
+                   @click="handleLoadPlugin">
+          启动插件
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button v-hasPermi="['system:plugin:remove']" type="primary" plain icon="Delete" :disabled="single"
+                   @click="handlePluginTest">
+          测试插件
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button v-hasPermi="['system:plugin:remove']" type="danger" plain icon="Delete" :disabled="single"
+                   @click="handleDelete">
+          删除
+        </el-button>
+      </el-col>
+    </el-row>
+    <el-table
+        v-if="showTable"
+        v-loading="loading"
+        :data="dataList"
+        border
+        :header-cell-class-name="handleHeaderClass"
+        @selection-change="handleSelectionChange"
+        @header-click="handleHeaderCLick"
+    >
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column v-if="false" label="对象存储主键" align="center" prop="ossId"/>
+      <el-table-column label="插件名" align="center" prop="id"/>
+      <el-table-column label="版本号" align="center" prop="version"/>
+      <el-table-column label="开发者" align="center" prop="provider"/>
+      <el-table-column label="运行状态" align="center" prop="state">
+        <template #default="scope">
+          <el-tag type="primary" v-if="scope.row.state==='STARTED'">启用</el-tag>
+          <el-tag type="danger" v-if="scope.row.state==='DISABLED'">停用</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-tooltip v-if="scope.row.state==='DISABLED'" content="启动" placement="top">
+            <el-button v-hasPermi="['system:plugin:start']" link type="primary" @click="handlePluginStart(scope.row)">
+              <svg-icon icon-class="play2"/>
+            </el-button>
+          </el-tooltip>
+          <el-tooltip v-if="scope.row.state==='STARTED'" content="停止" placement="top">
+            <el-button v-hasPermi="['system:plugin:stop']" link type="primary" @click="handlePluginStop(scope.row)">
+              <svg-icon icon-class="stop2"/>
+            </el-button>
+          </el-tooltip>
+          <el-tooltip content="删除" placement="top">
+            <el-button v-hasPermi="['system:plugin:stop']" link type="danger" @click="handlePluginStop(scope.row)"
+                       icon="Delete">
+            </el-button>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+                :total="total" @pagination="getList"/>
+
+    <!-- 上传插件对话框 -->
+    <!--    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>-->
+    <!--      <el-form ref="pluginFormRef" :model="form" :rules="rules" label-width="80px">-->
+    <!--        <el-form-item label="文件名">-->
+    <!--          <fileUpload v-model="form.file"/>-->
+    <!--        </el-form-item>-->
+    <!--      </el-form>-->
+    <!--      <template #footer>-->
+    <!--        <div class="dialog-footer">-->
+    <!--          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>-->
+    <!--          <el-button @click="cancel">取 消</el-button>-->
+    <!--        </div>-->
+    <!--      </template>-->
+    <!--    </el-dialog>-->
+    <!-- 用户导入对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-upload
+          ref="uploadRef"
+          :limit="1"
+          accept=".jar, .zip"
+          :headers="upload.headers"
+          :action="upload.url + '?updateSupport=' + upload.updateSupport"
+          :disabled="upload.isUploading"
+          :on-progress="handleFileUploadProgress"
+          :on-success="handleFileSuccess"
+          :auto-upload="false"
+          drag
+      >
+        <el-icon class="el-icon--upload">
+          <UploadFilled/>
+        </el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="text-center el-upload__tip">
+            <span style="color: red;">仅允许导入jar、zip格式文件。</span>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {
+  pluginDelete,
+  pluginList,
+  pluginReload,
+  pluginStart,
+  pluginStop,
+  pluginTest
+} from '@/api/system/plugin/index.js';
+import {getToken} from "@/utils/auth.js";
+import SvgIcon from "@/components/SvgIcon/index.vue";
+
+const router = useRouter();
+const {proxy} = getCurrentInstance();
+
+const dataList = ref([]);
+const showTable = ref(true);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const type = ref(0);
+
+/*** 用户上传插件参数 */
+const upload = reactive({
+  // 是否显示弹出层(上传插件)
+  open: false,
+  // 弹出层标题(弹出框title)
+  title: '',
+  // 是否禁用上传
+  isUploading: false,
+  // 设置上传的请求头部
+  headers: {Authorization: "Bearer " + getToken()},
+  // 上传的地址
+  url: import.meta.env.VITE_APP_BASE_API + '/plugin/upload'
+});
+
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+
+// 默认排序
+
+const pluginFormRef = ref();
+const queryFormRef = ref();
+const uploadRef = ref();
+
+const initFormData = {
+  file: undefined
+};
+
+const data = reactive({
+  form: {...initFormData},
+  // 查询参数
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: '',
+  },
+  rules: {
+    file: [{required: true, message: '文件不能为空', trigger: 'blur'}]
+  }
+});
+
+const {queryParams, form, rules} = toRefs(data);
+
+/** 查询插件存储列表 */
+const getList = () => {
+  loading.value = true;
+  pluginList(queryParams.value).then(response => {
+    dataList.value = response;
+    loading.value = false;
+    showTable.value = true;
+  });
+};
+
+/** 提交上传文件 */
+function submitFileForm() {
+  uploadRef.value.submit();
+}
+
+/**文件上传中处理 */
+const handleFileUploadProgress = () => {
+  upload.isUploading = true;
+};
+
+/** 文件上传成功处理 */
+const handleFileSuccess = (response, file) => {
+  if (response.code === 200) {
+    proxy.$modal.msgSuccess("上传成功");
+    uploadRef.value.handleRemove(file);
+    cancel()
+    getList();
+  } else {
+    proxy.$modal.msgError("上传失败:" + response.msg);
+    uploadRef.value.handleRemove(file);
+  }
+};
+
+/** 取消按钮 */
+function cancel() {
+  dialog.visible = false;
+  reset();
+}
+
+/** 表单重置 */
+function reset() {
+  form.value = {...initFormData};
+  pluginFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  showTable.value = false;
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+
+/** 选择条数  */
+function handleSelectionChange(selection) {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 设置列的排序为我们自定义的排序 */
+const handleHeaderClass = ({column}) => {
+  column.order = column.multiOrder;
+};
+/** 点击表头进行排序 */
+const handleHeaderCLick = (column) => {
+  if (column.sortable !== 'custom') {
+    return;
+  }
+  switch (column.multiOrder) {
+    case 'descending':
+      column.multiOrder = 'ascending';
+      break;
+    case 'ascending':
+      column.multiOrder = '';
+      break;
+    default:
+      column.multiOrder = 'descending';
+      break;
+  }
+  // handleOrderChange(column.property, column.multiOrder);
+};
+
+/** 文件按钮操作 */
+const handleFile = () => {
+  reset();
+  type.value = 0;
+  dialog.visible = true;
+  dialog.title = '上传插件';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  dialog.visible = false;
+  getList();
+};
+
+const handleLoadPlugin = async () => {
+  proxy.$modal.confirm('确认启动系统中所有插件吗?')
+      .then(() => pluginReload())
+      .then(() => {
+        getList();
+        proxy.$modal.msgSuccess('启动成功');
+      })
+};
+
+/** 插件【启动】操作 */
+const handlePluginStart = async (row) => {
+  const pluginIds = row?.id || ids.value;
+  await proxy.$modal.confirm('确认【启动】所选择的插件吗?');
+  pluginStart(pluginIds).then((response) => {
+    getList();
+    proxy.$modal.msgSuccess('启动成功');
+  });
+};
+
+/** 插件【停止】操作 */
+const handlePluginStop = async (row) => {
+  const pluginIds = row?.id || ids.value;
+  const [err] = await to(proxy.$modal.confirm('是否【停止】所选择的插件"' + pluginIds + '"?'));
+  if (!err) {
+    pluginStop(pluginIds).then((response) => {
+      getList();
+      proxy.$modal.msgSuccess('停止成功');
+    });
+  }
+};
+
+/** 插件【测试】操作 */
+const handlePluginTest = async (row) => {
+  const pluginIds = row?.id || ids.value;
+  pluginTest(pluginIds).then((response) => {
+    getList();
+    proxy.$modal.msgSuccess('测试成功');
+  });
+};
+
+function handleDelete() {
+  proxy.$modal.confirm('是否确认删除?').then(() => pluginDelete(ids.value.join(',')))
+      .then(() => {
+        getList();
+        proxy.$modal.msgSuccess("删除成功");
+      })
+}
+
+//打开页面时,读取数据
+onMounted(() => {
+  getList();
+});
+</script>