Ver código fonte

Merge branch 'master' of http://git.yangzhiqiang.tech/jiayq/ai-tagging

jiayongqiang 5 dias atrás
pai
commit
bdc1fa4bae
62 arquivos alterados com 3463 adições e 710 exclusões
  1. 12 0
      .gitignore
  2. BIN
      server/.gitignore
  3. 37 0
      server/pom.xml
  4. 179 0
      server/yusp-tagging-core/pom.xml
  5. 47 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/config/RedisConfig.java
  6. 86 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagAppController.java
  7. 95 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagCategoryController.java
  8. 16 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagAppCreateDTO.java
  9. 19 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagTagCategoryCreateDTO.java
  10. 23 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagTagCategoryUpdateDTO.java
  11. 81 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagApp.java
  12. 67 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagCategory.java
  13. 99 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfo.java
  14. 373 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/TabLog.java
  15. 33 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/AitagTagCategoryVO.java
  16. 44 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagAppMapper.java
  17. 41 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagCategoryMapper.java
  18. 80 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/model/Result.java
  19. 18 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagAppService.java
  20. 19 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagCategoryService.java
  21. 120 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagAppServiceImpl.java
  22. 136 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagCategoryServiceImpl.java
  23. 63 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/util/DataUtil.java
  24. BIN
      server/yusp-tagging-core/src/main/resources/cert/pwd_private.key
  25. BIN
      server/yusp-tagging-core/src/main/resources/cert/pwd_public.key
  26. 121 0
      server/yusp-tagging-core/src/main/resources/mapper/AitagAppMapper.xml
  27. 62 0
      server/yusp-tagging-core/src/main/resources/mapper/AitagTagCategoryMapper.xml
  28. 99 0
      server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg.properties
  29. 99 0
      server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg_en.properties
  30. 99 0
      server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg_zh.properties
  31. BIN
      server/yusp-tagging-core/src/main/resources/template/detail_col_template.xlsx
  32. 30 0
      server/yusp-tagging-starter/Dockerfile
  33. 250 0
      server/yusp-tagging-starter/pom.xml
  34. 16 0
      server/yusp-tagging-starter/src/main/java/cn/com/yusys/yusp/detail/App.java
  35. 56 0
      server/yusp-tagging-starter/src/main/resources/application.yml
  36. 25 0
      server/yusp-tagging-starter/src/main/resources/bootstrap.yml
  37. 8 0
      server/yusp-tagging-starter/src/main/scripts/deploy.sh
  38. 17 0
      server/yusp-tagging-starter/src/main/scripts/shutdown.sh
  39. 1 0
      server/yusp-tagging-starter/src/main/scripts/startup.cmd
  40. 105 0
      server/yusp-tagging-starter/src/main/scripts/startup.sh
  41. BIN
      web/dist.zip
  42. 2 2
      web/src/api/common/oauth.js
  43. 3 0
      web/src/assets/common/base.css
  44. 1 1
      web/src/assets/common/common.css
  45. 0 1
      web/src/components/base/Login/index.vue
  46. 3 3
      web/src/components/layout/Navbar/topLeft.vue
  47. 0 2
      web/src/components/layout/TopLeft/leftMenu.vue
  48. 3 0
      web/src/config/constant/app.data.service.js
  49. 1 1
      web/src/config/other/css.js
  50. 34 34
      web/src/router/router-filter.js
  51. 156 85
      web/src/store/modules/oauth.js
  52. 102 252
      web/src/views/content/aiTagging/appAuthManage/index.vue
  53. 22 76
      web/src/views/content/aiTagging/taggingLogs/components/LogDetailDrawer.vue
  54. 105 170
      web/src/views/content/aiTagging/taggingLogs/index.vue
  55. 6 9
      web/src/views/content/aiTagging/taggingResults/components/Detail.vue
  56. 1 1
      web/src/views/content/aiTagging/taggingResults/components/Overview.vue
  57. 3 3
      web/src/views/content/aiTagging/taggingResults/index.vue
  58. 1 1
      web/src/views/content/aiTagging/taggingSystemManage/TagSystemEdit.vue
  59. 28 4
      web/src/views/content/aiTagging/taggingSystemManage/components/CreateTagSystem.vue
  60. 107 63
      web/src/views/content/aiTagging/taggingSystemManage/index.vue
  61. 208 0
      web/src/views/content/aiTagging/utils/common.js
  62. 1 2
      web/src/views/content/workstation/workstation/workstation.vue

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+# JetBrains IDE
+.idea/
+*.iml
+*.iws
+
+# Other common ignores
+*.log
+target/
+build/
+.DS_Store
+Thumbs.db
+node_modules/".idea/" 

BIN
server/.gitignore


+ 37 - 0
server/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>cn.com.yusys.yusp</groupId>
+    <parent>
+        <artifactId>yusp-dependencies</artifactId>
+        <groupId>cn.com.yusys.yusp</groupId>
+        <version>V3.3.2.20220606.RELEASE</version>
+    </parent>
+    <artifactId>server</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <properties>
+        <!-- 文件拷贝时的编码 -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <!-- 编译时的编码 -->
+        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
+        <java.version>1.8</java.version>
+        <yusp.flow.version>V3.3.2.20220606.RELEASE</yusp.flow.version>
+        <yusp.file.version>V3.3.2.20220606.RELEASE</yusp.file.version>
+        <ojdbc8.version>19.3.0.0</ojdbc8.version>
+        <dynamic.datasource.version>3.4.1</dynamic.datasource.version>
+    </properties>
+    <!-- 模块 -->
+    <modules>
+        <module>yusp-tagging-core</module>
+        <module>yusp-tagging-starter</module>
+    </modules>
+    <name>server</name>
+
+
+</project>

+ 179 - 0
server/yusp-tagging-core/pom.xml

@@ -0,0 +1,179 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>cn.com.yusys.yusp</groupId>
+    <parent>
+        <artifactId>server</artifactId>
+        <groupId>cn.com.yusys.yusp</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>yusp-tagging-core</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <dependencies>
+
+
+        <dependency>
+            <groupId>cn.com.yusys.yusp.common</groupId>
+            <artifactId>yusp-commons-starter-redis</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.yusys.yusp.common</groupId>
+            <artifactId>yusp-commons-starter-mybatisplus</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-data-redis</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>5.3.33</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.17.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+
+
+<!--         2026.02.06新增-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.20</version> <!-- 版本号可根据需要调整 -->
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.30</version> <!-- 至少 1.18.16+ -->
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.4.3.4</version> <!-- 明确使用 3.4.x,支持 Java 8 -->
+            <exclusions>
+                <!-- 强制排除其可能引入的 HikariCP 4.x -->
+                <exclusion>
+                    <groupId>com.zaxxer</groupId>
+                    <artifactId>HikariCP</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>3.4.5</version> <!-- 最后一个支持 Java 8 的版本 -->
+            <scope>compile</scope>
+        </dependency>
+
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.zaxxer</groupId>
+                <artifactId>HikariCP</artifactId>
+                <version>3.4.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>

+ 47 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/config/RedisConfig.java

@@ -0,0 +1,47 @@
+package cn.com.yusys.yusp.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * @模块 : 标题
+ * @作者 : PrimaryKeyEnoch
+ * @时间 : 2021/11/29 18 : 11
+ * @描述 : 简述
+ */
+
+
+public class RedisConfig {
+
+    @Bean
+    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory){
+
+        RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<Object,Object>();
+        redisTemplate.setConnectionFactory(factory);
+        // 定义jackson2JsonRedisSerializer对象 这里使用jackson进行序列化
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+        // 指定要序列化的域,包括public private等
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        // 指定序列化的类型,类必须是非final修饰的才可以
+        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
+        // key 序列化方式用string
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        //value 方式用json存储
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+        // hash存储方式
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+}

+ 86 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagAppController.java

@@ -0,0 +1,86 @@
+package cn.com.yusys.yusp.controller;
+
+import cn.com.yusys.yusp.model.Result;
+import cn.com.yusys.yusp.domain.dto.AitagAppCreateDTO;
+import cn.com.yusys.yusp.domain.entity.AitagApp;
+import cn.com.yusys.yusp.service.AitagAppService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/aitag-app")
+public class AitagAppController {
+
+    @Autowired
+    private AitagAppService aiTagAppService;
+
+    @GetMapping("/list")
+    public Result<List<AitagApp>> listApps(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size) {
+        try {
+            // 分页查询
+            Page<AitagApp> pageResult = aiTagAppService.listApps(page, size);
+
+            // 提取 records 并封装为 Result 对象
+            return Result.pageSuccess(pageResult.getRecords(), pageResult.getTotal());
+        } catch (Exception e) {
+            return Result.error("500", "分页查询失败:" + e.getMessage());
+        }
+    }
+
+
+
+    @PostMapping("/add")
+    public Result<AitagApp> addApp(@Validated @RequestBody AitagAppCreateDTO dto) {
+        try {
+            AitagApp app = aiTagAppService.addApp(dto);
+            return Result.success(app); // 成功时返回封装后的数据
+        } catch (Exception e) {
+            return Result.error("500", "应用新增失败:" + e.getMessage()); // 异常时返回错误信息
+        }
+    }
+
+
+    @GetMapping("/query")
+    public Result<List<AitagApp>> queryByName(
+            @RequestParam String appName,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize) {
+        try {
+            Page<AitagApp> pageResult = aiTagAppService.queryByAppNameLikeWithPagination(appName, page, pageSize);
+            return Result.pageSuccess(pageResult.getRecords(), pageResult.getTotal()); // ✅ 返回 Result<List<AitagApp>>
+        } catch (Exception e) {
+            return Result.error("500", "应用查询失败:" + e.getMessage());
+        }
+    }
+
+
+
+
+    @PostMapping("/reset-secret")
+    public Result<String> resetSecret(@RequestParam String id) {
+        try {
+            String newSecret = aiTagAppService.resetSecret(id);
+            return Result.success(newSecret);
+        } catch (Exception e) {
+            return Result.error("500", "密钥重置失败:" + e.getMessage());
+        }
+    }
+
+
+    @PostMapping("/disable")
+    public Result<Void> disableApp(@RequestParam String id) {
+        try {
+            aiTagAppService.disableApp(id);
+            return Result.success();
+        } catch (Exception e) {
+            return Result.error("500", "应用禁用失败:" + e.getMessage());
+        }
+    }
+
+}

+ 95 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagCategoryController.java

@@ -0,0 +1,95 @@
+package cn.com.yusys.yusp.controller;
+
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryCreateDTO;
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryUpdateDTO;
+import cn.com.yusys.yusp.domain.entity.AitagTagCategory;
+import cn.com.yusys.yusp.domain.vo.AitagTagCategoryVO;
+import cn.com.yusys.yusp.model.Result;
+import cn.com.yusys.yusp.service.AitagTagCategoryService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/aitag-tagcategory")
+public class AitagTagCategoryController {
+
+    @Autowired
+    private AitagTagCategoryService aiTagCategoryService;
+
+    @GetMapping("/list")
+    public Result<List<AitagTagCategoryVO>> listCategories(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "5") int size) {
+        try {
+            Page<AitagTagCategoryVO> pageResult = aiTagCategoryService.listCategories(page, size);
+            return Result.pageSuccess(pageResult.getRecords(), pageResult.getTotal());
+        } catch (Exception e) {
+            return Result.error("500", "分页查询失败:" + e.getMessage());
+        }
+    }
+
+
+    @PostMapping("/create")
+    public Result<AitagTagCategory> createCategory(@Validated @RequestBody AitagTagCategoryCreateDTO dto) {
+        try {
+            AitagTagCategory category = aiTagCategoryService.createCategory(dto);
+            return Result.success(category);
+        } catch (Exception e) {
+            return Result.error("500", "新建标签体系失败:" + e.getMessage());
+        }
+    }
+
+    @PostMapping("/update")
+    public Result<AitagTagCategory> updateCategory(@Validated @RequestBody AitagTagCategoryUpdateDTO dto) {
+        try {
+            AitagTagCategory category = aiTagCategoryService.updateCategory(dto);
+            return Result.success(category);
+        } catch (Exception e) {
+            return Result.error("500", "编辑标签体系失败:" + e.getMessage());
+        }
+    }
+
+    @GetMapping("/query")
+    public Result<List<AitagTagCategory>> searchByCategoryNm(@RequestParam String categoryNm) {
+        try {
+            List<AitagTagCategory> categories = aiTagCategoryService.searchByCategoryNm(categoryNm);
+            return Result.success(categories);
+        } catch (Exception e) {
+            return Result.error("500", "模糊查询失败:" + e.getMessage());
+        }
+    }
+
+    @PostMapping("/enable")
+    public Result<Void> enableCategory(@RequestParam String id) {
+        try {
+            aiTagCategoryService.enableCategory(id);
+            return Result.success();
+        } catch (Exception e) {
+            return Result.error("500", "启用标签体系失败:" + e.getMessage());
+        }
+    }
+
+    @PostMapping("/disable")
+    public Result<Void> disableCategory(@RequestParam String id) {
+        try {
+            aiTagCategoryService.disableCategory(id);
+            return Result.success();
+        } catch (Exception e) {
+            return Result.error("500", "停用标签体系失败:" + e.getMessage());
+        }
+    }
+
+    @PostMapping("/delete")
+    public Result<Void> deleteCategory(@RequestParam String id) {
+        try {
+            aiTagCategoryService.deleteCategory(id);
+            return Result.success();
+        } catch (Exception e) {
+            return Result.error("500", "删除标签体系失败:" + e.getMessage());
+        }
+    }
+}

+ 16 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagAppCreateDTO.java

@@ -0,0 +1,16 @@
+package cn.com.yusys.yusp.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel("新增应用请求DTO")
+public class AitagAppCreateDTO {
+
+    @NotBlank(message = "应用名称不能为空")
+    @ApiModelProperty(value = "应用名称", required = true)
+    private String appName;
+}

+ 19 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagTagCategoryCreateDTO.java

@@ -0,0 +1,19 @@
+package cn.com.yusys.yusp.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel("新建标签体系请求DTO")
+public class AitagTagCategoryCreateDTO {
+
+    @NotBlank(message = "标签体系名称不能为空")
+    @ApiModelProperty(value = "标签体系名称", required = true)
+    private String categoryNm;
+
+    @ApiModelProperty(value = "标签体系描述")
+    private String categoryDesc;
+}

+ 23 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/AitagTagCategoryUpdateDTO.java

@@ -0,0 +1,23 @@
+package cn.com.yusys.yusp.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel("编辑标签体系请求DTO")
+public class AitagTagCategoryUpdateDTO {
+
+    @NotBlank(message = "ID不能为空")
+    @ApiModelProperty(value = "标签体系ID", required = true)
+    private String id;
+
+    @NotBlank(message = "标签体系名称不能为空")
+    @ApiModelProperty(value = "标签体系名称", required = true)
+    private String categoryNm;
+
+    @ApiModelProperty(value = "标签体系描述")
+    private String categoryDesc;
+}

+ 81 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagApp.java

@@ -0,0 +1,81 @@
+package cn.com.yusys.yusp.domain.entity;
+
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * 应用信息实体
+ * @TableName aitag_app
+ */
+@Data
+public class AitagApp implements Serializable {
+
+    /**
+     * 主键ID(UUID生成)
+     */
+    @NotBlank(message = "ID不能为空")
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "主键ID", required = true)
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String id;
+
+    /**
+     * 应用名称
+     */
+    @NotBlank(message = "应用名称不能为空")
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "应用名称", required = true)
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String appName;
+
+    /**
+     * 应用ID(YY+日期+序列号格式)
+     */
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "应用ID(非主键)")
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String appId;
+
+    /**
+     * 应用密钥
+     */
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "应用密钥")
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String appSecret;
+
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 创建人姓名
+     */
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "创建人姓名")
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String createrNm;
+
+    /**
+     * 创建人ID
+     */
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "创建人ID")
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String createrId;
+
+    /**
+     * 状态:0启用;1禁用
+     */
+    @Size(max = 100, message = "编码长度不能超过100")
+    @ApiModelProperty(value = "状态:0启用;1禁用", example = "0")
+    @Length(max = 100, message = "编码长度不能超过100")
+    private String state;
+}

+ 67 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagCategory.java

@@ -0,0 +1,67 @@
+package cn.com.yusys.yusp.domain.entity;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+
+import java.io.Serializable;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ *
+ * @TableName aitag_tag_category
+ */
+@Data
+public class AitagTagCategory implements Serializable {
+
+    /**
+     *
+     */
+    @NotBlank(message="[]不能为空")
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String id;
+    /**
+     * 类别代码
+     */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("类别代码")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String categoryCode;
+    /**
+     * 类别名称
+     */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("类别名称")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String categoryNm;
+
+    /**
+     * 类别描述
+     */
+    @Size(max = 500, message = "编码长度不能超过500")
+    @ApiModelProperty("类别描述")
+    @Length(max = 500, message = "编码长度不能超过500")
+    private String categoryDesc;
+
+    /**
+     * 生效级别
+     */
+    @ApiModelProperty("生效级别")
+    private Integer visibilityLevel;
+    /**
+     * 状态:0启用;1停用
+     */
+    @ApiModelProperty("0启用;1停用")
+    private Integer state;
+    /**
+     * 删除标记:0未删除;1删除
+     */
+    @ApiModelProperty("0未删除;1删除")
+    private Integer isDelete;
+
+}

+ 99 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfo.java

@@ -0,0 +1,99 @@
+package cn.com.yusys.yusp.domain.entity;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+
+import java.io.Serializable;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+/**
+* 
+* @TableName aitag_tag_info
+*/
+@Data
+public class AitagTagInfo implements Serializable {
+
+    /**
+    * 
+    */
+    @NotBlank(message="[]不能为空")
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String id;
+    /**
+    * 所属大类
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("所属大类")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String categoryId;
+    /**
+    * 标签名称
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("标签名称")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String tagNm;
+    /**
+    * 标签代码
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("标签代码")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String tagCode;
+    /**
+    * 标签备注
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("标签备注")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String tagRemark;
+    /**
+    * 父级ID
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("父级ID")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String parentCode;
+    /**
+    * 标签规则
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("标签规则")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String reg;
+    /**
+    * 标签等级
+    */
+    @ApiModelProperty("标签等级")
+    private Integer level;
+    /**
+    * tag1/tag2/tag3/...
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("tag1/tag2/tag3/...")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String tagPath;
+    /**
+    * 0未删除;1删除
+    */
+    @ApiModelProperty("0未删除;1删除")
+    private Integer isDelete;
+    /**
+    * 0 正常;1 停用
+    */
+    @ApiModelProperty("0 正常;1 停用")
+    private Integer state;
+    /**
+    * 标签提示词
+    */
+    @Size(max= 1000,message="编码长度不能超过1000")
+    @ApiModelProperty("标签提示词")
+    @Length(max= 1000,message="编码长度不能超过1,000")
+    private String tagPrompt;
+}

+ 373 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/TabLog.java

@@ -0,0 +1,373 @@
+package cn.com.yusys.yusp.domain.entity;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+
+import java.io.Serializable;
+
+import java.util.Date;
+import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.Length;
+
+/**
+* 
+* @TableName tab_log
+*/
+public class TabLog implements Serializable {
+
+    /**
+    * 
+    */
+    @NotBlank(message="[]不能为空")
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String id;
+    /**
+    * 应用ID
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("应用ID")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String appId;
+    /**
+    * 调用时间
+    */
+    @ApiModelProperty("调用时间")
+    private Date insertTime;
+    /**
+    * 业务属性,贷款编号
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("业务属性,贷款编号")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String businessAttr;
+    /**
+    * tab_tag_category的主键
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("tab_tag_category的主键")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String category;
+    /**
+    * 输入短语
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("输入短语")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String phrase;
+    /**
+    * 附件名称
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("附件名称")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String attachment;
+    /**
+    * 附件路径
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("附件路径")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String attachmentUrl;
+    /**
+    * 打标结果,JSON
+[{
+label:xxx,
+label_code:xxx,
+desc:xxx
+passr: true/false
+}]
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("打标结果,JSON [{label:xxx, label_code:xxx, desc:xxx passr: true/false }]")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String result;
+    /**
+    * 反馈人ID
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("反馈人ID")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String feedbackUserId;
+    /**
+    * 反馈人名字
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("反馈人名字")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String feedbackUserNm;
+    /**
+    * 反馈时间
+    */
+    @ApiModelProperty("反馈时间")
+    private Date feedbackTime;
+    /**
+    * 反馈,agree/reject
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("反馈,agree/reject")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String feedback;
+    /**
+    * JSON
+[
+{
+ label:xxx,
+ label_code:xxx,
+ desc:xxx
+ passr: true/false
+}
+]
+    */
+    @Size(max= 100,message="编码长度不能超过100")
+    @ApiModelProperty("JSON {label:xxx, label_code:xxx, desc:xxx passr: true/false}]")
+    @Length(max= 100,message="编码长度不能超过100")
+    private String feedbackResult;
+    /**
+    * 0:打标执行中;1:打标完成; 2:客户经理已经确认
+    */
+    @ApiModelProperty("0:打标执行中;1:打标完成; 2:客户经理已经确认")
+    private Integer state;
+
+    /**
+    * 
+    */
+    private void setId(String id){
+    this.id = id;
+    }
+
+    /**
+    * 应用ID
+    */
+    private void setAppId(String appId){
+    this.appId = appId;
+    }
+
+    /**
+    * 调用时间
+    */
+    private void setInsertTime(Date insertTime){
+    this.insertTime = insertTime;
+    }
+
+    /**
+    * 业务属性,贷款编号
+    */
+    private void setBusinessAttr(String businessAttr){
+    this.businessAttr = businessAttr;
+    }
+
+    /**
+    * tab_tag_category的主键
+    */
+    private void setCategory(String category){
+    this.category = category;
+    }
+
+    /**
+    * 输入短语
+    */
+    private void setPhrase(String phrase){
+    this.phrase = phrase;
+    }
+
+    /**
+    * 附件名称
+    */
+    private void setAttachment(String attachment){
+    this.attachment = attachment;
+    }
+
+    /**
+    * 附件路径
+    */
+    private void setAttachmentUrl(String attachmentUrl){
+    this.attachmentUrl = attachmentUrl;
+    }
+
+    /**
+    * 打标结果,JSON
+[{
+label:xxx,
+label_code:xxx,
+desc:xxx
+passr: true/false
+}]
+    */
+    private void setResult(String result){
+    this.result = result;
+    }
+
+    /**
+    * 反馈人ID
+    */
+    private void setFeedbackUserId(String feedbackUserId){
+    this.feedbackUserId = feedbackUserId;
+    }
+
+    /**
+    * 反馈人名字
+    */
+    private void setFeedbackUserNm(String feedbackUserNm){
+    this.feedbackUserNm = feedbackUserNm;
+    }
+
+    /**
+    * 反馈时间
+    */
+    private void setFeedbackTime(Date feedbackTime){
+    this.feedbackTime = feedbackTime;
+    }
+
+    /**
+    * 反馈,agree/reject
+    */
+    private void setFeedback(String feedback){
+    this.feedback = feedback;
+    }
+
+    /**
+    * JSON
+[
+{
+ label:xxx,
+ label_code:xxx,
+ desc:xxx
+ passr: true/false
+}
+]
+    */
+    private void setFeedbackResult(String feedbackResult){
+    this.feedbackResult = feedbackResult;
+    }
+
+    /**
+    * 0:打标执行中;1:打标完成; 2:客户经理已经确认
+    */
+    private void setState(Integer state){
+    this.state = state;
+    }
+
+
+    /**
+    * 
+    */
+    private String getId(){
+    return this.id;
+    }
+
+    /**
+    * 应用ID
+    */
+    private String getAppId(){
+    return this.appId;
+    }
+
+    /**
+    * 调用时间
+    */
+    private Date getInsertTime(){
+    return this.insertTime;
+    }
+
+    /**
+    * 业务属性,贷款编号
+    */
+    private String getBusinessAttr(){
+    return this.businessAttr;
+    }
+
+    /**
+    * tab_tag_category的主键
+    */
+    private String getCategory(){
+    return this.category;
+    }
+
+    /**
+    * 输入短语
+    */
+    private String getPhrase(){
+    return this.phrase;
+    }
+
+    /**
+    * 附件名称
+    */
+    private String getAttachment(){
+    return this.attachment;
+    }
+
+    /**
+    * 附件路径
+    */
+    private String getAttachmentUrl(){
+    return this.attachmentUrl;
+    }
+
+    /**
+    * 打标结果,JSON
+[{
+label:xxx,
+label_code:xxx,
+desc:xxx
+passr: true/false
+}]
+    */
+    private String getResult(){
+    return this.result;
+    }
+
+    /**
+    * 反馈人ID
+    */
+    private String getFeedbackUserId(){
+    return this.feedbackUserId;
+    }
+
+    /**
+    * 反馈人名字
+    */
+    private String getFeedbackUserNm(){
+    return this.feedbackUserNm;
+    }
+
+    /**
+    * 反馈时间
+    */
+    private Date getFeedbackTime(){
+    return this.feedbackTime;
+    }
+
+    /**
+    * 反馈,agree/reject
+    */
+    private String getFeedback(){
+    return this.feedback;
+    }
+
+    /**
+    * JSON
+[
+{
+ label:xxx,
+ label_code:xxx,
+ desc:xxx
+ passr: true/false
+}
+]
+    */
+    private String getFeedbackResult(){
+    return this.feedbackResult;
+    }
+
+    /**
+    * 0:打标执行中;1:打标完成; 2:客户经理已经确认
+    */
+    private Integer getState(){
+    return this.state;
+    }
+
+}

+ 33 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/AitagTagCategoryVO.java

@@ -0,0 +1,33 @@
+package cn.com.yusys.yusp.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("标签体系视图对象")
+public class AitagTagCategoryVO {
+    @ApiModelProperty("标签体系ID")
+    private String id;
+
+    @ApiModelProperty("类别代码")
+    private String categoryCode;
+
+    @ApiModelProperty("类别名称")
+    private String categoryNm;
+
+    @ApiModelProperty("类别描述")
+    private String categoryDesc;
+
+    @ApiModelProperty("生效级别")
+    private Integer visibilityLevel;
+
+    @ApiModelProperty("状态:0启用;1停用")
+    private Integer state;
+
+    @ApiModelProperty("删除标记:0未删除;1删除")
+    private Integer isDelete;
+
+    @ApiModelProperty("标签数量(临时字段)")
+    private Integer tagNum;
+}

+ 44 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagAppMapper.java

@@ -0,0 +1,44 @@
+package cn.com.yusys.yusp.mapper;
+
+import cn.com.yusys.yusp.domain.entity.AitagApp;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface AitagAppMapper {
+
+    // 分页查询(偏移量 + 限制)
+    List<AitagApp> selectPageApps(@Param("offset") int offset, @Param("limit") int limit);
+
+    List<AitagApp> selectByAppNameLike(@Param("appName") String appName);
+
+    // 按应用名称查重
+    int selectCountByName(@Param("appName") String appName);
+
+    // 新增
+    void insertApp(AitagApp app);
+
+    // 按 ID 查询
+    AitagApp selectById(@Param("id") String id);
+
+    // 按应用名称查询(仅三字段)
+    List<AitagApp> selectByName(@Param("appName") String appName);
+
+    // 重置密钥
+    void updateAppSecret(@Param("id") String id, @Param("appSecret") String appSecret);
+
+    // 禁用应用
+    void disableApp(@Param("id") String id);
+
+    // 全表计数(分页总数)
+    long selectCountAll();
+
+    // 分页模糊查询
+    List<AitagApp> selectByAppNameLikeWithPagination(@Param("appName") String appName, @Param("offset") int offset, @Param("limit") int limit);
+
+    // 模糊查询总数
+    long selectCountByAppNameLike(@Param("appName") String appName);
+
+}

+ 41 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagCategoryMapper.java

@@ -0,0 +1,41 @@
+package cn.com.yusys.yusp.mapper;
+
+import cn.com.yusys.yusp.domain.entity.AitagTagCategory;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface AitagTagCategoryMapper {
+
+    // 分页查询(偏移量 + 限制)
+    List<AitagTagCategory> selectPageCategories(@Param("offset") int offset, @Param("limit") int limit);
+
+    // 模糊查询(按名称)
+    List<AitagTagCategory> selectByCategoryNmLike(@Param("categoryNm") String categoryNm);
+
+    // 按名称查重(未删除)
+    int selectCountByNameAndNotDeleted(@Param("categoryNm") String categoryNm);
+
+    // 新增
+    void insertCategory(AitagTagCategory category);
+
+    // 按 ID 查询
+    AitagTagCategory selectById(@Param("id") String id);
+
+    // 更新
+    void updateCategory(AitagTagCategory category);
+
+    // 启用/停用
+    void updateState(@Param("id") String id, @Param("state") Integer state);
+
+    // 删除(逻辑删除)
+    void deleteCategory(@Param("id") String id);
+
+    // 计算标签数量(关联 aitag_tag_info 表)
+    int countTagsByCategoryId(@Param("categoryId") String categoryId);
+
+    // 全表计数(分页总数)
+    long selectCountAll();
+}

+ 80 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/model/Result.java

@@ -0,0 +1,80 @@
+package cn.com.yusys.yusp.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class Result<T> {
+    private String code;
+    private Long total; // 分页时使用的总数
+    private String message;
+    private String level; // 日志级别(如 info/warn/error)
+    private T data;
+
+    // 成功响应(无数据)
+    public static <T> Result<T> success() {
+        Result<T> result = new Result<>();
+        result.setCode("0");
+        result.setMessage(null);
+        result.setLevel(null);
+        result.setTotal(0L);
+        return result;
+    }
+
+    // 成功响应(带数据)
+    public static <T> Result<T> success(T data) {
+        Result<T> result = new Result<>();
+        result.setCode("0");
+        result.setMessage(null);
+        result.setLevel(null);
+        result.setTotal(0L);
+        result.setData(data);
+        return result;
+    }
+
+    // 成功响应(带数据和总数,用于分页)
+    public static <T> Result<T> success(T data, Long total) {
+        Result<T> result = new Result<>();
+        result.setCode("0");
+        result.setMessage(null);
+        result.setLevel(null);
+        result.setTotal(total);
+        result.setData(data);
+        return result;
+    }
+
+    // 错误响应
+    public static <T> Result<T> error(String code, String message) {
+        Result<T> result = new Result<>();
+        result.setCode(code);
+        result.setMessage(message);
+        result.setLevel("error");
+        result.setTotal(0L);
+        return result;
+    }
+
+    // 警告响应
+    public static <T> Result<T> warn(String message) {
+        Result<T> result = new Result<>();
+        result.setCode("0");
+        result.setMessage(message);
+        result.setLevel("warn");
+        result.setTotal(0L);
+        return result;
+    }
+
+    // 分页成功响应(只返回 records 和 total)
+    public static <T> Result<List<T>> pageSuccess(List<T> records, Long total) {
+        Result<List<T>> result = new Result<>();
+        result.setCode("0");
+        result.setMessage(null);
+        result.setLevel(null);
+        result.setTotal(total);
+        result.setData(records); // 只设置 records 部分
+        return result;
+    }
+
+
+}
+   

+ 18 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagAppService.java

@@ -0,0 +1,18 @@
+package cn.com.yusys.yusp.service;
+
+import cn.com.yusys.yusp.domain.entity.AitagApp;
+import cn.com.yusys.yusp.domain.dto.AitagAppCreateDTO;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import java.util.List;
+
+public interface AitagAppService {
+    Page<AitagApp> listApps(int page, int size);
+    Page<AitagApp> queryByAppNameLikeWithPagination(String appName, int page, int pageSize);
+
+    AitagApp addApp(AitagAppCreateDTO dto);
+    List<AitagApp> queryByName(String appName);
+    List<AitagApp> queryByAppNameLike(String appName);
+    String resetSecret(String id);
+    void disableApp(String id);
+}

+ 19 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagCategoryService.java

@@ -0,0 +1,19 @@
+package cn.com.yusys.yusp.service;
+
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryCreateDTO;
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryUpdateDTO;
+import cn.com.yusys.yusp.domain.entity.AitagTagCategory;
+import cn.com.yusys.yusp.domain.vo.AitagTagCategoryVO;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import java.util.List;
+
+public interface AitagTagCategoryService {
+    Page<AitagTagCategoryVO> listCategories(int page, int size); // ← 改为 VO
+    AitagTagCategory createCategory(AitagTagCategoryCreateDTO dto);
+    AitagTagCategory updateCategory(AitagTagCategoryUpdateDTO dto);
+    List<AitagTagCategory> searchByCategoryNm(String categoryNm);
+    void enableCategory(String id);
+    void disableCategory(String id);
+    void deleteCategory(String id);
+}

+ 120 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagAppServiceImpl.java

@@ -0,0 +1,120 @@
+package cn.com.yusys.yusp.service.impl;
+
+import cn.com.yusys.yusp.domain.dto.AitagAppCreateDTO;
+import cn.com.yusys.yusp.domain.entity.AitagApp;
+import cn.com.yusys.yusp.mapper.AitagAppMapper;
+import cn.com.yusys.yusp.service.AitagAppService;
+import cn.com.yusys.yusp.util.DataUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Random;
+
+@Service
+public class AitagAppServiceImpl implements AitagAppService {
+
+    @Autowired
+    private AitagAppMapper aiTagAppMapper;
+
+    @Override
+    public Page<AitagApp> listApps(int page, int size) {
+        int offset = (page - 1) * size;
+        List<AitagApp> records = aiTagAppMapper.selectPageApps(offset, size);
+        long total = aiTagAppMapper.selectCountAll();
+        Page<AitagApp> pageObj = new Page<>(page, size, total);
+        pageObj.setRecords(records);
+        return pageObj;
+    }
+
+    @Override
+    public AitagApp addApp(AitagAppCreateDTO dto) {
+        String appName = dto.getAppName();
+
+        // 校验唯一性
+        if (aiTagAppMapper.selectCountByName(appName) > 0) {
+            throw new RuntimeException("应用名称已存在");
+        }
+
+        String id = java.util.UUID.randomUUID().toString();
+        String appId = "YY" +
+                new SimpleDateFormat("yyyyMMdd").format(new Date()) +
+                "001" +
+                String.format("%06d", new Random().nextInt(1000000));
+        String appSecret = generateRandomString(25);
+
+        AitagApp app = new AitagApp();
+        app.setId(id);
+        app.setAppName(appName);
+        app.setAppId(appId);
+        app.setAppSecret(appSecret);
+        // 使用 DataUtil 截断时间精度
+        app.setCreateTime(DataUtil.truncateToSeconds(new Date()));
+        app.setState("0");
+
+        aiTagAppMapper.insertApp(app);
+        return app;
+    }
+
+    @Override
+    public List<AitagApp> queryByName(String appName) {
+        return aiTagAppMapper.selectByName(appName);
+    }
+
+    @Override
+    public List<AitagApp> queryByAppNameLike(String appName) {
+        return aiTagAppMapper.selectByAppNameLike(appName); // 调用 Mapper 层的模糊查询方法
+    }
+
+    @Override
+    public Page<AitagApp> queryByAppNameLikeWithPagination(String appName, int page, int pageSize) {
+        int offset = (page - 1) * pageSize;
+
+        // 查询符合条件的应用列表
+        List<AitagApp> records = aiTagAppMapper.selectByAppNameLikeWithPagination(appName, offset, pageSize);
+
+        // 查询符合条件的总记录数
+        long total = aiTagAppMapper.selectCountByAppNameLike(appName);
+
+        // 构造分页对象
+        Page<AitagApp> pageObj = new Page<>(page, pageSize, total);
+        pageObj.setRecords(records);
+
+        return pageObj;
+    }
+
+
+    @Override
+    public String resetSecret(String id) {
+        AitagApp app = aiTagAppMapper.selectById(id);
+        if (app == null) {
+            throw new RuntimeException("应用不存在");
+        }
+
+        String newSecret = generateRandomString(25);
+        aiTagAppMapper.updateAppSecret(id, newSecret);
+        return newSecret;
+    }
+
+    @Override
+    public void disableApp(String id) {
+        AitagApp app = aiTagAppMapper.selectById(id);
+        if (app == null) {
+            throw new RuntimeException("应用不存在");
+        }
+        aiTagAppMapper.disableApp(id);
+    }
+
+    private String generateRandomString(int length) {
+        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        StringBuilder sb = new StringBuilder();
+        Random random = new Random();
+        for (int i = 0; i < length; i++) {
+            sb.append(chars.charAt(random.nextInt(chars.length())));
+        }
+        return sb.toString();
+    }
+}

+ 136 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagCategoryServiceImpl.java

@@ -0,0 +1,136 @@
+package cn.com.yusys.yusp.service.impl;
+
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryCreateDTO;
+import cn.com.yusys.yusp.domain.dto.AitagTagCategoryUpdateDTO;
+import cn.com.yusys.yusp.domain.entity.AitagTagCategory;
+import cn.com.yusys.yusp.domain.vo.AitagTagCategoryVO;
+import cn.com.yusys.yusp.mapper.AitagTagCategoryMapper;
+import cn.com.yusys.yusp.service.AitagTagCategoryService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@Service
+public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
+
+    @Autowired
+    private AitagTagCategoryMapper aiTagCategoryMapper;
+
+    @Override
+    public Page<AitagTagCategoryVO> listCategories(int page, int size) {
+        int offset = (page - 1) * size;
+        List<AitagTagCategory> records = aiTagCategoryMapper.selectPageCategories(offset, size);
+        long total = aiTagCategoryMapper.selectCountAll();
+
+        // 转换为 VO 并动态计算 tagNum
+        List<AitagTagCategoryVO> voList = records.stream().map(category -> {
+            AitagTagCategoryVO vo = new AitagTagCategoryVO();
+            vo.setId(category.getId());
+            vo.setCategoryCode(category.getCategoryCode());
+            vo.setCategoryNm(category.getCategoryNm());
+            vo.setCategoryDesc(category.getCategoryDesc());
+            vo.setVisibilityLevel(category.getVisibilityLevel());
+            vo.setState(category.getState());
+            vo.setIsDelete(category.getIsDelete());
+
+            // 动态计算 tagNum
+            int tagNum = aiTagCategoryMapper.countTagsByCategoryId(category.getId());
+            vo.setTagNum(tagNum);
+
+            return vo;
+        }).collect(Collectors.toList());
+
+        Page<AitagTagCategoryVO> pageObj = new Page<>(page, size, total);
+        pageObj.setRecords(voList);
+        return pageObj;
+    }
+
+
+    @Override
+    public AitagTagCategory createCategory(AitagTagCategoryCreateDTO dto) {
+        // 校验唯一性
+        if (aiTagCategoryMapper.selectCountByNameAndNotDeleted(dto.getCategoryNm()) > 0) {
+            throw new RuntimeException("标签体系名称已存在");
+        }
+
+        AitagTagCategory category = new AitagTagCategory();
+        category.setId(UUID.randomUUID().toString());
+        category.setCategoryNm(dto.getCategoryNm());
+        category.setCategoryDesc(dto.getCategoryDesc());
+        category.setState(0); // 默认启用
+        category.setIsDelete(0); // 未删除
+
+        aiTagCategoryMapper.insertCategory(category);
+        return category;
+    }
+
+    @Override
+    public AitagTagCategory updateCategory(AitagTagCategoryUpdateDTO dto) {
+        AitagTagCategory existing = aiTagCategoryMapper.selectById(dto.getId());
+        if (existing == null) {
+            throw new RuntimeException("标签体系不存在");
+        }
+
+        // 校验唯一性(排除自己)
+        if (!existing.getCategoryNm().equals(dto.getCategoryNm())) {
+            if (aiTagCategoryMapper.selectCountByNameAndNotDeleted(dto.getCategoryNm()) > 0) {
+                throw new RuntimeException("标签体系名称已存在");
+            }
+        }
+
+        existing.setCategoryNm(dto.getCategoryNm());
+        existing.setCategoryDesc(dto.getCategoryDesc());
+
+        aiTagCategoryMapper.updateCategory(existing);
+        return existing;
+    }
+
+    @Override
+    public List<AitagTagCategory> searchByCategoryNm(String categoryNm) {
+        return aiTagCategoryMapper.selectByCategoryNmLike(categoryNm);
+    }
+
+    @Override
+    public void enableCategory(String id) {
+        AitagTagCategory category = aiTagCategoryMapper.selectById(id);
+        if (category == null) {
+            throw new RuntimeException("标签体系不存在");
+        }
+        if (category.getState() == 0) {
+            throw new RuntimeException("标签体系已经是启用状态");
+        }
+        aiTagCategoryMapper.updateState(id, 0);
+    }
+
+    @Override
+    public void disableCategory(String id) {
+        AitagTagCategory category = aiTagCategoryMapper.selectById(id);
+        if (category == null) {
+            throw new RuntimeException("标签体系不存在");
+        }
+        if (category.getState() == 1) {
+            throw new RuntimeException("标签体系已经是停用状态");
+        }
+        aiTagCategoryMapper.updateState(id, 1);
+    }
+
+    @Override
+    public void deleteCategory(String id) {
+        AitagTagCategory category = aiTagCategoryMapper.selectById(id);
+        if (category == null) {
+            throw new RuntimeException("标签体系不存在");
+        }
+
+        // 判断标签数量是否为0
+        int tagCount = aiTagCategoryMapper.countTagsByCategoryId(id);
+        if (tagCount > 0) {
+            throw new RuntimeException("该标签体系下仍有标签,无法删除");
+        }
+
+        aiTagCategoryMapper.deleteCategory(id);
+    }
+}

+ 63 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/util/DataUtil.java

@@ -0,0 +1,63 @@
+package cn.com.yusys.yusp.util;
+
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class DataUtil {
+
+
+    public static  String getDecimalFormatStr(String num) {
+        StringBuilder sb = new StringBuilder("");
+        Pattern pattern = Pattern.compile("[0-9]*");
+        if (num != null && !"".equals(num)) {
+            if (pattern.matcher(num).matches()) { //如果输入的是一个正整数
+                int n = Integer.valueOf(num);
+                if (n < 1) {
+                    return sb.toString();
+                }
+                sb.append("0.");
+                for (int i = 0; i < n; i++) {
+                    sb.append("0");
+                }
+            }
+        } else {//对于整型,num就是空字符串
+            sb.append("#");
+        }
+        return sb.toString();
+    }
+
+    public static String listToStringForSqlInClause(List<String> list) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("(");
+        for (int i = 0; i < list.size(); i++) {
+            sb.append("'").append(list.get(i)).append("'");
+            if (i < list.size() - 1) {
+                sb.append(",");
+            }
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public static String DateToString(Date date) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return sdf.format(date);
+    }
+
+    /**
+     * 将 Date 时间截断到秒级别(去除毫秒)
+     * @param date 原始时间
+     * @return 截断后的时间
+     */
+    public static Date truncateToSeconds(Date date) {
+        if (date == null) {
+            return null;
+        }
+        long timeInMillis = date.getTime();
+        long timeInSeconds = (timeInMillis / 1000) * 1000; // 截断毫秒
+        return new Date(timeInSeconds);
+    }
+}

BIN
server/yusp-tagging-core/src/main/resources/cert/pwd_private.key


BIN
server/yusp-tagging-core/src/main/resources/cert/pwd_public.key


+ 121 - 0
server/yusp-tagging-core/src/main/resources/mapper/AitagAppMapper.xml

@@ -0,0 +1,121 @@
+<?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="cn.com.yusys.yusp.mapper.AitagAppMapper">
+
+    <!-- 1. 分页查询 -->
+    <select id="selectPageApps" parameterType="map" resultType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        SELECT
+            id,
+            app_name,
+            app_id,
+            app_secret,
+            create_time,
+            creater_nm,
+            creater_id,
+            state
+        FROM aitag_app
+        ORDER BY create_time DESC
+            LIMIT #{limit} OFFSET #{offset}
+    </select>
+
+    <!-- 2. 按应用名称查重(用于新增前校验) -->
+    <select id="selectCountByName" parameterType="string" resultType="java.lang.Integer">
+        SELECT COUNT(*)
+        FROM aitag_app
+        WHERE app_name = #{appName}
+    </select>
+
+    <!-- 3. 新增应用(插入全部字段) -->
+    <insert id="insertApp" parameterType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        INSERT INTO aitag_app (
+            id,
+            app_name,
+            app_id,
+            app_secret,
+            create_time,
+            creater_nm,
+            creater_id,
+            state
+        ) VALUES (
+                     #{id},
+                     #{appName},
+                     #{appId},
+                     #{appSecret},
+                     #{createTime, jdbcType=TIMESTAMP},
+                     #{createrNm},
+                     #{createrId},
+                     #{state}
+                 )
+    </insert>
+
+    <!-- 4. 根据 ID 查询完整对象(用于 reset/disable) -->
+    <select id="selectById" parameterType="string" resultType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        SELECT *
+        FROM aitag_app
+        WHERE id = #{id}
+    </select>
+
+    <!-- 5. 按应用名称查询(返回 app_name, app_id, app_secret) -->
+    <select id="selectByName" parameterType="string" resultType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        SELECT app_name, app_id, app_secret
+        FROM aitag_app
+        WHERE app_name = #{appName}
+    </select>
+
+    <!-- 6. 重置密钥:仅更新 app_secret -->
+    <update id="updateAppSecret" parameterType="map">
+        UPDATE aitag_app
+        SET app_secret = #{appSecret}
+        WHERE id = #{id}
+    </update>
+
+    <!-- 7. 禁用应用:更新 state = '1' -->
+    <update id="disableApp" parameterType="string">
+        UPDATE aitag_app
+        SET state = '1'
+        WHERE id = #{id}
+    </update>
+
+    <!-- 8. 全表计数(用于分页总数) -->
+    <select id="selectCountAll" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM aitag_app
+    </select>
+<!--    9.模糊查询-->
+    <select id="selectByAppNameLike" parameterType="string" resultType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        SELECT
+            id,
+            app_name,
+            app_id,
+            app_secret,
+            create_time,
+            creater_nm,
+            creater_id,
+            state
+        FROM aitag_app
+        WHERE app_name LIKE CONCAT('%', #{appName}, '%')
+    </select>
+    <!--    10.模糊查询计数-->
+    <select id="selectByAppNameLikeWithPagination" parameterType="map" resultType="cn.com.yusys.yusp.domain.entity.AitagApp">
+        SELECT
+            id,
+            app_name,
+            app_id,
+            app_secret,
+            create_time,
+            creater_nm,
+            creater_id,
+            state
+        FROM aitag_app
+        WHERE app_name LIKE CONCAT('%', #{appName}, '%')
+        ORDER BY create_time DESC
+            LIMIT #{limit} OFFSET #{offset}
+    </select>
+    <!--    11.模糊查询总数-->
+    <select id="selectCountByAppNameLike" parameterType="string" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM aitag_app
+        WHERE app_name LIKE CONCAT('%', #{appName}, '%')
+    </select>
+</mapper>

+ 62 - 0
server/yusp-tagging-core/src/main/resources/mapper/AitagTagCategoryMapper.xml

@@ -0,0 +1,62 @@
+<?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="cn.com.yusys.yusp.mapper.AitagTagCategoryMapper">
+
+    <!-- 分页查询 -->
+    <select id="selectPageCategories" resultType="cn.com.yusys.yusp.domain.entity.AitagTagCategory">
+        SELECT * FROM aitag_tag_category
+        WHERE is_delete = 0
+            LIMIT #{offset}, #{limit}
+    </select>
+
+    <!-- 模糊查询 -->
+    <select id="selectByCategoryNmLike" resultType="cn.com.yusys.yusp.domain.entity.AitagTagCategory">
+        SELECT * FROM aitag_tag_category
+        WHERE is_delete = 0 AND category_nm LIKE CONCAT('%', #{categoryNm}, '%')
+            LIMIT 5
+    </select>
+
+    <!-- 按名称查重(未删除) -->
+    <select id="selectCountByNameAndNotDeleted" resultType="int">
+        SELECT COUNT(*) FROM aitag_tag_category
+        WHERE is_delete = 0 AND category_nm = #{categoryNm}
+    </select>
+
+    <!-- 新增 -->
+    <insert id="insertCategory">
+        INSERT INTO aitag_tag_category (id, category_nm, category_desc, state, is_delete)
+        VALUES (#{id}, #{categoryNm}, #{categoryDesc}, 0, 0)
+    </insert>
+
+    <!-- 按 ID 查询 -->
+    <select id="selectById" resultType="cn.com.yusys.yusp.domain.entity.AitagTagCategory">
+        SELECT * FROM aitag_tag_category WHERE id = #{id}
+    </select>
+
+    <!-- 更新 -->
+    <update id="updateCategory">
+        UPDATE aitag_tag_category
+        SET category_nm = #{categoryNm}, category_desc = #{categoryDesc}
+        WHERE id = #{id}
+    </update>
+
+    <!-- 启用/停用 -->
+    <update id="updateState">
+        UPDATE aitag_tag_category SET state = #{state} WHERE id = #{id}
+    </update>
+
+    <!-- 删除(逻辑删除) -->
+    <update id="deleteCategory">
+        UPDATE aitag_tag_category SET is_delete = 1 WHERE id = #{id}
+    </update>
+
+    <!-- 计算标签数量 -->
+    <select id="countTagsByCategoryId" resultType="int">
+        SELECT COUNT(*) FROM aitag_tag_info WHERE category_id = #{categoryId} AND is_delete = 0
+    </select>
+
+    <!-- 全表计数 -->
+    <select id="selectCountAll" resultType="long">
+        SELECT COUNT(*) FROM aitag_tag_category WHERE is_delete = 0
+    </select>
+</mapper>

+ 99 - 0
server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg.properties

@@ -0,0 +1,99 @@
+SUCCESS=\u64CD\u4F5C\u6210\u529F
+RESTART_TASK_COMPLETION=\u5B58\u5728\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\uFF0C\u8BF7\u7B49\u5F85\u8FD0\u884C\u7ED3\u675F\u540E\u91CD\u65B0\u542F\u52A8
+MISSING_OR_EMPTY_PARAMS=\u8BF7\u6C42\u53C2\u6570\u7F3A\u5931\u6216\u4E3A\u7A7A
+MODULE_CODE_REQUIRED=\u6A21\u5757\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+CODE_ITEMS_REQUIRED=\u7801\u503C\u4EE3\u7801\u9879\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+REPORT_ID_REQUIRED=\u53C2\u6570\u62A5\u8868ID\u4E0D\u5141\u8BB8\u4E3A\u7A7A
+DELETE_FAILED_EXCEPTION=\u5220\u9664\u5931\u8D25,\u5F02\u5E38\u4FE1\u606F\u4E3A:{0}
+TASK_RUNNING_CANNOT_DELETE=\u5220\u9664\u5931\u8D25,\u6240\u9009\u4EFB\u52A1\u4ECD\u5728\u5F15\u64CE\u961F\u5217\u8FD0\u884C\u4E2D,\u8BF7\u7A0D\u540E\u91CD\u8BD5
+OPERATION_FAILED=\u7CFB\u7EDF\u5F02\u5E38
+OPERATION_FAILED_PARAM=\u7CFB\u7EDF\u5F02\u5E38:{0}
+UNLOCKED_RULES_EXIST=\u5B58\u5728\u672A\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u5220\u9664\u5931\u8D25
+UNLOCKED_RULES_EXIST_CANNOT_IMPORT=\u5B58\u5728\u672A\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u65E0\u6CD5\u5BFC\u5165
+QUEUED_TASK_REQUIRED=\u53EA\u80FD\u9009\u62E9\u6392\u961F\u4E2D\u7684\u4EFB\u52A1
+TASK_CONFIG_NOT_EXIST=\u5F53\u524D\u4EFB\u52A1\u914D\u7F6E\u4E0D\u5B58\u5728
+MANUAL_DISPATCH_NOT_ALLOWED=\u5F53\u524D\u4EFB\u52A1\u4E0D\u80FD\u624B\u52A8\u4E0B\u53D1
+DEADLINE_CANNOT_BE_PAST=\u4EFB\u52A1\u622A\u6B62\u65F6\u95F4\u4E0D\u80FD\u5C0F\u4E8E\u5F53\u524D\u65E5\u671F
+SELECT_TASK_TO_START=\u8BF7\u9009\u62E9\u8981\u5F00\u542F\u7684\u4EFB\u52A1
+NO_SUBTASKS_EXIST=\u5F53\u524D\u6240\u9009\u4EFB\u52A1\uFF0C\u4E0D\u5B58\u5728\u5B50\u4EFB\u52A1
+SELECT_TASK_TO_RECOVER=\u8BF7\u9009\u62E9\u5F85\u5F00\u542F\u6216\u586B\u62A5\u4E2D\u7684\u4EFB\u52A1\u8FDB\u884C\u56DE\u6536
+FORCE_COMPLETE_NOT_ALLOWED=\u5F53\u524D\u72B6\u6001\u4E0D\u5141\u8BB8\u5F3A\u5236\u5B8C\u6210
+NO_REISSUE_TASK=\u5F53\u524D\u4E0D\u5B58\u5728\u53EF\u8865\u53D1\u4EFB\u52A1\uFF0C\u60A8\u53EF\u901A\u8FC7\u4E0B\u53D1\u6765\u53D1\u8D77\u4EFB\u52A1
+NO_DISABLED_SUBTASKS=\u5F53\u524D\u6240\u9009\u4EFB\u52A1\uFF0C\u4E0D\u5B58\u5728\u672A\u5F00\u542F\u7684\u5B50\u4EFB\u52A1
+FORCE_UNLOCK_COMPLETED_TASK_ONLY=\u53EA\u80FD\u5F3A\u5236\u89E3\u9501\u5DF2\u5B8C\u6210\u7684\u4EFB\u52A1
+NO_NEW_OR_UNLOCKED_RULES_IN_EXCEL=\u5F53\u524D\u5BFC\u5165\u7684excel\u4E2D\u65E0\u65B0\u589E\u548C\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u5BFC\u5165\u4E3A\u7A7A
+REQUESTER_DEVELOPER_TESTER_INFO_REQUIRED=\u63D0\u51FA\u4EBA\u3001\u63D0\u51FA\u65F6\u95F4\u3001\u5F00\u53D1\u4EBA\u3001\u5F00\u53D1\u65F6\u95F4\u3001\u6D4B\u8BD5\u4EBA\u3001\u6D4B\u8BD5\u65F6\u95F4\u5747\u4E0D\u53EF\u4E3A\u7A7A
+NO_CHANGES_IN_IMPORTED_RULES=\u5F53\u524D\u5BFC\u5165\u7684excel\u4E2D\u7684\u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u65E0\u9700\u5BFC\u5165
+UPLOAD_FILE_CANNOT_BE_EMPTY=\u4E0A\u4F20\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u586B\u5199\u540E\u518D\u4E0A\u4F20
+IMMEDIATE_RUN_TASK_EXCEPTION=\u7ACB\u5373\u8FD0\u884C\u4E00\u4E2A\u4EFB\u52A1\u65F6\u5F02\u5E38
+USER_NO_ORG_OR_LINE_PERMISSION=\u5F53\u524D\u7528\u6237\u65E0\u673A\u6784\u6216\u8005\u6761\u7EBF\u7684\u6743\u9650
+DATA_DICT_REQUIRED=\u8BF7\u914D\u7F6E\u6570\u636E\u5B57\u5178
+SINGLE_CHECK_FAILED=\u5355\u6761\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F15\u64CE\u72B6\u6001
+PENDING_TASK_EXISTS=\u6240\u9009\u4EFB\u52A1\u5B58\u5728\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\uFF0C\u8BF7\u7A0D\u540E\u542F\u52A8
+DISABLED_DATA_OPERATION_ONLY=\u53EA\u80FD\u64CD\u4F5C\u505C\u7528\u7684\u6570\u636E
+DICTIONARY_ITEM_REQUIRED=\u5B57\u5178\u9879\u4E0D\u80FD\u4E3A\u7A7A
+DICTIONARY_CODE_EXISTS=\u5B57\u5178\u4EE3\u7801:{0}\u5DF2\u5B58\u5728
+DICTIONARY_ITEM_DUPLICATE_NOT_ALLOWED=\u5B57\u5178\u9879\u4E0D\u80FD\u91CD\u590D
+LOGIN_TIMEOUT=\u767B\u5F55\u8D85\u65F6
+FILE_GEN_DIR_CREATE_FAIL=\u6587\u4EF6\u751F\u6210\u76EE\u5F55\u521B\u5EFA\u5931\u8D25
+EXPORT_FAIL=\u5BFC\u51FA\u5931\u8D25
+TEMPLATE_FILE_NOT_EXIST=\u6A21\u7248\u6587\u4EF6\u4E0D\u5B58\u5728
+RULE_ID=\u89C4\u5219ID
+CHK_TABLE=\u68C0\u6838\u8868\u540D
+CHK_FIELD=\u68C0\u6838\u5B57\u6BB5\u540D\u79F0
+RULE_NAME=\u89C4\u5219\u540D\u79F0
+FULL_CHK=\u5168\u8868\u6027\u6821\u9A8C\u89C4\u5219\u7C7B\u578B
+RULE_EXPR=\u89C4\u5219\u8868\u8FBE\u5F0F
+EXPR_TYPE=\u8868\u8FBE\u5F0F\u7C7B\u578B
+IS_ENABLE=\u662F\u5426\u542F\u7528
+FORCE_SUB=\u662F\u5426\u80FD\u5F3A\u5236\u63D0\u4EA4
+RULE_DESC=\u89C4\u5219\u53D1\u6587\u63CF\u8FF0
+STRONG_CHK=\u662F\u5426\u5F3A\u6821\u9A8C
+THRESHOLD=\u9608\u503C
+WEIGHT=\u6743\u91CD
+REMARK=\u5907\u6CE8
+ONLY_CMT=\u662F\u5426\u53EA\u5907\u6CE8
+PROPOSER=\u63D0\u51FA\u4EBA
+PROP_TIME=\u63D0\u51FA\u65F6\u95F4
+DEVELOPER=\u5F00\u53D1\u4EBA
+DEV_TIME=\u5F00\u53D1\u65F6\u95F4
+TESTER=\u6D4B\u8BD5\u4EBA
+TEST_TIME=\u6D4B\u8BD5\u65F6\u95F4
+RULE_MAINT=\u89C4\u5219\u7EF4\u62A4
+RULE_IMP_TMPL=\u89C4\u5219\u7EF4\u62A4\u5BFC\u5165\u6A21\u677F
+CODE_MAINT=\u7801\u503C\u7EF4\u62A4
+CODE_TYPE=\u4EE3\u7801\u7C7B\u578B
+CODE_TYPE_NAME=\u4EE3\u7801\u7C7B\u578B\u540D\u79F0
+CODE_ITEM=\u4EE3\u7801\u9879
+CODE_ITEM_NAME=\u4EE3\u7801\u9879\u540D\u79F0
+PARENT_ITEM=\u4E0A\u7EA7\u4EE3\u7801\u9879
+CODE_LEVEL=\u4EE3\u7801\u7EA7\u522B
+ITEM_SEQ=\u4EE3\u7801\u9879\u5E8F\u53F7
+IS_BOTTOM=\u662F\u5426\u6700\u5E95\u5C42\u4EE3\u7801
+RULE_MOD_REC=\u89C4\u5219\u4FEE\u6539\u8BB0\u5F55
+OPER_TYPE=\u64CD\u4F5C\u7C7B\u578B
+REPORT_ID=\u76D1\u7BA1ID
+UPDATE_FIELD=\u4FEE\u6539\u5B57\u6BB5
+OLD_VALUE=\u4FEE\u6539\u524D
+NEW_VALUE=\u4FEE\u6539\u540E
+CODE_IMP_TEMPLATE=\u7801\u503C\u5BFC\u5165\u6A21\u677F
+REPORT_GROUP_CODE_EXISTS=\u586B\u62A5\u7EC4\u4EE3\u7801\u5DF2\u5B58\u5728\uFF0C\u8BF7\u4FEE\u6539\u540E\u518D\u64CD\u4F5C
+CRON_EXPRESSION_INVALID=cron\u8868\u8FBE\u5F0F\u65E0\u6548,\u65E0\u6CD5\u65B0\u589E
+TRIGGER_NAME_USED=\u89E6\u53D1\u5668\u540D\u79F0\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u65B0\u589E
+CRON_EXPRESSION_INVALID_EDIT=cron\u8868\u8FBE\u5F0F\u65E0\u6548, \u65E0\u6CD5\u7F16\u8F91
+CRON_EXPRESSION_INVALID_MINUTE_UNIT=cron\u8868\u8FBE\u5F0F\u65E0\u6548, \u8BF7\u786E\u4FDD\u6700\u5C0F\u5355\u4F4D\u6309\u5929
+TRIGGER_ALREADY_USED_CANNOT_EDIT=\u89E6\u53D1\u5668\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u4FEE\u6539
+TRIGGER_NAME_USED_CANNOT_EDIT=\u89E6\u53D1\u5668\u540D\u79F0\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u4FEE\u6539
+ASSOCIATED_TASK_EXISTS_CANNOT_DISABLE=\u5B58\u5728\u5DF2\u5173\u8054\u4EFB\u52A1\uFF0C\u65E0\u6CD5\u505C\u7528
+TRIGGER_IN_USE_CANNOT_DELETE=\u5B58\u5728\u6B63\u5728\u4F7F\u7528\u7684\u89E6\u53D1\u5668\uFF0C\u65E0\u6CD5\u5220\u9664
+PLEASE_SELECT_TRIGGER=\u8BF7\u9009\u62E9\u89E6\u53D1\u5668\uFF01
+TRIGGER_ALREADY_USED_CANNOT_DELETE=\u89E6\u53D1\u5668\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+FIELD_ENGLISH_NAME_EXISTS=\u5B57\u6BB5\u82F1\u6587\u540D\u5DF2\u5B58\u5728
+ADD_FAILED_SELECT_DATASOURCE=\u65B0\u589E\u5931\u8D25,\u8BF7\u9009\u62E9\u6570\u636E\u6E90
+TASKS_EXIST_PLEASE_UNLINK_BEFORE_DELETING=\u5B58\u5728\u5DF2\u5173\u8054\u7684\u586B\u62A5\u4EFB\u52A1\uFF0C\u8BF7\u53D6\u6D88\u5173\u8054\u540E\u5220\u9664
+TASK_EXISTS_CANNOT_DISABLE=\u5B58\u5728\u5173\u8054\u7684\u542F\u7528\u72B6\u6001\u586B\u62A5\u4EFB\u52A1\uFF0C\u6682\u4E0D\u5141\u8BB8\u505C\u7528
+ONLY_APPLY_TO_AUDITING_TASKS=\u53EA\u80FD\u7533\u8BF7\u89E3\u9501\u5BA1\u6838\u4E2D\u7684\u4EFB\u52A1
+REQUEST_PARAMETER_CANNOT_BE_EMPTY=\u8BF7\u6C42\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A
+EXTENSION_FIELD_CANNOT_BE_EMPTY=\u6269\u5C55\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A
+SUPPLEMENTAL_TABLE_CONFIG_NOT_EXIST=\u8865\u5F55\u8868\u914D\u7F6E\u4E0D\u5B58\u5728,\u8BF7\u786E\u8BA4\u540E\u518D\u64CD\u4F5C
+DATA_SOURCE_INFO_NOT_EXIST=\u5F53\u524D\u8868\u6570\u636E\u6E90\u4FE1\u606F\u4E0D\u5B58\u5728

+ 99 - 0
server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg_en.properties

@@ -0,0 +1,99 @@
+SUCCESS=SUCCESS
+RESTART_TASK_COMPLETION=Running task exists, please wait until completion and restart
+MISSING_OR_EMPTY_PARAMS=Missing or empty request parameters
+MODULE_CODE_REQUIRED=Module code cannot be empty
+CODE_ITEMS_REQUIRED=Code items list cannot be empty
+REPORT_ID_REQUIRED=Report ID cannot be empty
+DELETE_FAILED_EXCEPTION=Deletion failed, exception message:{0}
+TASK_RUNNING_CANNOT_DELETE=Deletion failed, the selected task is still running in the engine queue. Please try again later.
+OPERATION_FAILED=Operation failed
+OPERATION_FAILED_PARAM=Operation failed:{0}
+UNLOCKED_RULES_EXIST=There are unlocked rules, deletion failed
+UNLOCKED_RULES_EXIST_CANNOT_IMPORT=Unlocked rules exist, import failed
+QUEUED_TASK_REQUIRED=Only queued tasks can be selected
+TASK_CONFIG_NOT_EXIST=Current task configuration does not exist
+MANUAL_DISPATCH_NOT_ALLOWED=Current task cannot be manually dispatched
+DEADLINE_CANNOT_BE_PAST=Task deadline cannot be earlier than the current date
+SELECT_TASK_TO_START=Please select the task to start
+NO_SUBTASKS_EXIST=The selected task has no subtasks
+SELECT_TASK_TO_RECOVER=Please select a task that is pending start or in progress to recover
+FORCE_COMPLETE_NOT_ALLOWED=Current status does not allow force completion
+NO_REISSUE_TASK=No reissue task available. You can initiate a task through dispatch
+NO_DISABLED_SUBTASKS=The selected task has no disabled subtasks
+FORCE_UNLOCK_COMPLETED_TASK_ONLY=Only completed tasks can be force-unlocked
+NO_NEW_OR_UNLOCKED_RULES_IN_EXCEL=No new or unlocked rules in the imported Excel file, the import is empty
+REQUESTER_DEVELOPER_TESTER_INFO_REQUIRED=Requester, request time, developer, development time, tester, and testing time cannot be empty
+NO_CHANGES_IN_IMPORTED_RULES=There are no changes in the rules from the imported Excel file, no need to import
+UPLOAD_FILE_CANNOT_BE_EMPTY=The uploaded file cannot be empty. Please fill in the content before uploading
+IMMEDIATE_RUN_TASK_EXCEPTION=An exception occurred while running a task immediately
+USER_NO_ORG_OR_LINE_PERMISSION=The current user has no organization or line permission
+DATA_DICT_REQUIRED=Please configure the data dictionary
+SINGLE_CHECK_FAILED=Single check failed, please check the engine status
+PENDING_TASK_EXISTS=There is an ongoing task in the selected tasks. Please start later
+DISABLED_DATA_OPERATION_ONLY=Only disabled data can be operated
+DICTIONARY_ITEM_REQUIRED=Dictionary item cannot be empty
+DICTIONARY_CODE_EXISTS=Dictionary code {0} already exists
+DICTIONARY_ITEM_DUPLICATE_NOT_ALLOWED=Dictionary item cannot be duplicated
+LOGIN_TIMEOUT=Login timeout
+FILE_GEN_DIR_CREATE_FAIL=Failed to create the file generation directory
+EXPORT_FAIL=Export failed
+TEMPLATE_FILE_NOT_EXIST=Template file does not exist
+RULE_ID=rule id
+CHK_TABLE=check table name
+CHK_FIELD=check field name
+RULE_NAME=rule name
+FULL_CHK=Full Table Check Rule Type
+RULE_EXPR=Rule Expression
+EXPR_TYPE=Expression Type
+IS_ENABLE=Is Enabled
+FORCE_SUB=Can Force Submit
+RULE_DESC=Rule Description
+STRONG_CHK=Is Strong Check
+THRESHOLD=Threshold
+WEIGHT=Weight
+REMARK=Remark
+ONLY_CMT=Is Only Comment
+PROPOSER=Proposer
+PROP_TIME=Proposal Time
+DEVELOPER=Developer
+DEV_TIME=Development Time
+TESTER=Tester
+TEST_TIME=Test Time
+RULE_MAINT=Rule Maintenance
+RULE_IMP_TMPL=Rule Maintenance Import Template
+CODE_MAINT=Code Value Maintenance
+CODE_TYPE=Code Type
+CODE_TYPE_NAME=Code Type Name
+CODE_ITEM=Code Item
+CODE_ITEM_NAME=Code Item Name
+PARENT_ITEM=Parent Code Item
+CODE_LEVEL=Code Level
+ITEM_SEQ=Item Sequence
+IS_BOTTOM=Is Bottom Level
+RULE_MOD_REC=Rule Modification Record
+OPER_TYPE=Operation Type
+REPORT_ID=Report ID
+UPDATE_FIELD=Update Field
+OLD_VALUE=Old Value
+NEW_VALUE=New Value
+CODE_IMP_TEMPLATE=Code Import Template
+REPORT_GROUP_CODE_EXISTS=Report group code already exists, please modify and try again
+CRON_EXPRESSION_INVALID=CRON expression is invalid, cannot add new
+TRIGGER_NAME_USED=Trigger name already used, cannot add new
+CRON_EXPRESSION_INVALID_EDIT=CRON expression is invalid, cannot edit
+CRON_EXPRESSION_INVALID_MINUTE_UNIT=CRON expression is invalid, please ensure the minimum unit is by day
+TRIGGER_ALREADY_USED_CANNOT_EDIT=Trigger is already in use, cannot be modified
+TRIGGER_NAME_USED_CANNOT_EDIT=Trigger name already used, cannot be modified
+ASSOCIATED_TASK_EXISTS_CANNOT_DISABLE=Associated task exists, cannot disable
+TRIGGER_IN_USE_CANNOT_DELETE=There is a trigger in use, cannot delete
+PLEASE_SELECT_TRIGGER=Please select a trigger!
+TRIGGER_ALREADY_USED_CANNOT_DELETE=Trigger is already in use, cannot delete
+FIELD_ENGLISH_NAME_EXISTS=Field English name already exists
+ADD_FAILED_SELECT_DATASOURCE=Add failed, please select data source
+TASKS_EXIST_PLEASE_UNLINK_BEFORE_DELETING=Associated reporting tasks exist, please unlink before deleting
+TASK_EXISTS_CANNOT_DISABLE=Associated enabled reporting tasks exist, temporarily not allowed to disable
+ONLY_APPLY_TO_AUDITING_TASKS=Can only apply to tasks under audit
+REQUEST_PARAMETER_CANNOT_BE_EMPTY=Request parameter cannot be empty
+EXTENSION_FIELD_CANNOT_BE_EMPTY=Extension field cannot be empty
+SUPPLEMENTAL_TABLE_CONFIG_NOT_EXIST=Supplemental table configuration does not exist, please confirm and operate again
+DATA_SOURCE_INFO_NOT_EXIST=Current table data source information does not exist

+ 99 - 0
server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg_zh.properties

@@ -0,0 +1,99 @@
+SUCCESS=\u64CD\u4F5C\u6210\u529F
+RESTART_TASK_COMPLETION=\u5B58\u5728\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\uFF0C\u8BF7\u7B49\u5F85\u8FD0\u884C\u7ED3\u675F\u540E\u91CD\u65B0\u542F\u52A8
+MISSING_OR_EMPTY_PARAMS=\u8BF7\u6C42\u53C2\u6570\u7F3A\u5931\u6216\u4E3A\u7A7A
+MODULE_CODE_REQUIRED=\u6A21\u5757\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+CODE_ITEMS_REQUIRED=\u7801\u503C\u4EE3\u7801\u9879\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+REPORT_ID_REQUIRED=\u53C2\u6570\u62A5\u8868ID\u4E0D\u5141\u8BB8\u4E3A\u7A7A
+DELETE_FAILED_EXCEPTION=\u5220\u9664\u5931\u8D25,\u5F02\u5E38\u4FE1\u606F\u4E3A:{0}
+TASK_RUNNING_CANNOT_DELETE=\u5220\u9664\u5931\u8D25,\u6240\u9009\u4EFB\u52A1\u4ECD\u5728\u5F15\u64CE\u961F\u5217\u8FD0\u884C\u4E2D,\u8BF7\u7A0D\u540E\u91CD\u8BD5
+OPERATION_FAILED=\u7CFB\u7EDF\u5F02\u5E38
+OPERATION_FAILED_PARAM=\u7CFB\u7EDF\u5F02\u5E38:{0}
+UNLOCKED_RULES_EXIST=\u5B58\u5728\u672A\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u5220\u9664\u5931\u8D25
+UNLOCKED_RULES_EXIST_CANNOT_IMPORT=\u5B58\u5728\u672A\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u65E0\u6CD5\u5BFC\u5165
+QUEUED_TASK_REQUIRED=\u53EA\u80FD\u9009\u62E9\u6392\u961F\u4E2D\u7684\u4EFB\u52A1
+TASK_CONFIG_NOT_EXIST=\u5F53\u524D\u4EFB\u52A1\u914D\u7F6E\u4E0D\u5B58\u5728
+MANUAL_DISPATCH_NOT_ALLOWED=\u5F53\u524D\u4EFB\u52A1\u4E0D\u80FD\u624B\u52A8\u4E0B\u53D1
+DEADLINE_CANNOT_BE_PAST=\u4EFB\u52A1\u622A\u6B62\u65F6\u95F4\u4E0D\u80FD\u5C0F\u4E8E\u5F53\u524D\u65E5\u671F
+SELECT_TASK_TO_START=\u8BF7\u9009\u62E9\u8981\u5F00\u542F\u7684\u4EFB\u52A1
+NO_SUBTASKS_EXIST=\u5F53\u524D\u6240\u9009\u4EFB\u52A1\uFF0C\u4E0D\u5B58\u5728\u5B50\u4EFB\u52A1
+SELECT_TASK_TO_RECOVER=\u8BF7\u9009\u62E9\u5F85\u5F00\u542F\u6216\u586B\u62A5\u4E2D\u7684\u4EFB\u52A1\u8FDB\u884C\u56DE\u6536
+FORCE_COMPLETE_NOT_ALLOWED=\u5F53\u524D\u72B6\u6001\u4E0D\u5141\u8BB8\u5F3A\u5236\u5B8C\u6210
+NO_REISSUE_TASK=\u5F53\u524D\u4E0D\u5B58\u5728\u53EF\u8865\u53D1\u4EFB\u52A1\uFF0C\u60A8\u53EF\u901A\u8FC7\u4E0B\u53D1\u6765\u53D1\u8D77\u4EFB\u52A1
+NO_DISABLED_SUBTASKS=\u5F53\u524D\u6240\u9009\u4EFB\u52A1\uFF0C\u4E0D\u5B58\u5728\u672A\u5F00\u542F\u7684\u5B50\u4EFB\u52A1
+FORCE_UNLOCK_COMPLETED_TASK_ONLY=\u53EA\u80FD\u5F3A\u5236\u89E3\u9501\u5DF2\u5B8C\u6210\u7684\u4EFB\u52A1
+NO_NEW_OR_UNLOCKED_RULES_IN_EXCEL=\u5F53\u524D\u5BFC\u5165\u7684excel\u4E2D\u65E0\u65B0\u589E\u548C\u89E3\u9501\u7684\u89C4\u5219\uFF0C\u5BFC\u5165\u4E3A\u7A7A
+REQUESTER_DEVELOPER_TESTER_INFO_REQUIRED=\u63D0\u51FA\u4EBA\u3001\u63D0\u51FA\u65F6\u95F4\u3001\u5F00\u53D1\u4EBA\u3001\u5F00\u53D1\u65F6\u95F4\u3001\u6D4B\u8BD5\u4EBA\u3001\u6D4B\u8BD5\u65F6\u95F4\u5747\u4E0D\u53EF\u4E3A\u7A7A
+NO_CHANGES_IN_IMPORTED_RULES=\u5F53\u524D\u5BFC\u5165\u7684excel\u4E2D\u7684\u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u65E0\u9700\u5BFC\u5165
+UPLOAD_FILE_CANNOT_BE_EMPTY=\u4E0A\u4F20\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u586B\u5199\u540E\u518D\u4E0A\u4F20
+IMMEDIATE_RUN_TASK_EXCEPTION=\u7ACB\u5373\u8FD0\u884C\u4E00\u4E2A\u4EFB\u52A1\u65F6\u5F02\u5E38
+USER_NO_ORG_OR_LINE_PERMISSION=\u5F53\u524D\u7528\u6237\u65E0\u673A\u6784\u6216\u8005\u6761\u7EBF\u7684\u6743\u9650
+DATA_DICT_REQUIRED=\u8BF7\u914D\u7F6E\u6570\u636E\u5B57\u5178
+SINGLE_CHECK_FAILED=\u5355\u6761\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F15\u64CE\u72B6\u6001
+PENDING_TASK_EXISTS=\u6240\u9009\u4EFB\u52A1\u5B58\u5728\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\uFF0C\u8BF7\u7A0D\u540E\u542F\u52A8
+DISABLED_DATA_OPERATION_ONLY=\u53EA\u80FD\u64CD\u4F5C\u505C\u7528\u7684\u6570\u636E
+DICTIONARY_ITEM_REQUIRED=\u5B57\u5178\u9879\u4E0D\u80FD\u4E3A\u7A7A
+DICTIONARY_CODE_EXISTS=\u5B57\u5178\u4EE3\u7801:{0}\u5DF2\u5B58\u5728
+DICTIONARY_ITEM_DUPLICATE_NOT_ALLOWED=\u5B57\u5178\u9879\u4E0D\u80FD\u91CD\u590D
+LOGIN_TIMEOUT=\u767B\u5F55\u8D85\u65F6
+FILE_GEN_DIR_CREATE_FAIL=\u6587\u4EF6\u751F\u6210\u76EE\u5F55\u521B\u5EFA\u5931\u8D25
+EXPORT_FAIL=\u5BFC\u51FA\u5931\u8D25
+TEMPLATE_FILE_NOT_EXIST=\u6A21\u7248\u6587\u4EF6\u4E0D\u5B58\u5728
+RULE_ID=\u89C4\u5219ID
+CHK_TABLE=\u68C0\u6838\u8868\u540D
+CHK_FIELD=\u68C0\u6838\u5B57\u6BB5\u540D\u79F0
+RULE_NAME=\u89C4\u5219\u540D\u79F0
+FULL_CHK=\u5168\u8868\u6027\u6821\u9A8C\u89C4\u5219\u7C7B\u578B
+RULE_EXPR=\u89C4\u5219\u8868\u8FBE\u5F0F
+EXPR_TYPE=\u8868\u8FBE\u5F0F\u7C7B\u578B
+IS_ENABLE=\u662F\u5426\u542F\u7528
+FORCE_SUB=\u662F\u5426\u80FD\u5F3A\u5236\u63D0\u4EA4
+RULE_DESC=\u89C4\u5219\u53D1\u6587\u63CF\u8FF0
+STRONG_CHK=\u662F\u5426\u5F3A\u6821\u9A8C
+THRESHOLD=\u9608\u503C
+WEIGHT=\u6743\u91CD
+REMARK=\u5907\u6CE8
+ONLY_CMT=\u662F\u5426\u53EA\u5907\u6CE8
+PROPOSER=\u63D0\u51FA\u4EBA
+PROP_TIME=\u63D0\u51FA\u65F6\u95F4
+DEVELOPER=\u5F00\u53D1\u4EBA
+DEV_TIME=\u5F00\u53D1\u65F6\u95F4
+TESTER=\u6D4B\u8BD5\u4EBA
+TEST_TIME=\u6D4B\u8BD5\u65F6\u95F4
+RULE_MAINT=\u89C4\u5219\u7EF4\u62A4
+RULE_IMP_TMPL=\u89C4\u5219\u7EF4\u62A4\u5BFC\u5165\u6A21\u677F
+CODE_MAINT=\u7801\u503C\u7EF4\u62A4
+CODE_TYPE=\u4EE3\u7801\u7C7B\u578B
+CODE_TYPE_NAME=\u4EE3\u7801\u7C7B\u578B\u540D\u79F0
+CODE_ITEM=\u4EE3\u7801\u9879
+CODE_ITEM_NAME=\u4EE3\u7801\u9879\u540D\u79F0
+PARENT_ITEM=\u4E0A\u7EA7\u4EE3\u7801\u9879
+CODE_LEVEL=\u4EE3\u7801\u7EA7\u522B
+ITEM_SEQ=\u4EE3\u7801\u9879\u5E8F\u53F7
+IS_BOTTOM=\u662F\u5426\u6700\u5E95\u5C42\u4EE3\u7801
+RULE_MOD_REC=\u89C4\u5219\u4FEE\u6539\u8BB0\u5F55
+OPER_TYPE=\u64CD\u4F5C\u7C7B\u578B
+REPORT_ID=\u76D1\u7BA1ID
+UPDATE_FIELD=\u4FEE\u6539\u5B57\u6BB5
+OLD_VALUE=\u4FEE\u6539\u524D
+NEW_VALUE=\u4FEE\u6539\u540E
+CODE_IMP_TEMPLATE=\u7801\u503C\u5BFC\u5165\u6A21\u677F
+REPORT_GROUP_CODE_EXISTS=\u586B\u62A5\u7EC4\u4EE3\u7801\u5DF2\u5B58\u5728\uFF0C\u8BF7\u4FEE\u6539\u540E\u518D\u64CD\u4F5C
+CRON_EXPRESSION_INVALID=cron\u8868\u8FBE\u5F0F\u65E0\u6548,\u65E0\u6CD5\u65B0\u589E
+TRIGGER_NAME_USED=\u89E6\u53D1\u5668\u540D\u79F0\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u65B0\u589E
+CRON_EXPRESSION_INVALID_EDIT=cron\u8868\u8FBE\u5F0F\u65E0\u6548, \u65E0\u6CD5\u7F16\u8F91
+CRON_EXPRESSION_INVALID_MINUTE_UNIT=cron\u8868\u8FBE\u5F0F\u65E0\u6548, \u8BF7\u786E\u4FDD\u6700\u5C0F\u5355\u4F4D\u6309\u5929
+TRIGGER_ALREADY_USED_CANNOT_EDIT=\u89E6\u53D1\u5668\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u4FEE\u6539
+TRIGGER_NAME_USED_CANNOT_EDIT=\u89E6\u53D1\u5668\u540D\u79F0\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u4FEE\u6539
+ASSOCIATED_TASK_EXISTS_CANNOT_DISABLE=\u5B58\u5728\u5DF2\u5173\u8054\u4EFB\u52A1\uFF0C\u65E0\u6CD5\u505C\u7528
+TRIGGER_IN_USE_CANNOT_DELETE=\u5B58\u5728\u6B63\u5728\u4F7F\u7528\u7684\u89E6\u53D1\u5668\uFF0C\u65E0\u6CD5\u5220\u9664
+PLEASE_SELECT_TRIGGER=\u8BF7\u9009\u62E9\u89E6\u53D1\u5668\uFF01
+TRIGGER_ALREADY_USED_CANNOT_DELETE=\u89E6\u53D1\u5668\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+FIELD_ENGLISH_NAME_EXISTS=\u5B57\u6BB5\u82F1\u6587\u540D\u5DF2\u5B58\u5728
+ADD_FAILED_SELECT_DATASOURCE=\u65B0\u589E\u5931\u8D25,\u8BF7\u9009\u62E9\u6570\u636E\u6E90
+TASKS_EXIST_PLEASE_UNLINK_BEFORE_DELETING=\u5B58\u5728\u5DF2\u5173\u8054\u7684\u586B\u62A5\u4EFB\u52A1\uFF0C\u8BF7\u53D6\u6D88\u5173\u8054\u540E\u5220\u9664
+TASK_EXISTS_CANNOT_DISABLE=\u5B58\u5728\u5173\u8054\u7684\u542F\u7528\u72B6\u6001\u586B\u62A5\u4EFB\u52A1\uFF0C\u6682\u4E0D\u5141\u8BB8\u505C\u7528
+ONLY_APPLY_TO_AUDITING_TASKS=\u53EA\u80FD\u7533\u8BF7\u89E3\u9501\u5BA1\u6838\u4E2D\u7684\u4EFB\u52A1
+REQUEST_PARAMETER_CANNOT_BE_EMPTY=\u8BF7\u6C42\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A
+EXTENSION_FIELD_CANNOT_BE_EMPTY=\u6269\u5C55\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A
+SUPPLEMENTAL_TABLE_CONFIG_NOT_EXIST=\u8865\u5F55\u8868\u914D\u7F6E\u4E0D\u5B58\u5728,\u8BF7\u786E\u8BA4\u540E\u518D\u64CD\u4F5C
+DATA_SOURCE_INFO_NOT_EXIST=\u5F53\u524D\u8868\u6570\u636E\u6E90\u4FE1\u606F\u4E0D\u5B58\u5728

BIN
server/yusp-tagging-core/src/main/resources/template/detail_col_template.xlsx


+ 30 - 0
server/yusp-tagging-starter/Dockerfile

@@ -0,0 +1,30 @@
+FROM openjdk:8-jdk
+
+# 设置维护者
+LABEL maintainer=shaofan1
+
+# 设置环境变量
+ENV PARAMS="--server.port=30001"
+ENV TZ=Asia/Shanghai
+ENV LOGGING_LEVEL_ROOT=INFO
+
+# 设置时区和语言
+RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
+
+# 创建应用目录
+WORKDIR /app
+
+# 创建日志目录
+RUN mkdir -p /app/logs
+
+# 复制打包好的jar文件
+COPY yusp-app-detail.jar app.jar
+
+# 暴露端口
+EXPOSE 30001
+
+# 挂载日志目录,方便宿主机访问和管理
+VOLUME ["/app/logs"]
+
+# 添加lib目录到类路径,确保core.jar被加载,启动应用,支持参数传递和语言设置
+ENTRYPOINT ["/bin/sh","-c","java -Duser.language=zh -Duser.region=CN -jar app.jar ${PARAMS}"]

+ 250 - 0
server/yusp-tagging-starter/pom.xml

@@ -0,0 +1,250 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>cn.com.yusys.yusp</groupId>
+    <parent>
+        <artifactId>server</artifactId>
+        <groupId>cn.com.yusys.yusp</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>yusp-tagging-starter</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- 依赖 core 模块 -->
+        <dependency>
+            <groupId>cn.com.yusys.yusp</groupId>
+            <artifactId>yusp-tagging-core</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <version>2.1.2.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <version>2.1.2.RELEASE</version>
+        </dependency>
+
+
+
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>1.26</version>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okio</groupId>
+            <artifactId>okio</artifactId>
+            <version>3.4.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>kotlin-stdlib</artifactId>
+                    <groupId>org.jetbrains.kotlin</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>1.10.0</version>
+        </dependency>
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+            <version>5.2.20.RELEASE</version>
+        </dependency>
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-messaging</artifactId>
+            <version>5.2.22.RELEASE</version>
+        </dependency>
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <version>5.2.20.RELEASE</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>spring-beans</artifactId>
+                    <groupId>org.springframework</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>spring-expression</artifactId>
+                    <groupId>org.springframework</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>spring-context</artifactId>
+                    <groupId>org.springframework</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--漏洞升级-->
+        <!--        <dependency>
+                    <groupId>org.springframework.security</groupId>
+                    <artifactId>spring-security-crypto</artifactId>
+                    <version>6.1.5</version>
+                </dependency>-->
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http</artifactId>
+            <version>4.1.108.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.15.3</version>
+        </dependency>
+        <!--漏洞升级-->
+        <!--        <dependency>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-core</artifactId>
+                    <version>5.2.24.RELEASE</version>
+                </dependency>-->
+        <!--        <dependency>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-beans</artifactId>
+                    <version>5.2.24.RELEASE</version>
+                </dependency>-->
+        <!-- MySQL JDBC Driver -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.33</version> <!-- 使用最新稳定版本 -->
+        </dependency>
+        <!-- PostgreSQL 的 JDBC 驱动-->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <!-- HikariCP -->
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>3.4.5</version> <!-- 使用最新稳定版本 -->
+        </dependency>
+        <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+            <version>3.8.4</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>logback-classic</artifactId>
+                    <groupId>ch.qos.logback</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>logback-core</artifactId>
+                    <groupId>ch.qos.logback</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+            <version>9.0.86</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.santuario</groupId>
+            <artifactId>xmlsec</artifactId>
+            <version>2.2.6</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>woodstox-core</artifactId>
+                    <groupId>com.fasterxml.woodstox</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-cbor</artifactId>
+            <version>2.17.0-rc1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jettison</groupId>
+            <artifactId>jettison</artifactId>
+            <version>1.5.4</version>
+        </dependency>
+        <!--漏洞升级-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>5.2.25.RELEASE</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>spring-beans</artifactId>
+                    <groupId>org.springframework</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>spring-expression</artifactId>
+                    <groupId>org.springframework</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-expression</artifactId>
+            <version>5.2.25.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+            <version>2.1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+            <version>5.3.33</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>yusp-app-detail</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <executable>true</executable>
+<!--                    <fork>true</fork>-->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>apollo</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <profile.active>apollo</profile.active>
+            </properties>
+            <dependencies>
+            </dependencies>
+        </profile>
+    </profiles>
+
+</project>

+ 16 - 0
server/yusp-tagging-starter/src/main/java/cn/com/yusys/yusp/detail/App.java

@@ -0,0 +1,16 @@
+package cn.com.yusys.yusp.detail;
+
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+@EnableDiscoveryClient
+@SpringBootApplication(scanBasePackages = {"cn.com.yusys.yusp"})
+@MapperScan("cn.com.yusys.yusp.mapper")
+public class App {
+    public static void main(String[] args) {
+        SpringApplication.run(App.class, args);
+    }
+}

+ 56 - 0
server/yusp-tagging-starter/src/main/resources/application.yml

@@ -0,0 +1,56 @@
+spring:
+  cache:
+    type: redis #缓存类型
+  main:
+    allow-bean-definition-overriding: true
+  redis:
+    database: 0 #Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
+    host: 10.192.72.13 #Redis地址
+    port: 36379 #Redis端口
+    password: 123456 #Redis密码
+    timeout: 10000 #Redis连接超时时间
+    lettuce:
+      shutdown-timeout: 60000ms #关闭超时时间
+      pool:
+        max-active: 20 #连接池最大连接数(使用负值表示没有限制) 默认 8
+        max-wait: 10000ms #连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
+        min-idle: 1 #连接池中的最小空闲连接 默认 0
+        max-idle: 20 #连接池中的最大空闲连接 默认 8
+  application:
+    jackson:
+      date-format: yyyy-MM-dd HH:mm:ss #日期格式
+      serialization:
+        INDENT_OUTPUT: true #是否格式化输出
+      default-property-inclusion: non_null #null不进行序列化
+#  datasource:
+#    driver-class-name: org.postgresql.Driver # PostgreSQL数据库连接驱动
+#    type: com.zaxxer.hikari.HikariDataSource # 数据库连接池类型
+#    url: jdbc:postgresql://10.192.72.13:5656/fjnx?currentSchema=ai_tag # PostgreSQL数据库地址及schema
+#    username: ai_tag # 数据库用户名
+#    password: 3edc$RFV # 数据库密码
+#    hikari:
+#      minimum-idle: 1 # 空闲时最小连接数
+#      maximum-pool-size: 20 # 连接池大小
+#      connection-test-query: SELECT 1 # 连接池验证查询
+
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver #mysql数据库连接驱动
+    type: com.zaxxer.hikari.HikariDataSource #数据库连接池类型
+    url: jdbc:mysql://10.192.72.13:6780/fjnx?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&allowMultiQueries=true&useSSL=false #数据库地址
+    username: root #数据库用户名
+    password: 123456 #数据库密码(密文)
+    hikari:
+      minimum-idle: 1 #空闲时最小连接数
+      maximum-pool-size: 20 #连接池大小
+      connection-test-query: select 1 #连接池验证
+
+
+license:
+  licenseContext: <?xml version="1.0" encoding="UTF-8"?><license build-time="Tue Dec 29 13:26:00 CST 2020" validate-code="6A1B578E7FDEF76A57917A12BC359ED4"><element name="customer_name">上海银行股份有限公司</element><element name="contract_code">KJ2020-0038</element><element name="license_code">0000019279</element><element name="product_code">0000001122</element><element name="product_name_zh-cn">宇信科技基于微服务框架统一开发平台</element><element name="product_name_en-us">YUSP</element><element name="version_major">1</element><element name="version_sub">0</element><element name="version_publish">1</element><element name="complie_date">2018-03-20</element><element name="license_type">客户销售许可证</element><element name="license_start_time">2020-12-29</element><element name="license_end_time">2040-12-27</element></license>
+  licenseModel: TRIAL
+management:
+  endpoints:
+    web:
+      exposure:
+        include: info,health,env,configprops,mappings,threaddump,metrics #管理节点
+

+ 25 - 0
server/yusp-tagging-starter/src/main/resources/bootstrap.yml

@@ -0,0 +1,25 @@
+spring:
+  application:
+    name: tag-server
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 10.192.72.13:38848
+        namespace: ai-tagging
+      config:
+        server-addr: 10.192.72.13:38848
+        namespace: ai-tagging
+        file-extension: yaml
+        username: nacos
+        password: nacos
+        group: DEFAULT_GROUP
+        extension-configs[0]:
+          data-id: tag-server.yaml
+          refresh: true
+        extension-configs[1]:
+          data-id: application-license.yml
+          refresh: true #配置扩展属性动态刷新
+
+
+server:
+  port: 30001

+ 8 - 0
server/yusp-tagging-starter/src/main/scripts/deploy.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+SERVICE_NAME=frs-yusp-bfd-starter
+SERVICE_VERSION=2.1.2-SNAPSHOT
+unzip -o $HOME/app/SERVICE_NAME-SERVICE_VERSION.zip -d $HOME/app/$SERVICE_NAME
+cd $HOME/app/$SERVICE_NAME/scripts
+./shutdown.sh
+./startup.sh
+

+ 17 - 0
server/yusp-tagging-starter/src/main/scripts/shutdown.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+SERVICE_NAME=frs-yusp-bfd-starter
+export APP_NAME=$SERVICE_NAME
+if [[ -z "$JAVA_HOME" && -d /usr/java/latest/ ]]; then
+    export JAVA_HOME=/usr/java/latest/
+fi
+
+cd  dirname $0 /..
+
+if [[ ! -f $SERVICE_NAME".war" && -d current ]]; then
+    cd current
+fi
+
+if [[ -f $SERVICE_NAME".war" ]]; then
+  chmod a+x $SERVICE_NAME".war"
+  ./$SERVICE_NAME".war" stop
+fi

+ 1 - 0
server/yusp-tagging-starter/src/main/scripts/startup.cmd

@@ -0,0 +1 @@
+java -jar -Dfile.encoding=UTF-8 ../frs-yusp-bfd-starter.jar

+ 105 - 0
server/yusp-tagging-starter/src/main/scripts/startup.sh

@@ -0,0 +1,105 @@
+#!/bin/bash
+SERVICE_NAME=frs-yusp-bfd-starter
+## Adjust log dir if necessary
+LOG_DIR=../logs
+## Adjust server port if necessary
+SERVER_PORT=8080
+## set app name
+export APP_NAME=$SERVICE_NAME
+
+## You must Adjust for apollo !!!!!!!!!!!!!!!!!
+#是否启用apollo
+APOLLO_ENABLED=true
+APOLLO_APPID=yusp-gateway
+APOLLO_ENV=DEV
+APOLLO_DEVMETA=http://localhost:9001
+NAMESPACES=application.yml
+
+## Adjust memory settings if necessary
+#export JAVA_OPTS="-Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=4096m -XX:MaxNewSize=4096m -XX:SurvivorRatio=8"
+#export JAVA_OPTS="-Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=4096m -XX:MaxNewSize=4096m -XX:SurvivorRatio=8"
+
+## Only uncomment the following when you are using server jvm
+#export JAVA_OPTS="$JAVA_OPTS -server -XX:-ReduceInitialCardMarks"
+
+export JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Duser.timezone=Asia/Shanghai -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
+export JAVA_OPTS="$JAVA_OPTS -Dserver.port=$SERVER_PORT -Dlogging.file=$LOG_DIR/$SERVICE_NAME.log -Xloggc:$LOG_DIR/heap_trace.txt -XX:HeapDumpPath=$LOG_DIR/HeapDumpOnOutOfMemoryError/"
+if [ $APOLLO_ENABLED ]; then
+    export JAVA_OPTS="$JAVA_OPTS -Dapp.id=$APOLLO_APPID -Denv=$APOLLO_ENV -Dapollo.meta=$APOLLO_DEVMETA -Dapollo.bootstrap.enabled=true -Dapollo.bootstrap.namespaces=$NAMESPACES"
+else
+    export JAVA_OPTS="$JAVA_OPTS -Dapollo.bootstrap.enabled=false"
+fi
+
+PATH_TO_JAR=$SERVICE_NAME".jar"
+SERVER_URL="http://localhost:$SERVER_PORT"
+
+function checkPidAlive {
+    return 0;
+}
+
+cd  dirname $0 /..
+
+for i in  ls *$SERVICE_NAME-*.jar 2>/dev/null
+do
+    if [[ ! $i == *"-sources.jar" ]]
+    then
+        PATH_TO_JAR=$i
+        break
+    fi
+done
+
+if [[ ! -f PATH_TO_JAR && -d current ]]; then
+    cd current
+    for i in  ls *$SERVICE_NAME-*.jar 2>/dev/null
+    do
+        if [[ ! $i == *"-sources.jar" ]]
+        then
+            PATH_TO_JAR=$i
+            break
+        fi
+    done
+fi
+
+if [[ -f $SERVICE_NAME".jar" ]]; then
+  rm -rf $SERVICE_NAME".jar"
+fi
+
+printf "$(date) ==== Starting ==== \n"
+
+ln $PATH_TO_JAR $SERVICE_NAME".jar"
+chmod a+x $SERVICE_NAME".jar"
+./$SERVICE_NAME".jar" start
+
+rc=$?;
+
+if [[ $rc != 0 ]];
+then
+    echo "$(date) Failed to start $SERVICE_NAME.jar, return code: $rc"
+    exit $rc;
+fi
+
+declare -i counter=0
+declare -i max_counter=48 # 48*5=240s
+declare -i total_time=0
+
+printf "Waiting for server startup"
+until [[ (( counter -ge max_counter )) || "$(curl -X GET --silent --connect-timeout 1 --max-time 2 --head $SERVER_URL | grep "HTTP/1.1 200")" != "" ]];
+do
+    printf "."
+    counter+=1
+    sleep 5
+
+    checkPidAlive
+done
+
+total_time=counter*5
+
+if [[ (( counter -ge max_counter )) ]];
+then
+    printf "\n$(date) Server failed to start in $total_time seconds!\n"
+    exit 1;
+fi
+
+printf "\n$(date) Server started in $total_time seconds!\n"
+
+exit 0;

BIN
web/dist.zip


+ 2 - 2
web/src/api/common/oauth.js

@@ -63,8 +63,8 @@ export function getSessionInfoFn() {
 export function getMenuandcontrFn(params,headers) {
   console.log(headers, "---headers")
   return request({
-    // url: backend.appOcaService + '/api/account/menuandcontr',
-    url: backend.appOcaService + '/api/adminsmapp/catemenu2',
+    url: backend.appOcaService + '/api/account/menuandcontr',
+    // url: backend.appOcaService + '/api/adminsmapp/catemenu2',
     method: 'get',
     headers:headers,
     params

+ 3 - 0
web/src/assets/common/base.css

@@ -769,6 +769,9 @@ sup {
     border-bottom: 13px solid #F4F8FE;
     background: #fff;
 }
+.search-bar .el-form-item{
+    margin-bottom: 0;
+}
 
 .list-container {
     min-height: calc(100vh - 136px);

+ 1 - 1
web/src/assets/common/common.css

@@ -1317,7 +1317,7 @@ textarea {
   display: inline-block;
   padding-right: 16px;
   font-weight: 600;
-  border-right: 1px solid rgb(0, 0, 0, 0.12);
+  /* border-right: 1px solid rgb(0, 0, 0, 0.12); */
 }
 
 .yu-frame-top-left-bar .left-content .yu-app-box-top-button-wrap .sort-name {

+ 0 - 1
web/src/components/base/Login/index.vue

@@ -779,7 +779,6 @@
   .login-foot {
     height: 100px;
     line-height: 100px;
-    // background: #FFFFFF;
     text-align: center;
 
     & > * {

+ 3 - 3
web/src/components/layout/Navbar/topLeft.vue

@@ -11,7 +11,7 @@
           <!-- {{ appName }} -->
           {{ tenantInfo.systemName }}
         </span>
-        <span class="sort-name">
+        <span class="sort-name" style="display: none;">
           <div>
             <span :title="curCateName">{{ curCateName }}</span
             ><img class="arr-down" :src="arrowDownImg" />
@@ -48,7 +48,7 @@
       </div>
     </div>
     <!-- 工作台 搜索 -->
-    <div class="yu-frame-top-bar-content-search">
+    <div class="yu-frame-top-bar-content-search" style="display: none;">
       <span :class="{ 'is-hidden': isHidden }" id="step3" style="width: 140px">
         <span class="stationFontClass" @click="toWorkstation">
           <img src="@/assets/common/img/icon_gzt.png" />{{
@@ -414,7 +414,7 @@
       </div>
 
       <!-- 国际化 -->
-      <div ref="topThemesLanguage" class="yu-frame-top-language">
+      <div ref="topThemesLanguage" class="yu-frame-top-language" style="display: none;">
         <yu-dropdown @command="switchLanguage">
           <span
             id="step8"

+ 0 - 2
web/src/components/layout/TopLeft/leftMenu.vue

@@ -88,7 +88,6 @@
 		z-index: 2000;
 		opacity: 1;
 		color: #fff;
-		// background: #255DDB;
 		transition: width 0.3s ease, opacity 0.15s ease;
 		.yu-frame-left-menu-content {
 			position: relative;
@@ -124,7 +123,6 @@
 			.left-menu-box {
 				overflow-y: auto;
 				height: calc(100vh - 107px);
-				// background: #255DDB;
 				margin-top: 10px;
 				.el-menu-item, .el-submenu__title {
 					span {

+ 3 - 0
web/src/config/constant/app.data.service.js

@@ -33,7 +33,10 @@ const serviceNameList = {
   gatewayService: "/base-gateway", // 网关
   appCommonService: "/base-common", // 基础服务
   appOcaService: "/base-oca", // 组织机构、菜单权限微服务
+  tagServer:"/tag-server",//智能打标服务
   
+
+
   useService: "/yusp-use3.2", // 一体化微服务
   oddpService: "/yudi-oddp", // 一体化微服务
   oddpServicePrd: "/yudi-oddp", // 一体化微服

+ 1 - 1
web/src/config/other/css.js

@@ -5,7 +5,7 @@
 import store from "@/store";
 import "@/assets/common/normalize.css"; // A modern alternative to CSS resets
 import "@/assets/common/iconfont/iconfont.css"; // @fixable 使用yuwp-ui字体图标
-// import "@/assets/common/iconfont/iconfont3.css"; // @fixable 使用yuwp-ui字体图标
+import "@/assets/common/iconfont/iconfont3.css"; // @fixable 使用yuwp-ui字体图标
 // import "@/assets/common/iconfont/iconfont4.css"; // @fixable 使用yuwp-ui字体图标
 // import "@/assets/common/iconfont_bddp/iconfont.css"; // bddp 离线图标
 // import "@/assets/common/iconfont_ida/iconfont.css"; // @fixable 使用yuwp-ui字体图标-一体化经分

+ 34 - 34
web/src/router/router-filter.js

@@ -26,44 +26,42 @@ const whiteList = settings.whiteList;
 const originalRequest = yufp.service.request; // 先保存原始请求函数
 // 路由前置拦截器
 router.beforeEach(async (to, from, next) => {
-  console.log('to',to);
-  
   // 从其他系统跳转到本系统直接查看看板预览的界面
   if (to.query.page_name && to.query.page_name === "externalPreviewBoard") {
     // 其他系统跳转到本系统的预览界面,不需要传token验证,headers中加一个Jobid属性
     yufp.service.request = async param => {
-			param.data = param.data || {};
-			if ((param.method || 'get').toLowerCase() === 'get') {
-				param['params'] = param.data;
+		param.data = param.data || {};
+		if ((param.method || 'get').toLowerCase() === 'get') {
+			param['params'] = param.data;
 
-				if (Object.prototype.toString(param.params).includes('Object')) {
-					let a = JSON.stringify(param.params).replaceAll('#', '%23');
-					param.params = JSON.parse(a);
-				}
-			}
-			
-			if (!param.headers) {
-				param.headers = {};
-			}
-			param.headers.Projectid = yufp.sessionStorage.get('projectId') || ''
-			param.headers.Jobid = to.query?.Jobid??""
-			param.needToken = false
-			const manager = await store.dispatch("sysProp/getSysProp", i18n.t('router.54cc9e1f-409c-47dc-951c-65c03588f200'));
-			if (manager) {
-				param.headers.isManager = yufp.session.userName == manager ? '1' : '0';
-				param.headers.managerName = encodeURIComponent(manager);
+			if (Object.prototype.toString(param.params).includes('Object')) {
+				let a = JSON.stringify(param.params).replaceAll('#', '%23');
+				param.params = JSON.parse(a);
 			}
+		}
+		
+		if (!param.headers) {
+			param.headers = {};
+		}
+		param.headers.Projectid = yufp.sessionStorage.get('projectId') || ''
+		param.headers.Jobid = to.query?.Jobid??""
+		param.needToken = false
+		const manager = await store.dispatch("sysProp/getSysProp", i18n.t('router.54cc9e1f-409c-47dc-951c-65c03588f200'));
+		if (manager) {
+			param.headers.isManager = yufp.session.userName == manager ? '1' : '0';
+			param.headers.managerName = encodeURIComponent(manager);
+		}
       let language = getLanguage()
-			if(language == 'en') {
-				language = 'en-US'
-			}else {
-				language = 'zh-CN'
-			}
-			param.headers[ 'Accept-Language']= language;
-			request(param).then(res => {
-				param.callback(res.code, '', res)
-			})
-		},
+		if(language == 'en') {
+			language = 'en-US'
+		}else {
+			language = 'zh-CN'
+		}
+		param.headers[ 'Accept-Language']= language;
+		request(param).then(res => {
+			param.callback(res.code, '', res)
+		})
+	},
     next()
   } else {
     // 基础的路由拦截逻辑
@@ -93,9 +91,10 @@ router.beforeEach(async (to, from, next) => {
     // set page title
     // console.log(`当前路由信息${JSON.stringify(to)}`)
     // document.title = getPageTitle(to.meta.title)
-    if (to.path === "/") {
-      next({ path: "/404" });
-    }
+    // 注释掉根路径重定向到404的逻辑,以便登录成功后能正常跳转到主页面
+    // if (to.path === "/") {
+    //   next({ path: "/404" });
+    // }
     // 1.获取Token信息,判定是否已登录
     const hasToken = getToken();
     /* 2.不存在token信息,当前状态是未登录 */
@@ -152,6 +151,7 @@ router.beforeEach(async (to, from, next) => {
       next();
       return;
     }
+
     // 角色非必须时,刷新页面重新请求用户信息
     if (!settings.mustRole && store.getters.userName) {
       await store.dispatch("oauth/getSessionInfo");

+ 156 - 85
web/src/store/modules/oauth.js

@@ -10,7 +10,7 @@ import router, { resetRouter } from '@/router'
 import constantRoutes from '@/router/constant-routes'
 import {getLanguage} from "@/utils/i18n";
 // import { USER_MAPPING, setToken, removeToken, generateRoutes, MENU_JSON_ROOT, MENU_JSON_ROOT1,CTRL_JSON_ROOT, USER_STORE_KEY, MENU_STOREOG_KEY, MENU_STORE_KEY, CTRL_STOREOG_KEY, CTRL_STORE_KEY, ROUTER_STORE_KEY, CURRENT_TOP_MENU_STORE_KEY } from '@/utils/oauth'
-import { USER_MAPPING, setToken, removeToken, generateRoutes, MENU_JSON_ROOT, MENU_JSON_ROOT1, CTRL_JSON_ROOT, USER_STORE_KEY, MENU_STOREOG_KEY, MENU_STORE_KEY, CTRL_STOREOG_KEY, CTRL_STORE_KEY, ROUTER_STORE_KEY, CURRENT_TOP_MENU_STORE_KEY, CATE_NAME_STORE_KEY } from '@/utils/oauth'
+import { USER_MAPPING, setToken, removeToken, generateRoutes, MENU_JSON_ROOT, /* MENU_JSON_ROOT1, */ CTRL_JSON_ROOT, USER_STORE_KEY, MENU_STOREOG_KEY, MENU_STORE_KEY, CTRL_STOREOG_KEY, CTRL_STORE_KEY, ROUTER_STORE_KEY, CURRENT_TOP_MENU_STORE_KEY /*, CATE_NAME_STORE_KEY */ } from '@/utils/oauth'
 
 import { extend, toMappingFn, sessionStore } from 'xy-utils'
 
@@ -49,7 +49,7 @@ const state = {
   showLeftMenu: true, // 上左菜单时 是否显示左侧二级菜单
   routes: sessionStore.get(ROUTER_STORE_KEY) || [], // 路由
   // cateNameId: '', //默认公共管理
-  cateNameId: sessionStore.get(CATE_NAME_STORE_KEY) || '',
+  // cateNameId: sessionStore.get(CATE_NAME_STORE_KEY) || '', // 移除多分类菜单支持
   allRoutes: [], // 全量路由
   userPic: ''
 }
@@ -148,10 +148,10 @@ const mutations = {
   SET_SHOW_LEFT_MENU: (state, isShow) => {
     state.showLeftMenu = isShow;
   },
-  SET_CATE_NAME_ID: (state, id) => {
-    state.cateNameId = id
-    sessionStore.set(CATE_NAME_STORE_KEY, id)
-  },
+  // SET_CATE_NAME_ID: (state, id) => {
+  //   state.cateNameId = id
+  //   sessionStore.set(CATE_NAME_STORE_KEY, id)
+  // }, // 移除多分类菜单支持
   CHANGE_MESSAGE_USER_PIC: (state, pic) => {
     state.userPic = pic
   }
@@ -219,37 +219,53 @@ const actions = {
   // 获取用户会话信息、菜单及控制点信息
   getSessionInfo ({ dispatch, commit, state }) {
     return new Promise(async (resolve, reject) => {
-      await getSessionInfoFn().then(response => {
-        if (!response) {
-          reject('Verification failed, please Login again.')
-        }
-        commit('CHANGE_MESSAGE_USER_PIC', response.userAvatar); 
-        const userInfo = toMappingFn(response, USER_MAPPING)
-        const { userId, userName, userCode, userAvatar, logicSys, loginCode,
-          roles, org, dpt, instu, upOrg, upDpt, ...otherInfo } = userInfo
-        const role = roles && roles.length > 0 ? roles[0] : {};
-        commit('SET_USER_ID', userId)
-        commit('SET_USER_NAME', userName)
-        commit('SET_USER_CODE', userCode)
-        commit('SET_USER_AVATAR', userAvatar)
-        commit('SET_LOGIC_SYS', logicSys)
-        commit('SET_ROLES', roles)
-        commit('SET_SELECTED_ROLES', role)
-        commit('SET_ORG', org)
-        commit('SET_DPT', dpt)
-        commit('SET_INSTU', instu)
-        commit('SET_UP_ORG', upOrg)
-        commit('SET_UP_DPT', upDpt)
-        commit('SET_OTHER_INFO', otherInfo)
-        commit('CHANGE_MESSAGE_USER_PIC', userInfo.userAvatar);
-        userInfo.loginCode = userInfo.userCode
-        sessionStore.set(USER_STORE_KEY, userInfo)
-        // #TODO 临时,将用户信息挂载到yufp.session上
-        yufp.extend(yufp.session, userInfo);
-        yufp.extend(yufp.session.user = {}, userInfo);
-      }).catch(error => {
-        reject(error)
-      })
+      try {
+        await getSessionInfoFn().then(response => {
+          if (!response) {
+            console.error('=== getSessionInfoFn response is null ===');
+            // 即使没有获取到用户信息,也不阻止登录成功后的跳转
+            // reject('Verification failed, please Login again.')
+          } else {
+            commit('CHANGE_MESSAGE_USER_PIC', response.userAvatar); 
+            const userInfo = toMappingFn(response, USER_MAPPING)
+            const { userId, userName, userCode, userAvatar, logicSys, loginCode,
+              roles, org, dpt, instu, upOrg, upDpt, ...otherInfo } = userInfo
+            const role = roles && roles.length > 0 ? roles[0] : {};
+            commit('SET_USER_ID', userId)
+            commit('SET_USER_NAME', userName)
+            commit('SET_USER_CODE', userCode)
+            commit('SET_USER_AVATAR', userAvatar)
+            commit('SET_LOGIC_SYS', logicSys)
+            commit('SET_ROLES', roles)
+            commit('SET_SELECTED_ROLES', role)
+            commit('SET_ORG', org)
+            commit('SET_DPT', dpt)
+            commit('SET_INSTU', instu)
+            commit('SET_UP_ORG', upOrg)
+            commit('SET_UP_DPT', upDpt)
+            commit('SET_OTHER_INFO', otherInfo)
+            commit('CHANGE_MESSAGE_USER_PIC', userInfo.userAvatar);
+            userInfo.loginCode = userInfo.userCode
+            sessionStore.set(USER_STORE_KEY, userInfo)
+            // #TODO 临时,将用户信息挂载到yufp.session上
+            try {
+              if (yufp && yufp.extend) {
+                yufp.extend(yufp.session, userInfo);
+                yufp.extend(yufp.session.user = {}, userInfo);
+              }
+            } catch (e) {
+              console.error('=== yufp.extend error ===', e);
+            }
+          }
+        }).catch(error => {
+          console.error('=== getSessionInfoFn error ===', error);
+          // 即使获取用户信息失败,也不阻止登录成功后的跳转
+          // reject(error)
+        })
+      } catch (error) {
+        console.error('=== getSessionInfoFn try-catch error ===', error);
+        // 即使获取用户信息失败,也不阻止登录成功后的跳转
+      }
       // let param = {}
       let headers = {}
       let language = getLanguage()
@@ -261,61 +277,116 @@ const actions = {
 			}
       Vue.prototype.$labelPosition = language == 'en-US' ? 'top' :'right'
       headers['Accept-Language']= language;
-      await getMenuandcontrFn({},headers).then(response => {
-        let _menu=response.data[MENU_JSON_ROOT1]
-        // 处理数据,将路径调整到对应的路由上,仅供测试使用
-        Object.values(_menu).forEach(item => {
-          item.forEach(m => {
-            if (m.funcUrl) {
-              m.funcUrl = m.funcUrl.replace('pages/', '')
+      try {
+        await getMenuandcontrFn({},headers).then(response => {
+          
+          // ==================== 新增:简化菜单处理逻辑(去除多分类)====================
+          // 根据实际API返回的数据结构提取菜单数据
+          // /api/account/menuandcontr 返回的数据结构是 { menu: [...] },没有 data 字段
+          // /api/adminsmapp/catemenu2 返回的数据结构是 { data: { menu: {...} } }
+          let menuData = response && response.menu ? response.menu : (response.data && response.data.menu ? response.data.menu : undefined)
+          
+          // 处理数据结构:如果 menuData 是对象(多分类结构),则转换为数组
+          let menus = []
+          if (menuData) {
+            if (Array.isArray(menuData)) {
+              // 如果已经是数组,直接使用
+              menus = menuData
+            } else {
+              // 如果是对象,将所有分类的菜单合并为一个数组
+              try {
+                Object.values(menuData).forEach(category => {
+                  if (Array.isArray(category)) {
+                    menus = menus.concat(category)
+                  }
+                })
+              } catch (e) {
+                console.error('=== Object.values error ===', e);
+              }
             }
-          })
-        })
-        // 默认是COMMON_MANAGE 如果没有公共模块就按照返回取一个
-        const keys = Object.keys(_menu);
-        if (!state.cateNameId) {
-          state.cateNameId = keys.includes("COMMON_MANAGE") ? "COMMON_MANAGE" : keys[0];
-        }
-
-        if (!response || !_menu[state.cateNameId]) {
-          reject('Verification failed, please Login again.')
-        }
-        // 全量菜单-添加菜单
-        let arr = Object.values(_menu), list = [];
-        arr.forEach(item => {
-          if (item.length) {
-            item.forEach(ele => {
-              list.push(yufp.clone(ele, {}));
+          }
+          
+          // 处理数据,将路径调整到对应的路由上
+          if (menus && Array.isArray(menus)) {
+            menus.forEach(m => {
+              if (m.funcUrl) {
+                m.funcUrl = m.funcUrl.replace('pages/', '')
+              }
             })
           }
-        })
 
-        sessionStore.set('menu-key', JSON.stringify(Object.keys(response.data.menu)))
+          // 即使菜单数据为空,也不阻止登录成功后的跳转
+          // 这样可以确保即使后端接口没有返回菜单数据,用户也能登录成功并跳转到主页面
+          // if (!response || !response.data) {
+          //   reject('Verification failed, please Login again.')
+          // }
 
-        commit('SET_ORIGINAL_MENUS', list)
-        commit('SET_ORIGINAL_CTRLS', response.data[CTRL_JSON_ROOT])
-        commit('SET_CTRLS', response.data[CTRL_JSON_ROOT]) // TODO 操作级权限点
+          // 存储菜单键(如果需要)
+          // 兼容两种不同的 API 返回格式
+          if (response && response.data && response.data.menu) {
+            try {
+              // 如果 menu 是对象(多分类结构),则存储其键
+              if (typeof response.data.menu === 'object' && !Array.isArray(response.data.menu)) {
+                sessionStore.set('menu-key', JSON.stringify(Object.keys(response.data.menu)))
+              } else {
+                // 如果 menu 是数组,则使用默认的分类 ID
+                sessionStore.set('menu-key', JSON.stringify(['DEFAULT']))
+              }
+            } catch (e) {
+              console.error('=== sessionStore.set error ===', e);
+            }
+          } else if (response && response.menu) {
+            try {
+              // 如果 menu 是对象(多分类结构),则存储其键
+              if (typeof response.menu === 'object' && !Array.isArray(response.menu)) {
+                sessionStore.set('menu-key', JSON.stringify(Object.keys(response.menu)))
+              } else {
+                // 如果 menu 是数组,则使用默认的分类 ID
+                sessionStore.set('menu-key', JSON.stringify(['DEFAULT']))
+              }
+            } catch (e) {
+              console.error('=== sessionStore.set error ===', e);
+            }
+          }
+          
+          commit('SET_ORIGINAL_MENUS', menus)
+          // 兼容两种不同的 API 返回格式,提取控制点数据
+          commit('SET_ORIGINAL_CTRLS', response && response.contr ? response.contr : (response && response.data ? response.data[CTRL_JSON_ROOT] : []))
+          commit('SET_CTRLS', response && response.contr ? response.contr : (response && response.data ? response.data[CTRL_JSON_ROOT] : [])) // TODO 操作级权限点
 
-        // 主菜单
-        const copyData = extend({}, _menu, true)
-        let menus = copyData[state.cateNameId];
-        menus = menus.filter(item => {
-          return item.menuName !== '工作台'
-        })
-        const accessRoutes = generateRoutes(menus)
-        commit('SET_ROUTES', accessRoutes)
-        // 全量路由
-        let allmenus = yufp.clone(list, []);
-        allmenus = allmenus.filter(item => {
-          return item.menuName !== '工作台'
+          // 过滤掉工作台菜单
+          const filteredMenus = menus.filter(item => {
+            return item.menuName !== '工作台'
+          })
+          // 生成路由
+          try {
+            let accessRoutes = []
+            
+            // 如果 filteredMenus 不为空,则生成路由
+            if (filteredMenus && filteredMenus.length > 0) {
+              accessRoutes = generateRoutes(filteredMenus)
+            } 
+            
+            commit('SET_ROUTES', accessRoutes)
+            
+            // 全量路由(与主路由相同)
+            commit('SET_All_ROUTES', accessRoutes)
+            
+            // 添加路由
+            router.addRoutes(accessRoutes)
+          } catch (e) {
+            console.error('=== generateRoutes error ===', e);
+          }
+          // ==================== 新增:简化菜单处理逻辑(去除多分类)====================
+        }).catch(error => {
+          console.error('=== getMenuandcontrFn error ===', error);
+          // 即使获取菜单和控制点信息失败,也不阻止登录成功后的跳转
+          // reject(error)
         })
-        const allRoutes = generateRoutes(allmenus)
-        commit('SET_All_ROUTES', allRoutes)
-        // resetRouter()
-        router.addRoutes(allRoutes)
-      }).catch(error => {
-        reject(error)
-      })
+      } catch (error) {
+        console.error('=== getMenuandcontrFn try-catch error ===', error);
+        // 即使获取菜单和控制点信息失败,也不阻止登录成功后的跳转
+      }
 
       resolve()
     })

+ 102 - 252
web/src/views/content/aiTagging/appAuthManage/index.vue

@@ -28,9 +28,6 @@
           <template slot-scope="scope">
             <div class="app-id-container">
               <span class="app-id">{{ scope.row.appId }}</span>
-              <el-button type="text" size="mini" @click="handleCopy(scope.row.appId)" class="copy-btn">
-                <i class="el-icon-document-copy"></i>
-              </el-button>
             </div>
           </template>
         </el-table-column>
@@ -39,15 +36,9 @@
             <span>************************</span>
           </template>
         </el-table-column>
-        <el-table-column prop="expiryTime" label="有效时间" min-width="180">
-          <template slot-scope="scope">
-            <span>{{ scope.row.expiryTime || '--' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" min-width="160">
+        <el-table-column label="操作" min-width="100">
           <template slot-scope="scope">
             <el-button type="text" @click="handleResetSecret(scope.row)" class="action-btn">重置密钥</el-button>
-            <el-button type="text" @click="handleExtend(scope.row)" class="action-btn">延期</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -107,48 +98,6 @@
       </span>
     </el-dialog>
     
-    <!-- 延期确认弹框 -->
-    <el-dialog
-      title="密钥延期确认"
-      :visible.sync="extendConfirmVisible"
-      width="400px"
-      :close-on-click-modal="false"
-    >
-      <div class="extend-confirm">
-        <div class="confirm-icon">
-          <i class="el-icon-info"></i>
-        </div>
-        <div class="confirm-content">
-          <p>密钥延期为敏感操作,确定要将该密钥延期 180 天?</p>
-        </div>
-      </div>
-      <span slot="footer" class="dialog-footer">
-        <el-button @click="extendConfirmVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleConfirmExtend">确定</el-button>
-      </span>
-    </el-dialog>
-    
-    <!-- 无需延期提示 -->
-    <el-dialog
-      title="提示"
-      :visible.sync="noExtendVisible"
-      width="400px"
-      :close-on-click-modal="false"
-      :show-close="false"
-    >
-      <div class="no-extend">
-        <div class="warning-icon">
-          <i class="el-icon-warning"></i>
-        </div>
-        <div class="warning-content">
-          <p>该密钥尚未过期,无需延期</p>
-        </div>
-      </div>
-      <span slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="noExtendVisible = false">确定</el-button>
-      </span>
-    </el-dialog>
-    
     <!-- 密钥生成成功弹框 -->
     <el-dialog
       title="密钥已生成!"
@@ -171,6 +120,7 @@
 </template>
 
 <script>
+import { copyToClipboard } from '../utils/common';
 export default {
   name: 'AppAuthManage',
   data() {
@@ -184,104 +134,59 @@ export default {
         appName: ''
       },
       // 应用列表数据
-      appList: [
-        {
-          id: 1,
-          appName: '应用A',
-          appId: 'YY20260203001098791241241',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 2,
-          appName: '应用B',
-          appId: 'YY2026020300123124441241',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 3,
-          appName: '应用C',
-          appId: 'YY20260203001321321321567',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 4,
-          appName: '应用D',
-          appId: 'YY2026020300109879124167',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 5,
-          appName: '应用E',
-          appId: 'YY2026020300109879890786',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 6,
-          appName: '应用F',
-          appId: 'YY2026020300105567876671',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 7,
-          appName: '应用G',
-          appId: 'YY20260203001098791242241',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 8,
-          appName: '应用H',
-          appId: 'YY20260203001098791241232',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 9,
-          appName: '应用I',
-          appId: 'YY20260203001098791245545',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        },
-        {
-          id: 10,
-          appName: '应用J',
-          appId: 'YY20260203001098791241980',
-          appSecret: 'secret1234567890',
-          expiryTime: ''
-        }
-      ],
+      appList: [],
       // 分页数据
       currentPage: 1,
       pageSize: 10,
-      totalCount: 120,
+      totalCount: 0,
       // 弹框状态
       dialogVisible: false,
       secretDialogVisible: false,
       resetConfirmVisible: false,
-      extendConfirmVisible: false,
-      noExtendVisible: false,
       // 生成的密钥
       generatedSecret: '',
       // 当前操作的应用
       currentApp: null
     }
   },
+  mounted() {
+    // 默认请求列表数据
+    this.loadAppList();
+  },
   methods: {
+    // 加载应用列表数据
+    loadAppList() {
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-app/query",
+        method: 'get',
+        data: {
+          // sysId: yufp.session.logicSys.id,
+          appName: this.searchForm.appName,
+          page: this.currentPage,
+          pageSize: this.pageSize
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.appList = response.data || [];
+            this.totalCount = response.total || 0;
+          } else {
+            this.$message.error(response.message || '获取应用列表失败');
+          }
+        }
+      });
+    },
     // 查询
     handleSearch() {
       console.log('查询应用:', this.searchForm.appName);
-      // 这里可以添加查询逻辑
+      this.currentPage = 1;
+      this.loadAppList();
     },
     
     // 重置
     handleReset() {
       this.searchForm.appName = '';
+      this.currentPage = 1;
+      this.loadAppList();
       console.log('重置搜索条件');
     },
     
@@ -302,45 +207,50 @@ export default {
         return;
       }
       
-      // 生成随机密钥
-      this.generatedSecret = this.generateRandomSecret();
-      
-      // 模拟创建应用
-      console.log('创建应用:', this.newAppForm.appName);
-      console.log('生成密钥:', this.generatedSecret);
-      
-      // 关闭新建应用弹框
-      this.dialogVisible = false;
-      
-      // 打开密钥生成成功弹框
-      this.secretDialogVisible = true;
+      // 调用后端接口创建应用
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-app/add",
+        method: 'post',
+        data: {
+          appName: this.newAppForm.appName
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            // 保存生成的密钥
+            this.generatedSecret = response.data.appSecret;
+            
+            // 关闭新建应用弹框
+            this.dialogVisible = false;
+            
+            // 打开密钥生成成功弹框
+            this.secretDialogVisible = true;
+            
+            // 重新加载应用列表
+            this.loadAppList();
+          } else {
+            this.$message.error(response.message || '创建应用失败');
+          }
+        }
+      });
     },
     
-    // 生成随机密钥
-    generateRandomSecret() {
-      const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
-      let secret = '';
-      for (let i = 0; i < 20; i++) {
-        secret += chars.charAt(Math.floor(Math.random() * chars.length));
-      }
-      return secret;
-    },
+
     
     // 复制生成的密钥
-    handleCopySecret() {
-      // 模拟复制到剪贴板
-      console.log('复制密钥:', this.generatedSecret);
-      this.$message.success('复制成功');
-      // 关闭密钥生成成功弹框
-      this.secretDialogVisible = false;
+    async handleCopySecret() {
+      try {
+        // 使用公共复制方法
+        await copyToClipboard(this.generatedSecret, this);
+        // 关闭密钥生成成功弹框
+        this.secretDialogVisible = false;
+      } catch (error) {
+        console.error('复制失败:', error);
+        // 复制失败后仍然关闭弹框
+        this.secretDialogVisible = false;
+      }
     },
     
-    // 复制应用ID
-    handleCopy(appId) {
-      console.log('复制应用ID:', appId);
-      // 这里可以添加复制到剪贴板的逻辑
-      this.$message.success('复制成功');
-    },
+    
     
     // 重置密钥
     handleResetSecret(row) {
@@ -354,66 +264,45 @@ export default {
     handleConfirmReset() {
       if (!this.currentApp) return;
       
-      // 生成随机密钥
-      this.generatedSecret = this.generateRandomSecret();
-      
-      // 模拟重置密钥
-      console.log('重置密钥:', this.currentApp.appName);
-      console.log('生成新密钥:', this.generatedSecret);
-      
-      // 关闭确认弹框
-      this.resetConfirmVisible = false;
-      
-      // 打开密钥生成成功弹框
-      this.secretDialogVisible = true;
-    },
-    
-    // 延期
-    handleExtend(row) {
-      // 保存当前操作的应用
-      this.currentApp = row;
-      
-      // 模拟检查密钥是否过期
-      // 这里可以根据实际的expiryTime来判断
-      // 为了演示,我们假设前5个应用需要延期,后5个不需要
-      const needExtend = row.id <= 5;
-      
-      if (needExtend) {
-        // 显示延期确认弹框
-        this.extendConfirmVisible = true;
-      } else {
-        // 显示无需延期提示
-        this.noExtendVisible = true;
-      }
-    },
-    
-    // 确认延期
-    handleConfirmExtend() {
-      if (!this.currentApp) return;
-      
-      // 模拟延期操作
-      console.log('延期:', this.currentApp.appName);
-      console.log('延期180天');
-      
-      // 关闭确认弹框
-      this.extendConfirmVisible = false;
-      
-      // 显示成功提示
-      this.$message.success('延期成功');
+      // 调用后端接口重置密钥
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-app/reset-secret",
+        method: 'post',
+        data: {
+          appId: this.currentApp.appId
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            // 保存生成的密钥
+            this.generatedSecret = response.data.appSecret;
+            
+            // 关闭确认弹框
+            this.resetConfirmVisible = false;
+            
+            // 打开密钥生成成功弹框
+            this.secretDialogVisible = true;
+            
+            // 重新加载应用列表
+            this.loadAppList();
+          } else {
+            this.$message.error(response.message || '重置密钥失败');
+          }
+        }
+      });
     },
     
     // 分页大小变化
     handleSizeChange(val) {
       console.log(`每页 ${val} 条`);
       this.pageSize = val;
-      // 这里可以添加分页逻辑
+      this.loadAppList();
     },
     
     // 当前页码变化
     handleCurrentChange(val) {
       console.log(`当前页: ${val}`);
       this.currentPage = val;
-      // 这里可以添加分页逻辑
+      this.loadAppList();
     }
   }
 }
@@ -423,7 +312,7 @@ export default {
 .app-auth-manage {
   padding: 0;
   background-color: transparent;
-  min-height: 100vh;
+  height: 100%;
 }
 
 /* 搜索栏 */
@@ -467,7 +356,6 @@ export default {
 .app-id-container {
   display: flex;
   align-items: center;
-  gap: 8px;
 }
 
 /* 应用ID文本 */
@@ -478,17 +366,6 @@ export default {
   white-space: nowrap;
 }
 
-/* 复制按钮 */
-.copy-btn {
-  color: #409EFF;
-  font-size: 14px;
-  padding: 0 4px;
-}
-
-.copy-btn:hover {
-  color: #66B1FF;
-}
-
 /* 操作按钮 */
 .action-btn {
   color: #409EFF;
@@ -510,8 +387,9 @@ export default {
   display: flex;
   justify-content: flex-end;
   align-items: center;
-  padding: 0 20px;
-  border-top: 1px solid #ebeef5;
+}
+.pagination .el-pagination{
+  padding: 10px 20px;
 }
 
 /* 重置密钥确认弹框样式 */
@@ -544,35 +422,7 @@ export default {
   line-height: 1.5;
 }
 
-/* 延期确认弹框样式 */
-.extend-confirm {
-  display: flex;
-  padding: 20px 0;
-}
-
-/* 无需延期提示样式 */
-.no-extend {
-  display: flex;
-  padding: 20px 0;
-}
 
-.warning-icon {
-  color: #F56C6C;
-  font-size: 24px;
-  margin-right: 16px;
-  flex-shrink: 0;
-  margin-top: 4px;
-}
-
-.warning-content {
-  flex: 1;
-}
-
-.warning-content p {
-  margin: 8px 0;
-  font-size: 14px;
-  color: #606266;
-}
 
 /* 密钥生成成功弹框样式 */
 .secret-success {

+ 22 - 76
web/src/views/content/aiTagging/taggingLogs/components/LogDetailDrawer.vue

@@ -10,73 +10,41 @@
     <div class="log-detail-container">
       <!-- 基本信息 -->
       <div class="detail-section">
-        <h3 class="section-title">基本信息</h3>
-        <div class="info-grid">
-          <div class="info-item">
-            <span class="label">调用时间:</span>
-            <span class="value">{{ logDetail.callTime }}</span>
-          </div>
-          <div class="info-item">
-            <span class="label">响应时间:</span>
-            <span class="value">{{ logDetail.responseTime }}</span>
-          </div>
-          <div class="info-item">
-            <span class="label">请求ID:</span>
-            <span class="value">{{ logDetail.requestId }}</span>
-          </div>
-          <div class="info-item">
-            <span class="label">接口名称:</span>
-            <span class="value">{{ logDetail.apiName }}</span>
-          </div>
-          <div class="info-item">
-            <span class="label">状态结果:</span>
-            <el-tag :type="getStatusType(logDetail.status)">{{ logDetail.status }}</el-tag>
-          </div>
-          <div class="info-item">
-            <span class="label">调用IP:</span>
-            <span class="value">{{ logDetail.clientIp }}</span>
-          </div>
+        <div class="info-item">
+          <span class="label">用户名称:</span>
+          <span class="value">{{ logDetail.userName }}</span>
         </div>
-      </div>
-
-      <!-- 请求信息 -->
-      <div class="detail-section">
-        <h3 class="section-title">请求信息</h3>
         <div class="info-item">
-          <span class="label">请求方法:</span>
-          <span class="value">{{ logDetail.requestMethod }} {{ logDetail.requestUrl }}</span>
+          <span class="label">用户ID:</span>
+          <span class="value">{{ logDetail.userId }}</span>
         </div>
         <div class="info-item">
-          <span class="label">请求头:</span>
-          <pre class="code-block">{{ formatJson(logDetail.requestHeaders) }}</pre>
+          <span class="label">操作类型:</span>
+          <span class="value">{{ logDetail.operationType }}</span>
         </div>
         <div class="info-item">
-          <span class="label">请求体:</span>
-          <pre class="code-block">{{ formatJson(logDetail.requestBody) }}</pre>
+          <span class="label">操作时间:</span>
+          <span class="value">{{ logDetail.operationTime }}</span>
         </div>
       </div>
 
-      <!-- 响应信息 -->
+      <!-- 输入信息 -->
       <div class="detail-section">
-        <h3 class="section-title">响应信息</h3>
         <div class="info-item">
-          <span class="label">响应状态:</span>
-          <span class="value">{{ logDetail.responseStatus }}</span>
-        </div>
-        <div class="info-item">
-          <span class="label">响应头:</span>
-          <pre class="code-block">{{ formatJson(logDetail.responseHeaders) }}</pre>
+          <span class="label">输入:</span>
+          <pre class="code-block">{{ logDetail.input }}</pre>
         </div>
+      </div>
+
+      <!-- 输出信息 -->
+      <div class="detail-section">
         <div class="info-item">
-          <span class="label">响应体:</span>
-          <pre class="code-block">{{ formatJson(logDetail.responseBody) }}</pre>
+          <span class="label">输出:</span>
+          <pre class="code-block">{{ logDetail.output }}</pre>
         </div>
       </div>
 
-      <!-- 底部操作 -->
-      <div class="detail-footer">
-        <el-button type="primary" @click="handleExport">导出</el-button>
-      </div>
+
     </div>
   </el-drawer>
 </template>
@@ -100,25 +68,7 @@ export default {
       this.$emit('update:visible', false)
     },
     
-    // 处理导出
-    handleExport() {
-      console.log('导出日志详情:', this.logDetail.requestId)
-      this.$message.success('导出成功')
-    },
-    
-    // 根据状态获取标签类型
-    getStatusType(status) {
-      switch (status) {
-        case '成功':
-          return 'success'
-        case '失败':
-          return 'danger'
-        case '响应中':
-          return 'warning'
-        default:
-          return ''
-      }
-    },
+
     
     // 格式化JSON
     formatJson(json) {
@@ -141,13 +91,9 @@ export default {
 }
 
 .detail-section {
-  margin-bottom: 30px;
+  /* margin-bottom: 30px;
   padding-bottom: 20px;
-  border-bottom: 1px solid #f0f0f0;
-}
-
-.detail-section:last-child {
-  border-bottom: none;
+  border-bottom: 1px solid #f0f0f0; */
 }
 
 .section-title {

+ 105 - 170
web/src/views/content/aiTagging/taggingLogs/index.vue

@@ -2,16 +2,7 @@
   <div class="tagging-logs-container">
     <!-- 顶部搜索栏 -->
     <div class="search-bar">
-      <el-form :inline="true" :model="searchForm" class="search-form">
-        <el-form-item label="请求ID:">
-          <el-input v-model="searchForm.requestId" placeholder="请输入" style="width: 200px;"></el-input>
-        </el-form-item>
-        <el-form-item label="贷款申请编号:">
-          <el-input v-model="searchForm.loanApplyNo" placeholder="请输入" style="width: 200px;"></el-input>
-        </el-form-item>
-        <el-form-item label="合同编号:">
-          <el-input v-model="searchForm.contractNo" placeholder="请输入" style="width: 200px;"></el-input>
-        </el-form-item>
+      <el-form :inline="true" :model="searchForm" label-width="110px">
         <el-form-item label="调用时间:">
           <el-date-picker
             v-model="searchForm.callTime"
@@ -22,30 +13,11 @@
             style="width: 240px;"
           ></el-date-picker>
         </el-form-item>
-        <el-form-item label="接口:">
-          <el-select v-model="searchForm.apiType" placeholder="全部" style="width: 150px;">
-            <el-option label="全部" value=""></el-option>
-            <el-option label="getCustomerTags" value="getCustomerTags"></el-option>
-            <el-option label="queryCustomerTags" value="queryCustomerTags"></el-option>
-            <el-option label="feedbackTagsResult" value="feedbackTagsResult"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="状态:">
-          <el-select v-model="searchForm.status" placeholder="全部" style="width: 150px;">
-            <el-option label="全部" value=""></el-option>
-            <el-option label="成功" value="success"></el-option>
-            <el-option label="失败" value="failed"></el-option>
-            <el-option label="响应中" value="pending"></el-option>
-          </el-select>
-        </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="handleSearch">查询</el-button>
         </el-form-item>
         <el-form-item>
-          <el-button @click="handleReset">重置</el-button>
-        </el-form-item>
-        <el-form-item>
-          <el-button @click="handleRefresh">刷新</el-button>
+          <el-button type="" plain @click="handleReset">重置</el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -55,16 +27,10 @@
       <!-- 数据表格 -->
       <div class="table-container">
         <el-table :data="logs" style="width: 100%">
-          <el-table-column prop="requestId" label="请求ID" min-width="180"></el-table-column>
-          <el-table-column prop="loanApplyNo" label="贷款申请编号" min-width="150"></el-table-column>
-          <el-table-column prop="contractNo" label="合同编号" min-width="150"></el-table-column>
-          <el-table-column prop="apiName" label="接口" min-width="150"></el-table-column>
-          <el-table-column prop="callTime" label="调用时间" min-width="180"></el-table-column>
-          <el-table-column prop="status" label="状态" min-width="100">
-            <template slot-scope="scope">
-              <el-tag :type="getStatusType(scope.row.status)">{{ scope.row.status }}</el-tag>
-            </template>
-          </el-table-column>
+          <el-table-column prop="userName" label="用户名称" min-width="120"></el-table-column>
+          <el-table-column prop="userId" label="用户id" min-width="180"></el-table-column>
+          <el-table-column prop="operationType" label="操作类型" min-width="120"></el-table-column>
+          <el-table-column prop="operationTime" label="操作时间" min-width="180"></el-table-column>
           <el-table-column prop="action" label="操作" min-width="100">
             <template slot-scope="scope">
               <el-button type="text" @click="handleViewDetail(scope.row)">查看详情</el-button>
@@ -74,7 +40,7 @@
       </div>
 
       <!-- 分页控件 -->
-      <div class="pagination-container">
+      <div class="pagination">
         <el-pagination
           :current-page="currentPage"
           :page-size="pageSize"
@@ -108,95 +74,90 @@ export default {
     return {
       // 搜索表单
       searchForm: {
-        requestId: '',
-        loanApplyNo: '',
-        contractNo: '',
-        callTime: '',
-        apiType: '',
-        status: ''
+        callTime: ''
       },
       
       // 日志列表
       logs: [
         {
-          requestId: 'API-20260203-87654',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'ks139914512123',
+          operationType: '用户登录',
+          operationTime: '2026-02-03 15:39:35',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87653',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'queryCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '修改标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87652',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'feedbackTagsResult',
-          callTime: '2023-01-01 11:11:11',
-          status: '失败'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '删除标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87651',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '响应中'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '新建标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87650',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '标签查询',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87649',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '用户登录',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87648',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '修改标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87647',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '删除标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87646',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '新建标签',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         },
         {
-          requestId: 'API-20260203-87645',
-          loanApplyNo: 'DKSQ20260203001',
-          contractNo: 'CUST20260203001',
-          apiName: 'getCustomerTags',
-          callTime: '2023-01-01 11:11:11',
-          status: '成功'
+          userName: '张三',
+          userId: 'DKSQ20260203001',
+          operationType: '标签查询',
+          operationTime: '2023-01-01 11:11:11',
+          input: '{"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}',
+          output: '{"Success"}'
         }
       ],
       
@@ -211,19 +172,7 @@ export default {
     }
   },
   methods: {
-    // 根据状态获取标签类型
-    getStatusType(status) {
-      switch (status) {
-        case '成功':
-          return 'success'
-        case '失败':
-          return 'danger'
-        case '响应中':
-          return 'warning'
-        default:
-          return ''
-      }
-    },
+
     
     // 搜索日志
     handleSearch() {
@@ -235,12 +184,7 @@ export default {
     // 重置搜索
     handleReset() {
       this.searchForm = {
-        requestId: '',
-        loanApplyNo: '',
-        contractNo: '',
-        callTime: '',
-        apiType: '',
-        status: ''
+        callTime: ''
       }
       console.log('重置搜索')
     },
@@ -255,41 +199,14 @@ export default {
     // 查看详情
     handleViewDetail(row) {
       console.log('查看详情:', row)
-      // 模拟加载日志详情数据
+      // 构建与LogDetailDrawer.vue期望结构一致的详情数据
       this.currentLogDetail = {
-        callTime: '2026-02-03 15:30:45',
-        responseTime: '124ms',
-        requestId: row.requestId,
-        apiName: row.apiName,
-        status: row.status,
-        clientIp: '192.168.1.45',
-        requestMethod: 'POST',
-        requestUrl: '/api/user/login',
-        requestHeaders: {
-          'Content-Type': 'application/json',
-          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
-        },
-        requestBody: {
-          username: 'testuser',
-          password: '********'
-        },
-        responseStatus: 'HTTP 200',
-        responseHeaders: {
-          'Content-Type': 'application/json',
-          'X-Request-Id': row.requestId
-        },
-        responseBody: {
-          code: 0,
-          message: '登录成功',
-          data: {
-            token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
-            userInfo: {
-              id: '1001',
-              username: 'testuser',
-              role: 'user'
-            }
-          }
-        }
+        userName: row.userName,
+        userId: row.userId,
+        operationType: row.operationType,
+        operationTime: row.operationTime,
+        input: row.input,
+        output: row.output
       }
       this.drawerVisible = true
     },
@@ -315,7 +232,7 @@ export default {
 .tagging-logs-container {
   padding: 0;
   background-color: transparent;
-  min-height: 100vh;
+  height: 100%;
 }
 
 /* 搜索栏样式 */
@@ -327,11 +244,28 @@ export default {
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 }
 
-.search-form {
+.search-row {
   display: flex;
-  flex-wrap: wrap;
   align-items: center;
-  gap: 16px;
+  margin-bottom: 16px;
+}
+
+.search-row:last-child {
+  margin-bottom: 0;
+  justify-content: flex-end;
+}
+
+.search-row .el-form-item {
+  margin-right: 32px;
+  margin-bottom: 0;
+}
+
+.search-row .el-form-item:last-child {
+  margin-right: 0;
+}
+
+.search-row .el-button {
+  margin-left: 12px;
 }
 
 /* 表格和分页容器 */
@@ -348,13 +282,14 @@ export default {
   padding: 20px 20px 10px;
 }
 
-/* 分页控件 */
-.pagination-container {
+/* 分页 */
+.pagination {
   display: flex;
   justify-content: flex-end;
   align-items: center;
-  padding: 0 20px 20px;
-  /* border-top: 1px solid #f0f0f0; */
+}
+.pagination .el-pagination{
+  padding: 10px 20px;
 }
 
 /* 响应式设计 */
@@ -373,11 +308,11 @@ export default {
 
   .search-bar,
   .table-container,
-  .pagination-container {
+  .pagination {
     padding: 10px;
   }
 
-  .pagination-container {
+  .pagination {
     flex-direction: column;
     align-items: flex-start;
     gap: 16px;

+ 6 - 9
web/src/views/content/aiTagging/taggingResults/components/Detail.vue

@@ -42,7 +42,7 @@
     </div>
 
     <!-- 分页控件 -->
-    <div class="pagination-container">
+    <div class="pagination">
       <el-pagination
         :current-page="currentDetailPage"
         :page-size="detailPageSize"
@@ -165,17 +165,14 @@ export default {
   margin-bottom: 8px;
 }
 
-/* 分页控件 */
-.pagination-container {
+/* 分页 */
+.pagination {
   display: flex;
   justify-content: flex-end;
   align-items: center;
-  padding:0 20px 20px;
 }
-
-.total-count {
-  font-size: 14px;
-  color: #606266;
+.pagination .el-pagination{
+  padding: 10px 20px;
 }
 
 /* 响应式设计 */
@@ -186,7 +183,7 @@ export default {
     gap: 12px;
   }
 
-  .pagination-container {
+  .pagination {
     flex-direction: column;
     align-items: flex-start;
     gap: 16px;

+ 1 - 1
web/src/views/content/aiTagging/taggingResults/components/Overview.vue

@@ -243,7 +243,7 @@ export default {
   grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
   gap: 20px;
   padding: 20px;
-  margin-bottom: 20px;
+  /* margin-bottom: 20px; */
 }
 
 /* 统计指标项 */

+ 3 - 3
web/src/views/content/aiTagging/taggingResults/index.vue

@@ -272,9 +272,9 @@ export default {
 
 <style scoped>
 .tagging-results-container {
-  padding: 20px;
-  background-color: #f0f2f5;
-  min-height: 100vh;
+  padding: 0;
+  background-color: transparent;
+  height: 100%;
 }
 
 /* 搜索栏样式 */

+ 1 - 1
web/src/views/content/aiTagging/taggingSystemManage/TagSystemEdit.vue

@@ -193,7 +193,7 @@ export default {
 .edit-page {
   padding: 0;
   background-color: transparent;
-  min-height: 100vh;
+  height: 100%;
 }
 
 .edit-header {

+ 28 - 4
web/src/views/content/aiTagging/taggingSystemManage/components/CreateTagSystem.vue

@@ -117,11 +117,35 @@ export default {
         if (valid) {
           console.log(this.isEditMode ? '编辑标签体系:' : '创建标签体系:', this.form);
           
-          // 触发父组件的对应事件
-          this.$emit(this.isEditMode ? 'edit' : 'create', this.form);
+          // 构建请求参数
+          const requestData = this.isEditMode ? {
+            id: this.form.id,
+            categoryNm: this.form.name,
+            categoryDesc: this.form.description
+          } : {
+            categoryNm: this.form.name,
+            categoryDesc: this.form.description
+          };
           
-          // 重置表单
-          this.resetForm();
+          // 调用后端接口
+          yufp.service.request({
+            url: backend.tagServer + (this.isEditMode ? "/api/aitag-tagcategory/update" : "/api/aitag-tagcategory/create"),
+            method: 'post',
+            data: requestData,
+            callback: (code, error, response) => {
+              if (response.code == '0') {
+                this.$message.success(this.isEditMode ? '标签体系编辑成功' : '标签体系创建成功');
+                
+                // 重置表单
+                this.resetForm();
+                
+                // 触发父组件的成功事件
+                this.$emit('success');
+              } else {
+                this.$message.error(response.message || (this.isEditMode ? '编辑标签体系失败' : '创建标签体系失败'));
+              }
+            }
+          });
         } else {
           console.log('表单验证失败');
           return false;

+ 107 - 63
web/src/views/content/aiTagging/taggingSystemManage/index.vue

@@ -3,7 +3,7 @@
     <!-- 顶部搜索栏 -->
     <div class="search-bar">
       <el-form :inline="true" :model="searchForm" class="search-form">
-        <el-form-item label="123123标签体系名称:">
+        <el-form-item label="标签体系名称:">
           <el-input v-model="searchForm.name" placeholder="请输入" style="width: 300px;"></el-input>
         </el-form-item>
         <el-form-item>
@@ -61,13 +61,12 @@
     </div>
 
     <!-- 分页控件 -->
-    <div class="pagination-container">
-      <div class="total-count">共 {{ totalCount }} 条数据</div>
+    <div class="pagination">
       <el-pagination
         :current-page="currentPage"
         :page-size="pageSize"
         :page-sizes="[10, 20, 50]"
-        layout="prev, pager, next"
+        layout="total, sizes, prev, pager, next, jumper"
         :total="totalCount"
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
@@ -77,7 +76,7 @@
     <!-- 创建标签体系对话框 -->
     <CreateTagSystem
       :visible="createDialogVisible"
-      @create="handleTagSystemCreate"
+      @success="handleCreateSuccess"
       @cancel="handleCreateCancel"
     />
     
@@ -86,7 +85,7 @@
       :visible="editDialogVisible"
       :is-edit-mode="true"
       :edit-data="currentEditSystem"
-      @edit="handleTagSystemEdit"
+      @success="handleEditSuccess"
       @cancel="handleEditCancel"
     />
   </div>
@@ -165,17 +164,43 @@ export default {
       currentEditSystem: {}
     }
   },
+  mounted() {
+    // 默认加载标签体系列表
+    this.loadTagSystems();
+  },
   methods: {
+    // 加载标签体系列表
+    loadTagSystems() {
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-category/list",
+        method: 'get',
+        data: {
+          name: this.searchForm.name,
+          page: this.currentPage,
+          pageSize: this.pageSize
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.tagSystems = response.data || [];
+            this.totalCount = response.total || 0;
+          } else {
+            this.$message.error(response.message || '获取标签体系列表失败');
+          }
+        }
+      });
+    },
     // 搜索标签体系
     handleSearch() {
       console.log('搜索标签体系:', this.searchForm.name)
-      // 这里可以调用后端接口进行搜索
-      this.$message.success('搜索成功')
+      this.currentPage = 1;
+      this.loadTagSystems();
     },
 
     // 重置搜索
     handleReset() {
       this.searchForm.name = ''
+      this.currentPage = 1;
+      this.loadTagSystems();
       console.log('重置搜索')
     },
 
@@ -185,29 +210,11 @@ export default {
       this.createDialogVisible = true
     },
     
-    // 处理标签体系创建
-    handleTagSystemCreate(formData) {
-      console.log('创建标签体系:', formData)
-      
-      // 模拟创建标签体系的逻辑
-      const newSystem = {
-        id: Date.now(),
-        name: formData.name,
-        description: formData.description,
-        status: 'enabled',
-        tagCount: 0,
-        createDate: new Date().toISOString().split('T')[0]
-      }
-      
-      // 添加到标签体系列表
-      this.tagSystems.unshift(newSystem)
-      this.totalCount++
-      
-      // 显示成功消息
-      this.$message.success('标签体系创建成功')
-      
-      // 关闭对话框
+    // 处理创建成功
+    handleCreateSuccess() {
+      console.log('标签体系创建成功')
       this.createDialogVisible = false
+      this.loadTagSystems();
     },
     
     // 处理创建取消
@@ -223,19 +230,11 @@ export default {
       this.editDialogVisible = true
     },
     
-    // 处理标签体系编辑
-    handleTagSystemEdit(formData) {
-      console.log('编辑标签体系:', formData)
-      
-      // 找到并更新标签体系
-      const index = this.tagSystems.findIndex(system => system.id === formData.id)
-      if (index !== -1) {
-        this.tagSystems[index] = { ...formData }
-        this.$message.success('标签体系编辑成功')
-      }
-      
-      // 关闭对话框
+    // 处理编辑成功
+    handleEditSuccess() {
+      console.log('标签体系编辑成功')
       this.editDialogVisible = false
+      this.loadTagSystems();
     },
     
     // 处理编辑取消
@@ -264,15 +263,49 @@ export default {
     // 启用标签体系
     handleEnableTagSystem(system) {
       console.log('启用标签体系:', system.name)
-      system.status = 'enabled'
-      this.$message.success(`启用标签体系: ${system.name}`)
+      
+      // 调用后端接口启用标签体系
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-tagcategory/enable",
+        method: 'post',
+        data: {
+          id: system.id
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.$message.success(`启用标签体系: ${system.name}`)
+            
+            // 重新加载标签体系列表
+            this.loadTagSystems();
+          } else {
+            this.$message.error(response.message || '启用标签体系失败');
+          }
+        }
+      });
     },
 
     // 停用标签体系
     handleDisableTagSystem(system) {
       console.log('停用标签体系:', system.name)
-      system.status = 'disabled'
-      this.$message.success(`停用标签体系: ${system.name}`)
+      
+      // 调用后端接口停用标签体系
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-tagcategory/disable",
+        method: 'post',
+        data: {
+          id: system.id
+        },
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.$message.success(`停用标签体系: ${system.name}`)
+            
+            // 重新加载标签体系列表
+            this.loadTagSystems();
+          } else {
+            this.$message.error(response.message || '停用标签体系失败');
+          }
+        }
+      });
     },
 
     // 删除标签体系
@@ -283,8 +316,25 @@ export default {
         type: 'warning'
       }).then(() => {
         console.log('删除标签体系:', system.name)
-        // 这里可以调用后端接口删除标签体系
-        this.$message.success(`删除标签体系: ${system.name}`)
+        
+        // 调用后端接口删除标签体系
+        yufp.service.request({
+          url: backend.tagServer + "/api/aitag-tagcategory/delete",
+          method: 'post',
+          data: {
+            id: system.id
+          },
+          callback: (code, error, response) => {
+            if (response.code == '0') {
+              this.$message.success(`删除标签体系: ${system.name}`)
+              
+              // 重新加载标签体系列表
+              this.loadTagSystems();
+            } else {
+              this.$message.error(response.message || '删除标签体系失败');
+            }
+          }
+        });
       }).catch(() => {
         console.log('取消删除标签体系:', system.name)
       })
@@ -306,14 +356,14 @@ export default {
     handleSizeChange(size) {
       console.log('每页条数:', size)
       this.pageSize = size
-      // 这里可以重新加载数据
+      this.loadTagSystems();
     },
 
     // 当前页码变化
     handleCurrentChange(current) {
       console.log('当前页码:', current)
       this.currentPage = current
-      // 这里可以重新加载数据
+      this.loadTagSystems();
     }
   }
 }
@@ -323,7 +373,7 @@ export default {
 .tag-system-container {
   padding: 0;
   background-color: transparent;
-  min-height: 100vh;
+  height: 100%;
 }
 
 /* 搜索栏样式 */
@@ -469,20 +519,14 @@ export default {
   padding-top: 16px;
 }
 
-/* 分页控件 */
-.pagination-container {
+/* 分页 */
+.pagination {
   display: flex;
-  justify-content: space-between;
+  justify-content: flex-end;
   align-items: center;
-  background-color: #fff;
-  padding: 20px;
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 }
-
-.total-count {
-  font-size: 14px;
-  color: #606266;
+.pagination .el-pagination{
+  padding: 10px 20px;
 }
 
 /* 响应式设计 */
@@ -508,7 +552,7 @@ export default {
     gap: 12px;
   }
 
-  .pagination-container {
+  .pagination {
     flex-direction: column;
     align-items: flex-start;
     gap: 16px;

+ 208 - 0
web/src/views/content/aiTagging/utils/common.js

@@ -0,0 +1,208 @@
+/**
+ * 公共工具方法
+ */
+
+/**
+ * 复制文本到剪贴板
+ * @param {string} text - 要复制的文本
+ * @param {Object} vueInstance - Vue实例,用于显示消息提示
+ * @returns {Promise<boolean>} - 复制是否成功
+ */
+export function copyToClipboard(text, vueInstance = null) {
+  return new Promise((resolve, reject) => {
+    // 确保文本存在
+    if (!text) {
+      if (vueInstance) {
+        vueInstance.$message.error('复制内容为空');
+      }
+      reject(new Error('复制内容为空'));
+      return;
+    }
+
+    const copyText = String(text);
+    console.log('尝试复制文本:', copyText);
+
+    // 尝试使用现代Clipboard API
+    if (navigator.clipboard) {
+      console.log('使用现代Clipboard API');
+      navigator.clipboard.writeText(copyText)
+        .then(() => {
+          console.log('复制成功');
+          if (vueInstance) {
+            vueInstance.$message.success('复制成功');
+          }
+          resolve(true);
+        })
+        .catch(err => {
+          console.error('Clipboard API 错误:', err);
+          // 降级到传统方法
+          fallbackCopyTextToClipboard(copyText, vueInstance, resolve, reject);
+        });
+    } else {
+      // 直接使用传统方法
+      fallbackCopyTextToClipboard(copyText, vueInstance, resolve, reject);
+    }
+  });
+}
+
+/**
+ * 传统复制方法(降级方案)
+ * @param {string} text - 要复制的文本
+ * @param {Object} vueInstance - Vue实例,用于显示消息提示
+ * @param {Function} resolve - Promise resolve函数
+ * @param {Function} reject - Promise reject函数
+ */
+function fallbackCopyTextToClipboard(text, vueInstance, resolve, reject) {
+  console.log('使用传统复制方法');
+
+  // 创建textarea元素
+  const textArea = document.createElement('textarea');
+  textArea.value = text;
+
+  // 确保textarea不在可视区域内
+  textArea.style.position = 'fixed';
+  textArea.style.left = '-999999px';
+  textArea.style.top = '-999999px';
+  textArea.style.width = '2em';
+  textArea.style.height = '2em';
+  textArea.style.padding = '0';
+  textArea.style.border = 'none';
+  textArea.style.outline = 'none';
+  textArea.style.boxShadow = 'none';
+  textArea.style.background = 'transparent';
+
+  // 添加到DOM
+  document.body.appendChild(textArea);
+
+  try {
+    // 选择文本
+    textArea.focus();
+    textArea.select();
+
+    // 执行复制命令
+    const successful = document.execCommand('copy');
+    console.log('传统方法复制结果:', successful);
+
+    if (successful) {
+      if (vueInstance) {
+        vueInstance.$message.success('复制成功');
+      }
+      resolve(true);
+    } else {
+      if (vueInstance) {
+        vueInstance.$message.error('复制失败,请手动复制');
+      }
+      reject(new Error('复制失败'));
+    }
+  } catch (err) {
+    console.error('复制失败:', err);
+    if (vueInstance) {
+      vueInstance.$message.error('复制失败,请手动复制');
+    }
+    reject(err);
+  } finally {
+    // 清理临时元素
+    setTimeout(() => {
+      document.body.removeChild(textArea);
+    }, 100);
+  }
+}
+
+/**
+ * 显示手动复制界面(最终降级方案)
+ * @param {string} text - 要复制的文本
+ * @param {Object} vueInstance - Vue实例,用于显示消息提示
+ */
+export function showManualCopyDialog(text, vueInstance = null) {
+  if (!text) {
+    if (vueInstance) {
+      vueInstance.$message.error('复制内容为空');
+    }
+    return;
+  }
+
+  console.log('显示手动复制界面');
+
+  // 创建一个临时的div来显示文本,让用户手动复制
+  const tempDiv = document.createElement('div');
+  tempDiv.style.position = 'fixed';
+  tempDiv.style.top = '50%';
+  tempDiv.style.left = '50%';
+  tempDiv.style.transform = 'translate(-50%, -50%)';
+  tempDiv.style.background = 'white';
+  tempDiv.style.border = '1px solid #ddd';
+  tempDiv.style.borderRadius = '8px';
+  tempDiv.style.padding = '20px';
+  tempDiv.style.boxShadow = '0 2px 12px 0 rgba(0, 0, 0, 0.1)';
+  tempDiv.style.zIndex = '9999';
+  tempDiv.style.minWidth = '300px';
+
+  tempDiv.innerHTML = `
+    <div style="margin-bottom: 15px;">
+      <p style="margin: 0 0 10px 0; font-weight: bold;">复制内容</p>
+      <input type="text" value="${text}" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: monospace;" id="temp-copy-input">
+    </div>
+    <div style="display: flex; gap: 10px; justify-content: flex-end;">
+      <button style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; background: white; cursor: pointer;" id="temp-copy-btn">复制</button>
+      <button style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; background: white; cursor: pointer;" id="temp-close-btn">关闭</button>
+    </div>
+  `;
+
+  document.body.appendChild(tempDiv);
+
+  // 获取元素
+  const input = document.getElementById('temp-copy-input');
+  const copyBtn = document.getElementById('temp-copy-btn');
+  const closeBtn = document.getElementById('temp-close-btn');
+
+  // 自动选择文本
+  input.select();
+
+  // 复制按钮点击事件
+  copyBtn.addEventListener('click', () => {
+    input.select();
+    try {
+      const successful = document.execCommand('copy');
+      if (successful) {
+        if (vueInstance) {
+          vueInstance.$message.success('复制成功');
+        }
+      } else {
+        if (vueInstance) {
+          vueInstance.$message.error('复制失败,请手动复制');
+        }
+      }
+    } catch (err) {
+      console.error('复制失败:', err);
+      if (vueInstance) {
+        vueInstance.$message.error('复制失败,请手动复制');
+      }
+    }
+  });
+
+  // 关闭按钮点击事件
+  closeBtn.addEventListener('click', () => {
+    document.body.removeChild(tempDiv);
+  });
+
+  // 点击外部关闭
+  tempDiv.addEventListener('click', (e) => {
+    if (e.target === tempDiv) {
+      document.body.removeChild(tempDiv);
+    }
+  });
+}
+
+/**
+ * 生成随机字符串
+ * @param {number} length - 字符串长度
+ * @returns {string} - 随机字符串
+ */
+export function generateRandomString(length = 20) {
+  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+  let result = '';
+  for (let i = 0; i < length; i++) {
+    result += chars.charAt(Math.floor(Math.random() * chars.length));
+  }
+  return result;
+}

+ 1 - 2
web/src/views/content/workstation/workstation/workstation.vue

@@ -486,13 +486,12 @@
 			init(){
 				this.loading = true;
 				yufp.service.request({
-					url: this.url.getUserQuickMenu,
+					url: backend.tagServer + "/api/tab-app/list",
 					method: 'get',
 					data:{
 						sysId:yufp.session.logicSys.id,
 					},
 					callback: (code, error, response) => {
-						this.loading = false;
 						if (response.code == '0') {
 							this.list = response.data;
 						} else {