Procházet zdrojové kódy

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

jianggs před 3 týdny
rodič
revize
7b90451017
49 změnil soubory, kde provedl 1242 přidání a 630 odebrání
  1. 4 4
      server/pom.xml
  2. 15 20
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/aop/ApiLogAspect.java
  3. 23 2
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/config/DataDictionary.java
  4. 56 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/config/GlobalExceptionHandler.java
  5. 20 30
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagInfoController.java
  6. 15 15
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagLogController.java
  7. 3 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/FastApiController.java
  8. 8 2
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/TagImportDto.java
  9. 175 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/TagLogDto.java
  10. 51 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/TagResultDto.java
  11. 2 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/fastapidto/AiTaggingFeedbackRequestDto.java
  12. 15 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/fastapidto/AiTaggingRequestDto.java
  13. 13 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagCategory.java
  14. 0 55
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagDailyAggEntity.java
  15. 30 3
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfoEntity.java
  16. 5 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfoVersionEntity.java
  17. 7 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagLogEntity.java
  18. 10 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/AitagTagCategoryVo.java
  19. 36 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileNode.java
  20. 26 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileReqVo.java
  21. 24 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileResVo.java
  22. 4 4
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/ExportDataVo.java
  23. 31 2
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/IconResVo.java
  24. 13 28
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/TagNodeVo.java
  25. 76 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/TagSaveVo.java
  26. 81 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/TagUpdateVo.java
  27. 54 54
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/fastapivo/AiTaggingQueryResponseVo.java
  28. 0 6
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagCategoryMapper.java
  29. 0 24
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagDailyAggDao.java
  30. 4 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagLogDao.java
  31. 6 6
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagApiLogService.java
  32. 1 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagInfoService.java
  33. 3 2
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagLogService.java
  34. 0 121
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/DistributedScheduledTask.java
  35. 26 1
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/TagImportListener.java
  36. 21 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/esb/ESBService.java
  37. 13 85
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagApiLogServiceImpl.java
  38. 14 3
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagCategoryServiceImpl.java
  39. 66 15
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagInfoServiceImpl.java
  40. 116 49
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagLogServiceImpl.java
  41. 67 4
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/FastApiServiceImpl.java
  42. 26 0
      server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/util/SessionCommonUtil.java
  43. 0 8
      server/yusp-tagging-core/src/main/resources/mapper/AitagApiLogMapper.xml
  44. 5 17
      server/yusp-tagging-core/src/main/resources/mapper/AitagTagCategoryMapper.xml
  45. 0 37
      server/yusp-tagging-core/src/main/resources/mapper/AitagTagDailyAggMapper.xml
  46. 2 1
      server/yusp-tagging-core/src/main/resources/mapper/AitagTagInfoMapper.xml
  47. 64 25
      server/yusp-tagging-core/src/main/resources/mapper/AitagTagLogMapper.xml
  48. 11 2
      server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg.properties
  49. binární
      server/yusp-tagging-core/src/main/resources/template/tag_batch_Import_template.xlsx

+ 4 - 4
server/pom.xml

@@ -17,10 +17,10 @@
         <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>
+<!--        <maven.compiler.source>17</maven.compiler.source>-->
+<!--        <maven.compiler.target>17</maven.compiler.target>-->
+<!--        <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>-->
+<!--        <java.version>17</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>

+ 15 - 20
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/aop/ApiLogAspect.java

@@ -3,6 +3,7 @@ package cn.com.yusys.yusp.aop;
 import cn.com.yusys.yusp.annotation.ApiOperationType;
 import cn.com.yusys.yusp.domain.entity.AitagApiLog;
 import cn.com.yusys.yusp.service.AitagApiLogService;
+import cn.com.yusys.yusp.util.SessionCommonUtil;
 import cn.com.yusys.yusp.util.AuthContextUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
@@ -24,18 +25,15 @@ public class ApiLogAspect {
     @Autowired
     private AitagApiLogService apiLogService;
 
-    @Value("${auth.enabled:false}")
-    private boolean authEnabled;
-
     @Around("@annotation(cn.com.yusys.yusp.annotation.ApiOperationType)")
     public Object logApiCall(ProceedingJoinPoint joinPoint) throws Throwable {
         long startTime = System.currentTimeMillis();
 
         AitagApiLog apiLog = new AitagApiLog();
-        apiLog.setId(apiLogService.generateLogId());
+        apiLog.setId(apiLogService.generateUuid());
         apiLog.setCreateDate(new Date());
 
-        // 设置用户信息 - 根据认证状态决定
+        // 设置用户信息 - 通过 SessionCommonUtil 获取
         setUserInfo(apiLog);
 
         // 获取操作类型
@@ -110,30 +108,27 @@ public class ApiLogAspect {
 
     /**
      * 设置用户信息
-     * @param apiLog API日志对象
+     * @param apiLog API 日志对象
      */
     private void setUserInfo(AitagApiLog apiLog) {
-        if (authEnabled) {
-            // 认证启用时,使用当前登录用户信息
-            String currentUserId = AuthContextUtil.getCurrentUser();
-            String currentUserName = AuthContextUtil.getCurrentUserName();
-
-            if (currentUserId != null) {
-                apiLog.setUserId(currentUserId);
-                // 如果有用户名就用用户名,否则用用户ID
-                apiLog.setUserName(currentUserName != null ? currentUserName : currentUserId);
+        try {
+            String userId = SessionCommonUtil.getUserId();
+            String userName = SessionCommonUtil.getUserInfo();
+
+            if (userId != null && !userId.isEmpty()) {
+                apiLog.setUserId(userId);
+                apiLog.setUserName(userName != null ? userName : userId);
             } else {
-                // fallback到默认值
                 apiLog.setUserId("unknown");
                 apiLog.setUserName("unknown");
             }
-        } else {
-            // 认证关闭时,使用固定测试值
-            apiLog.setUserId("testID");
-            apiLog.setUserName("testName");
+        } catch (Exception e) {
+            apiLog.setUserId("unknown");
+            apiLog.setUserName("unknown");
         }
     }
 
+
     /**
      * 隐藏敏感数据
      * @param jsonStr 原始JSON字符串

+ 23 - 2
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/config/DataDictionary.java

@@ -55,8 +55,29 @@ public class DataDictionary {
      *        day0正常数据; 1测试数据
      *
      */
-    public static final String TEST_DATA = "1";
+    public static final Integer TEST_DATA = 1;
+
+    public static final Integer PROD_DATA = 0;
+
+
+    /**
+     *    标签范围 tag_scope:
+     *        day0正常数据; 1测试数据
+     *
+     */
+    public static final Integer DELETE = 1;
+
+    public static final Integer NON_DELETE  = 0;
+
+    /**
+     *    画像系统 打标结果
+     *        1:确认打标;
+     *        0确认不打标
+     *
+     */
+    public static final String CONFIRM_TAGGING = "1";
+
+    public static final String CONFIRM_NOT_TAGGING = "0";
 
-    public static final String PROD_DATA = "0";
 
 }

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

@@ -0,0 +1,56 @@
+package cn.com.yusys.yusp.config;
+
+
+import cn.com.yusys.yusp.commons.exception.BizException;
+import cn.com.yusys.yusp.commons.exception.ExceptionMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.validation.ConstraintViolationException;
+
+/**
+ * 异常处理
+ * @author zky
+ */
+@RestControllerAdvice
+@Order(0)
+@Slf4j
+public class GlobalExceptionHandler {
+
+    @ExceptionHandler(BizException.class)
+    public ExceptionMessage handleValidationExceptions(BizException ex) {
+        ExceptionMessage exceptionMessage = new ExceptionMessage();
+        exceptionMessage.setCode(ex.getErrorCode());
+        exceptionMessage.setMessage(ex.getMessage());
+        return exceptionMessage;
+    }
+
+
+    /**
+     * 处理参数校验异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ExceptionMessage handleValidationException(MethodArgumentNotValidException ex) {
+        ExceptionMessage exceptionMessage = new ExceptionMessage();
+        BindingResult bindingResult = ex.getBindingResult();
+        exceptionMessage.setCode("-1");
+        if (bindingResult.hasErrors() && bindingResult.getFieldError() != null) {
+            FieldError fieldError = bindingResult.getFieldError();
+            // 使用校验注解中定义的message
+            exceptionMessage.setMessage(fieldError.getDefaultMessage());
+        }
+
+        return exceptionMessage;
+    }
+
+
+}

+ 20 - 30
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagInfoController.java

@@ -3,13 +3,9 @@ package cn.com.yusys.yusp.controller;
 import cn.com.yusys.yusp.annotation.ApiOperationType;
 import cn.com.yusys.yusp.commons.exception.BizException;
 import cn.com.yusys.yusp.commons.module.adapter.web.rest.ResultDto;
-import cn.com.yusys.yusp.util.SessionCommonUtil;
 import cn.com.yusys.yusp.domain.dto.TagInfoDto;
 import cn.com.yusys.yusp.domain.entity.AitagTagInfoEntity;
-import cn.com.yusys.yusp.domain.vo.AitagTagInfoQueryVo;
-import cn.com.yusys.yusp.domain.vo.GenerateRegexVo;
-import cn.com.yusys.yusp.domain.vo.TagNodeVo;
-import cn.com.yusys.yusp.domain.vo.VersionRollbackVo;
+import cn.com.yusys.yusp.domain.vo.*;
 import cn.com.yusys.yusp.domain.vo.fastapivo.AiTaggingResponseVo;
 import cn.com.yusys.yusp.service.AitagTagInfoService;
 import com.alibaba.fastjson.JSON;
@@ -17,7 +13,6 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ClassPathResource;
@@ -28,12 +23,10 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.validation.Valid;
 import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -55,6 +48,12 @@ public class AitagTagInfoController {
     @Autowired
     private AitagTagInfoService aitagTagInfoService;
 
+
+    /**
+     * 最大文件上传5M
+     */
+    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;
+
     /**
      * 列表查询
      *
@@ -115,13 +114,14 @@ public class AitagTagInfoController {
     /**
      * 保存
      *
-     * @param aitagTagInfoEntity
+     * @param tagSaveVo
      * @return ResultDto
      */
     @ApiOperationType("标签保存")
     @PostMapping("/save")
-    public ResultDto save(@RequestBody AitagTagInfoEntity aitagTagInfoEntity) {
-        String id = aitagTagInfoService.saveTag(aitagTagInfoEntity);
+    public ResultDto save(@Valid @RequestBody TagSaveVo tagSaveVo) {
+        AitagTagInfoEntity aitagTagInfo = JSON.parseObject(JSON.toJSONString(tagSaveVo), AitagTagInfoEntity.class);
+        String id = aitagTagInfoService.saveTag(aitagTagInfo);
         HashMap<String, Object> body = new HashMap<>();
         String[] ids = {id};
         body.put("tag_ids",ids);
@@ -132,15 +132,16 @@ public class AitagTagInfoController {
     /**
      * 修改
      *
-     * @param aitagTagInfoEntity
+     * @param updateVo
      * @return ResultDto
      */
     @ApiOperationType("标签修改")
     @PostMapping("/update")
-    public ResultDto update(@RequestBody AitagTagInfoEntity aitagTagInfoEntity) {
-        aitagTagInfoService.updateTag(aitagTagInfoEntity);
+    public ResultDto update(@Valid @RequestBody TagUpdateVo updateVo) {
+        AitagTagInfoEntity aitagTagInfo = JSON.parseObject(JSON.toJSONString(updateVo), AitagTagInfoEntity.class);
+        aitagTagInfoService.updateTag(aitagTagInfo);
         HashMap<String, Object> body = new HashMap<>();
-        String[] ids = {aitagTagInfoEntity.getId()};
+        String[] ids = {aitagTagInfo.getId()};
         body.put("tag_ids",ids);
         aitagTagInfoService.callAiTag(body,"/api/aitag/admin/v1/synchronize_tag");
         return ResultDto.success();
@@ -192,20 +193,13 @@ public class AitagTagInfoController {
     @ApiOperationType("批量导入")
     @PostMapping("/batchImport")
     public ResultDto batchImport( @RequestParam("file") MultipartFile file,
-                                  @RequestParam("reviser") String reviser,
                                   @RequestParam("categoryId") String categoryId) throws IOException {
 
         if (file.isEmpty()) {
            throw BizException.of("E002");
         }
-
         // 简单的文件类型检查
-        String fileName = file.getOriginalFilename();
-        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
-            throw BizException.of("E003");
-        }
-        List<String> tagIds = aitagTagInfoService.batchImport(file, reviser, categoryId);
-
+        List<String> tagIds = aitagTagInfoService.batchImport(file,categoryId);
         HashMap<String, Object> body = new HashMap<>();
         body.put("tag_ids",tagIds);
         aitagTagInfoService.callAiTag(body,"/api/aitag/admin/v1/synchronize_tag");
@@ -242,15 +236,11 @@ public class AitagTagInfoController {
     @GetMapping("/downloadFile")
     public ResponseEntity<Resource> downloadFile( ) throws IOException {
 
-        // 1. 构造文件路径并进行规范化,防止路径遍历攻击 (如 ../../恶意路径)
-        Path filePath = Paths.get("template").resolve("tag_batch_Import_template.xlsx").normalize();
-        ClassPathResource resource = new ClassPathResource(filePath.toString());
-
+        ClassPathResource resource = new ClassPathResource("template/tag_batch_Import_template.xlsx");
         // 2. 校验文件是否存在且可读
         if (resource.exists() && resource.isReadable()) {
             // 3. 推断文件的 MediaType (MIME type)
-            String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
-            contentType = Files.probeContentType(filePath);
+            String contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
             // 4. 返回 ResponseEntity,设置响应头
             return ResponseEntity.ok()
                     .contentType(MediaType.parseMediaType(contentType))

+ 15 - 15
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/controller/AitagTagLogController.java

@@ -2,27 +2,25 @@ package cn.com.yusys.yusp.controller;
 
 import cn.com.yusys.yusp.annotation.ApiOperationType;
 import cn.com.yusys.yusp.commons.module.adapter.web.rest.ResultDto;
+import cn.com.yusys.yusp.domain.dto.TagLogDto;
+import cn.com.yusys.yusp.domain.dto.TagResultDto;
 import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
 import cn.com.yusys.yusp.domain.vo.*;
 import cn.com.yusys.yusp.service.AitagTagLogService;
-import cn.com.yusys.yusp.service.DistributedScheduledTask;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import static cn.com.yusys.yusp.config.DataDictionary.FEEDBACK_RESULT_REJECT;
 
@@ -93,8 +91,8 @@ public class AitagTagLogController {
      */
     @ApiOperationType("打标明细")
     @PostMapping("/taggingTransaction")
-    public ResultDto<IPage<AitagTagLogEntity>> taggingTransaction(@RequestBody TaggingTransactionReqVo transactionReqVo) {
-        IPage<AitagTagLogEntity> page = aitagTagLogService.taggingDetails(transactionReqVo);
+    public ResultDto<IPage<TagLogDto>> taggingTransaction(@RequestBody TaggingTransactionReqVo transactionReqVo) {
+        IPage<TagLogDto> page = aitagTagLogService.taggingDetails(transactionReqVo);
         return ResultDto.success(page.getRecords()).total(page.getTotal());
     }
 
@@ -106,8 +104,8 @@ public class AitagTagLogController {
      */
     @ApiOperationType("打标结果详情")
     @GetMapping("/show/{id}")
-    public ResultDto<AitagTagLogEntity> show(@PathVariable("id") String id) {
-        AitagTagLogEntity taggingDetailsResDTO = aitagTagLogService.show(id);
+    public ResultDto<TagLogDto> show(@PathVariable("id") String id) {
+        TagLogDto taggingDetailsResDTO = aitagTagLogService.show(id);
         return ResultDto.success(taggingDetailsResDTO);
     }
 
@@ -150,14 +148,16 @@ public class AitagTagLogController {
             return "";
         }
         result = result.replace("\\", "");
-        List<Map> results = JSONArray.parseArray(result, Map.class);
+        List<TagResultDto> tagResultDtos = JSONArray.parseArray(result, TagResultDto.class);
         StringBuilder stringBuilder = new StringBuilder();
-        for (int i = 0 ;i<results.size();i++){
-            Map resultMap = results.get(i);
-            String tagName = resultMap.getOrDefault("tag_name","").toString();
-            stringBuilder.append(tagName);
-            if(i < (results.size()-1) ){
-                stringBuilder.append(",");
+        for (int i = 0 ;i<tagResultDtos.size();i++){
+            TagResultDto resultMap = tagResultDtos.get(i);
+            if(resultMap.getPassr()){
+                String tagName = resultMap.getTag_name();
+                stringBuilder.append(tagName);
+                if(i < (tagResultDtos.size()-1) ){
+                    stringBuilder.append(",");
+                }
             }
         }
         return stringBuilder.toString();

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

@@ -4,10 +4,12 @@ import cn.com.yusys.yusp.annotation.ApiOperationType;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingFeedbackRequestDto;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingQueryRequestDto;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingRequestDto;
+import cn.com.yusys.yusp.domain.vo.EsbVo.CustomerProfileReqVo;
 import cn.com.yusys.yusp.domain.vo.fastapivo.AiTaggingQueryResponseVo;
 import cn.com.yusys.yusp.domain.vo.fastapivo.AiTaggingResponseVo;
 import cn.com.yusys.yusp.model.Result;
 import cn.com.yusys.yusp.service.FastApiService;
+import cn.com.yusys.yusp.service.esb.ESBService;
 import cn.com.yusys.yusp.util.AuthContextUtil;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
@@ -25,6 +27,7 @@ public class FastApiController {
     @Autowired
     private FastApiService fastApiService;
 
+
     @ApiOperationType("AI 打标")
     @PostMapping("/tagging")
     public Result<AiTaggingResponseVo> tagging(@RequestBody AiTaggingRequestDto request) {

+ 8 - 2
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/TagImportDto.java

@@ -11,15 +11,21 @@ public class TagImportDto {
     @ExcelProperty("标签名称")
     private String tagNm;
 
+    @ExcelProperty("标签编号")
+    private String tagCode;
+
     @ExcelProperty("父标签名称")
     private String parentName;
 
-    @ExcelProperty("标签说明")
+    @ExcelProperty("标签定义")
     private String tagRemark;
 
-    @ExcelProperty("标签关键词规则")
+    @ExcelProperty("关键词")
     private String reg;
 
+    @ExcelProperty("标签判断说明")
+    private String tagPrompt;
+
     /**
      *  运行时填充字段
      */

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

@@ -0,0 +1,175 @@
+package cn.com.yusys.yusp.domain.dto;
+
+import cn.com.yusys.yusp.commons.util.date.DateUtils;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+
+/**
+ *  打标日志实体类
+ * @author zky
+ *
+ */
+
+@Data
+public class TagLogDto {
+
+
+    /**
+     * id
+     **/
+    @ApiModelProperty(value="id")
+    @TableId(type= IdType.UUID)
+    private String id;
+
+    /**
+     * 应用ID
+     **/
+    @ApiModelProperty(value = "应用ID")
+    private String appId;
+
+    /**
+     * 发起时间
+     **/
+    @ApiModelProperty(value = "发起时间")
+    private LocalDateTime insertTime;
+
+    public String getInsertTime() {
+        if(insertTime == null){
+            return "";
+        }
+        return insertTime.format(DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME));
+    }
+
+    /**
+     * 业务属性,贷款编号
+     **/
+    @ApiModelProperty(value = "业务属性,贷款编号")
+    private String businessAttr;
+
+    /**
+     * 输入短语
+     **/
+    @ApiModelProperty(value = "输入短语")
+    private String phrase;
+
+    /**
+     * 附件名称
+     **/
+    @ApiModelProperty(value = "附件名称")
+    private String attachment;
+
+    /**
+     * 附件路径
+     **/
+    @ApiModelProperty(value = "附件路径")
+    private String attachmentUrl;
+
+    /**
+     * 打标结果,JSON
+     [{
+     label:xxx,
+     label_code:xxx,
+     desc:xxx
+     passr: true/false
+     }]
+     **/
+    @ApiModelProperty(value = "打标结果,JSON [{ label:xxx, label_code:xxx, desc:xxx passr: true/false }]")
+    private String result;
+
+    /**
+     * 反馈人ID
+     **/
+    @ApiModelProperty(value = "反馈人ID")
+    private String feedbackUserId;
+
+    /**
+     * 反馈人名字
+     **/
+    @ApiModelProperty(value = "反馈人名字")
+    private String feedbackUserNm;
+
+    /**
+     * 反馈时间
+     **/
+    @ApiModelProperty(value = "反馈时间")
+    private LocalDateTime feedbackTime;
+    public String getFeedbackTime() {
+        if(feedbackTime == null){
+            return "";
+        }
+        return feedbackTime.format(DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME));
+    }
+
+
+
+    /**
+     * 反馈,agree/reject
+     **/
+    @ApiModelProperty(value = "反馈,agree/reject")
+    private String feedback;
+
+    /**
+     * JSON
+     [
+     {
+     label:xxx,
+     label_code:xxx,
+     desc:xxx
+     passr: true/false
+     }
+     ]
+     **/
+    @ApiModelProperty(value = "JSON [ { label:xxx, label_code:xxx, desc:xxx passr: true/false } ]")
+    private String feedbackResult;
+
+    /**
+     * 0:打标执行中;1:打标完成; 2:客户经理已经确认;3,结果已推送
+     **/
+    @ApiModelProperty(value = "0:打标执行中;1:打标完成; 2:客户经理已经确认;3,结果已推送")
+    private Integer state;
+
+    /**
+     * 打标耗时,秒
+     **/
+    @ApiModelProperty(value = "打标耗时,秒")
+    private String consumingTime;
+
+
+    /**
+     * 合同编号
+     **/
+    @ApiModelProperty(value = "合同编号")
+    private String contractNo;
+
+
+
+    /**
+     * 正则过滤结果
+     **/
+    @ApiModelProperty(value = "正则过滤结果")
+    private String regResult;
+
+    /**
+     * 法人机构
+     **/
+    @ApiModelProperty(value = "法人机构")
+    private String feedbackUserOrg;
+
+    /**
+     * 网点
+     **/
+    @ApiModelProperty(value = "网点")
+    private String feedbackUserEndpoint;
+
+    /**
+     * 标签标识 0正常数据; 1测试数据
+     **/
+    @ApiModelProperty(value = "标签标识 0:正常数据; 1:测试数据")
+    private String tagScope;
+}

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

@@ -0,0 +1,51 @@
+package cn.com.yusys.yusp.domain.dto;
+
+
+import lombok.Data;
+
+
+/**
+ * 标签结果 实体类
+ *
+ * @author zky
+ */
+@Data
+public class TagResultDto {
+
+    /**
+     * 唯一标识ID
+     */
+    private String id;
+
+    /**
+     * 标签命中描述
+     */
+    private String desc;
+
+    /**
+     * 打标结果(是否命中)
+     */
+    private Boolean passr;
+
+    /**
+     * 标签编号
+     */
+    private String tag_code;
+
+    /**
+     * 标签名称
+     */
+    private String tag_name;
+
+    /**
+     * 标签路径
+     */
+    private String tag_path;
+
+    /**
+     * 标签类别ID
+     */
+    private String category_id;
+
+
+}

+ 2 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/fastapidto/AiTaggingFeedbackRequestDto.java

@@ -1,6 +1,7 @@
 package cn.com.yusys.yusp.domain.dto.fastapidto;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -26,7 +27,7 @@ public class AiTaggingFeedbackRequestDto {
 
     @JsonProperty("feedback_result")
     @ApiModelProperty(value = "用户反馈的打标结果详情,为空表示同意,不为空表示不同意并输入了反馈", required = false)
-    private String feedbackResult;
+    private JsonNode feedbackResult;
 
     @JsonProperty("contract_no")
     @ApiModelProperty(value = "合同编号", required = false)

+ 15 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/dto/fastapidto/AiTaggingRequestDto.java

@@ -21,4 +21,19 @@ public class AiTaggingRequestDto {
 
     @ApiModelProperty(value = "体系 ID", required = false)
     private String tagCategoryId;
+
+    @ApiModelProperty(value = "用户 ID", required = false)
+    private String userId;
+
+    @ApiModelProperty(value = "用户姓名", required = false)
+    private String userNm;
+
+    @ApiModelProperty(value = "合同编号", required = false)
+    private String contractNo;
+
+    @ApiModelProperty(value = "用户机构", required = false)
+    private String userOrg;
+
+    @ApiModelProperty(value = "用户终端", required = false)
+    private String userEndpoint;
 }

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

@@ -5,6 +5,7 @@ import javax.validation.constraints.Size;
 import javax.validation.constraints.NotNull;
 
 import java.io.Serializable;
+import java.util.Date;
 
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -64,4 +65,16 @@ public class AitagTagCategory implements Serializable {
     @ApiModelProperty("0未删除;1删除")
     private Integer isDelete;
 
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    @ApiModelProperty("更新时间")
+    private Date updateTime;
+
 }

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

@@ -1,55 +0,0 @@
-package cn.com.yusys.yusp.domain.entity;
-
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.IdType;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-
-/**
- * 智能标签按天汇总信息
- *
- * @author 2507040827
- * @date 2026-02-26 11:07:40
- */
-@TableName("aitag_tag_daily_agg")
-@Data
-@ApiModel(value = "AitagTagDailyAggEntity", description = "智能标签按天汇总信息")
-public class AitagTagDailyAggEntity {
-
-    /**
-     * id
-     **/
-    @ApiModelProperty(value="id")
-    @TableId(type=IdType.UUID)
-    private String id;
-
-    /**
-     * 汇总日期
-     **/
-    @ApiModelProperty(value = "汇总日期")
-    private String aggDate;
-
-    /**
-     * 类别名称
-     **/
-    @ApiModelProperty(value = "标签类别")
-    private String categoryId;
-
-    /**
-     * 标签名称
-     **/
-    @ApiModelProperty(value = "标签名称")
-    private String tagNm;
-
-    /**
-     * 统计梳理
-     **/
-    @ApiModelProperty(value = "统计梳理")
-    private Integer tagCount;
-
-
-
-}

+ 30 - 3
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfoEntity.java

@@ -1,12 +1,12 @@
 package cn.com.yusys.yusp.domain.entity;
 
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.*;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.Objects;
+
 
 /**
  * 
@@ -30,18 +30,21 @@ public class AitagTagInfoEntity {
      * 所属大类
      **/
     @ApiModelProperty(value = "所属大类")
+    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
     private String categoryId;
 
     /**
      * 标签名称
      **/
     @ApiModelProperty(value = "标签名称")
+    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
     private String tagNm;
 
     /**
      * 标签代码
      **/
     @ApiModelProperty(value = "标签代码")
+    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
     private String tagCode;
 
     /**
@@ -112,4 +115,28 @@ public class AitagTagInfoEntity {
     private String reviser;
 
 
+    public boolean valueEqualsValue(AitagTagInfoEntity that) {
+        return  isNullOrEmptyEqual(categoryId, that.categoryId)
+                && isNullOrEmptyEqual(tagNm, that.tagNm)
+                && isNullOrEmptyEqual(tagCode, that.tagCode)
+                && isNullOrEmptyEqual(tagRemark, that.tagRemark)
+                && isNullOrEmptyEqual(parentId, that.parentId)
+                && isNullOrEmptyEqual(reg, that.reg)
+                && isNullOrEmptyEqual(tagPrompt, that.tagPrompt) ;
+    }
+
+    private boolean isNullOrEmptyEqual(String str1, String str2) {
+        str1 = normalizeString(str1);
+        str2 = normalizeString(str2);
+        return Objects.equals(str1, str2);
+    }
+    /**
+     * 标准化字符串:null/空/空白字符串都转为 null
+     */
+    private String normalizeString(String str) {
+        if (str == null || str.trim().isEmpty()) {
+            return null;
+        }
+        return str.trim();
+    }
 }

+ 5 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagInfoVersionEntity.java

@@ -111,5 +111,9 @@ public class AitagTagInfoVersionEntity {
     private String reviser;
 
 
-
+    /**
+     * 主版本id
+     **/
+    @ApiModelProperty(value = "主版本id")
+    private String primaryVersionId;
 }

+ 7 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/entity/AitagTagLogEntity.java

@@ -154,8 +154,14 @@ passr: true/false
     private String feedbackUserEndpoint;
 
     /**
+     * 0正常,1删除
+     **/
+    @ApiModelProperty(value = "0正常,1删除")
+    private Integer isDelete;
+
+    /**
      * 标签标识 0正常数据; 1测试数据
      **/
     @ApiModelProperty(value = "标签标识 0:正常数据; 1:测试数据")
-    private String tagScope;
+    private Integer tagScope;
 }

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

@@ -4,6 +4,9 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.time.LocalDateTime;
+import java.util.Date;
+
 @Data
 @ApiModel("标签体系视图对象")
 public class AitagTagCategoryVo {
@@ -30,4 +33,11 @@ public class AitagTagCategoryVo {
 
     @ApiModelProperty("标签数量(临时字段)")
     private Integer tagNum;
+
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    private Date updateTime;
+
 }

+ 36 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileNode.java

@@ -0,0 +1,36 @@
+package cn.com.yusys.yusp.domain.vo.EsbVo;
+
+
+import lombok.Data;
+import lombok.Getter;
+
+
+/**
+ * ESB 客户画像信息 数据节点
+ * @author zky
+ */
+@Getter
+public class CustomerProfileNode {
+
+    /** 对象编号(必输,长度32) */
+    private String OBJECT_ID;
+
+    /** 标签编号(必输,长度20) */
+    private String LABEL_ID;
+
+    /** 标签名称(必输,长度18) */
+    private String LABEL_NAME;
+
+
+    /**
+     *
+     * @param OBJECT_ID 合同Id
+     * @param LABEL_ID 标签编号
+     * @param LABEL_NAME 标签名称
+     */
+    public CustomerProfileNode(String OBJECT_ID, String LABEL_ID, String LABEL_NAME) {
+        this.OBJECT_ID = OBJECT_ID;
+        this.LABEL_ID = LABEL_ID;
+        this.LABEL_NAME = LABEL_NAME;
+    }
+}

+ 26 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileReqVo.java

@@ -0,0 +1,26 @@
+package cn.com.yusys.yusp.domain.vo.EsbVo;
+
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * ESB 客户画像信息 请求参数
+ *
+ * @author zky
+ */
+
+
+@Data
+public class CustomerProfileReqVo {
+
+    /** 对象信息(必输,数组) */
+    private List<CustomerProfileNode> DATA;
+
+    /** 操作人编号(必输,长度10) */
+    private String USER_ID;
+
+    /** 打标结果(必输,长度1:0-不确认/1-确认) */
+    private String LABEL_RESULT;
+}

+ 24 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/EsbVo/CustomerProfileResVo.java

@@ -0,0 +1,24 @@
+package cn.com.yusys.yusp.domain.vo.EsbVo;
+
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * ESB 客户画像信息 请求参数
+ *
+ * @author zky
+ */
+
+
+@Data
+public class CustomerProfileResVo {
+
+    /** 结果状态(必输,长度1:1-失败/2-成功) */
+    private String RESULT;
+
+    /** 返回信息(必输,长度10) */
+    private String MSG;
+
+}

+ 4 - 4
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/ExportDataVo.java

@@ -25,16 +25,16 @@ public class ExportDataVo {
 
 
     /**
-     * 客户经理
+     * 所属行社
      **/
-    @ExcelProperty(value = "法人机构" ,index = 2)
+    @ExcelProperty(value = "所属行社" ,index = 2)
     private String feedbackUserOrg;
 
 
     /**
-     * 客户经理
+     * 所属机构
      **/
-    @ExcelProperty(value = "网点" ,index = 3)
+    @ExcelProperty(value = "所属机构" ,index = 3)
     private String feedbackUserEndpoint;
 
 

+ 31 - 2
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/IconResVo.java

@@ -1,6 +1,7 @@
 package cn.com.yusys.yusp.domain.vo;
 
 
+import cn.com.yusys.yusp.commons.util.StringUtils;
 import lombok.Data;
 
 /**
@@ -18,7 +19,35 @@ public class IconResVo {
     /**
      * 字段Value值
      */
-    private String val;
-
+    private Integer val;
 
+    /**
+     * 统计类型 day 按天统计, mother 按月统计
+     */
+    private String statType;
+
+
+
+    public IconResVo(String stat, Integer val , String statType) {
+        this.stat = stat;
+        this.val = val;
+        this.statType = statType;
+    }
+
+    public IconResVo() {
+    }
+
+    public String getStatShow() {
+        if(StringUtils.equals("day",statType)){
+            String month = stat.substring(5, 7);
+            String day = stat.substring(8, 10);
+            return month + "月" + day + "日";
+        }else if(StringUtils.equals("mother",statType)){
+            String year = stat.substring(0, 4);
+            String month = stat.substring(5,7);
+            return year + "年" + month + "月";
+        }else{
+            return stat;
+        }
+    }
 }

+ 13 - 28
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/TagNodeVo.java

@@ -38,6 +38,11 @@ public class TagNodeVo {
      */
     private String categoryId;
 
+    /**
+     * tag1/tag2/tag3/..
+     */
+    private String tagPath;
+
     private List<TagNodeVo> children = new ArrayList<>();
 
 
@@ -47,58 +52,38 @@ public class TagNodeVo {
         this.tagNm = entity.getTagNm();
         this.tagCode = entity.getTagCode();
         this.categoryId = entity.getCategoryId();
-    }
-
-    public String getCategoryId() {
-        return categoryId;
-    }
-
-    public void setCategoryId(String categoryId) {
-        this.categoryId = categoryId;
+        this.tagPath = entity.getTagPath();
     }
 
     public String getId() {
         return id;
     }
 
-    public void setId(String id) {
-        this.id = id;
-    }
-
     public String getParentId() {
         return parentId;
     }
 
-    public void setParentId(String parentId) {
-        this.parentId = parentId;
-    }
-
     public String getTagNm() {
         return tagNm;
     }
 
-    public void setTagNm(String tagNm) {
-        this.tagNm = tagNm;
-    }
-
     public String getTagCode() {
         return tagCode;
     }
 
-    public void setTagCode(String tagCode) {
-        this.tagCode = tagCode;
+    public String getCategoryId() {
+        return categoryId;
     }
 
+    public String getTagPath() {
+        return tagPath;
+    }
 
     public List<TagNodeVo> getChildren() {
         return children;
     }
 
-    public void setChildren(List<TagNodeVo> children) {
-        this.children = children;
-    }
-
-    public void addChild(TagNodeVo child) {
-        this.children.add(child);
+    public void addChild(TagNodeVo currentNode) {
+        this.children.add(currentNode);
     }
 }

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

@@ -0,0 +1,76 @@
+package cn.com.yusys.yusp.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+
+/**
+ * 标签新增
+ * @author zky
+ */
+
+@Data
+public class TagSaveVo {
+
+
+    /**
+     * 所属大类
+     **/
+    @ApiModelProperty(value = "所属大类")
+    @NotBlank(message = "所属大类不能为空")
+    private String categoryId;
+
+
+    /**
+     * 标签名称
+     **/
+    @NotBlank(message = "标签名称不能为空")
+    @ApiModelProperty(value = "标签名称", required = true)
+    private String tagNm;
+
+    /**
+     * 标签代码
+     **/
+    @NotBlank(message = "标签代码不能为空")
+    @ApiModelProperty(value = "标签代码", required = true)
+    private String tagCode;
+
+    /**
+     * 标签备注
+     **/
+    @ApiModelProperty(value = "标签备注")
+    private String tagRemark;
+
+    /**
+     * 父级ID
+     **/
+    @ApiModelProperty(value = "父级ID", required = true)
+    private String parentId;
+
+    /**
+     * 标签规则
+     **/
+    @ApiModelProperty(value = "标签规则")
+    private String reg;
+
+    /**
+     * 标签等级
+     **/
+    @ApiModelProperty(value = "标签等级")
+    private Integer tagLevel;
+
+    /**
+     * tag1/tag2/tag3/...
+     **/
+    @ApiModelProperty(value = "tag1/tag2/tag3/...")
+    private String tagPath;
+
+    /**
+     * 标签提示词
+     **/
+    @ApiModelProperty(value = "标签提示词")
+    private String tagPrompt;
+
+}

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

@@ -0,0 +1,81 @@
+package cn.com.yusys.yusp.domain.vo;
+
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+
+/**
+ * 标签修改实体磊
+ */
+@Data
+public class TagUpdateVo {
+
+
+    /**
+     * id
+     **/
+    @ApiModelProperty(value="id")
+    @NotBlank(message = "标签主键")
+    private String id;
+
+    /**
+     * 所属大类
+     **/
+    @ApiModelProperty(value = "所属大类")
+    private String categoryId;
+
+    /**
+     * 标签名称
+     **/
+    @ApiModelProperty(value = "标签名称", required = true)
+    private String tagNm;
+
+    /**
+     * 标签代码
+     **/
+    @ApiModelProperty(value = "标签代码", required = true)
+    private String tagCode;
+
+    /**
+     * 标签备注
+     **/
+    @ApiModelProperty(value = "标签备注")
+    private String tagRemark;
+
+    /**
+     * 父级ID
+     **/
+    @ApiModelProperty(value = "父级ID", required = true)
+    private String parentId;
+
+    /**
+     * 标签规则
+     **/
+    @ApiModelProperty(value = "标签规则")
+    private String reg;
+
+    /**
+     * 标签等级
+     **/
+    @ApiModelProperty(value = "标签等级")
+    private Integer tagLevel;
+
+    /**
+     * tag1/tag2/tag3/...
+     **/
+    @ApiModelProperty(value = "tag1/tag2/tag3/...")
+    private String tagPath;
+
+    /**
+     * 标签提示词
+     **/
+    @ApiModelProperty(value = "标签提示词")
+    private String tagPrompt;
+
+
+}

+ 54 - 54
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/domain/vo/fastapivo/AiTaggingQueryResponseVo.java

@@ -86,6 +86,60 @@ public class AiTaggingQueryResponseVo {
 
         @ApiModelProperty(value = "分类 ID")
         private String category_id;
+
+        public String getInsert_time() {
+            return formatDateTime(insert_time);
+        }
+
+        public String getFeedback_time() {
+            return formatDateTime(feedback_time);
+        }
+
+        private String formatDateTime(String dateTimeStr) {
+            if (dateTimeStr == null || dateTimeStr.isEmpty()) {
+                return null;
+            }
+
+            try {
+                if (dateTimeStr.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
+                    return dateTimeStr;
+                }
+
+                if (dateTimeStr.matches("\\d+")) {
+                    long timestamp = Long.parseLong(dateTimeStr);
+                    java.util.Date date = new java.util.Date(timestamp);
+                    return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
+                }
+
+                java.time.LocalDateTime dateTime = parseDateTime(dateTimeStr);
+                if (dateTime != null) {
+                    return dateTime.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                }
+            } catch (Exception e) {
+            }
+
+            return dateTimeStr;
+        }
+
+        private java.time.LocalDateTime parseDateTime(String dateTimeStr) {
+            try {
+                java.time.format.DateTimeFormatter[] formatters = {
+                        java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME,
+                        java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"),
+                        java.time.format.DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
+                        java.time.format.DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")
+                };
+
+                for (java.time.format.DateTimeFormatter formatter : formatters) {
+                    try {
+                        return java.time.LocalDateTime.parse(dateTimeStr, formatter);
+                    } catch (Exception e) {
+                    }
+                }
+            } catch (Exception e) {
+            }
+            return null;
+        }
     }
 
     @Data
@@ -114,57 +168,3 @@ public class AiTaggingQueryResponseVo {
         private String category_id;
     }
 }
-
-
-//
-//import io.swagger.annotations.ApiModel;
-//import io.swagger.annotations.ApiModelProperty;
-//import lombok.Data;
-//
-//@Data
-//@ApiModel("AI 打标查询响应")
-//public class AiTaggingQueryResponseVo {
-//
-//    @ApiModelProperty(value = "状态码 200(查询成功)/300(正在处理中)/500(处理失败)")
-//    private String code;
-//
-//    @ApiModelProperty(value = "消息内容")
-//    private String message;
-//
-//    @ApiModelProperty(value = "标签数据")
-//    private AiTagDataWrapper data;
-//
-//    @Data
-//    @ApiModel("标签数据包装")
-//    public static class AiTagDataWrapper {
-//
-//        @ApiModelProperty(value = "标签结果列表")
-//        private AiTagResultVo[] result;
-//    }
-//
-//    @Data
-//    @ApiModel("标签结果")
-//    public static class AiTagResultVo {
-//
-//        @ApiModelProperty(value = "标签 ID")
-//        private String id;
-//
-//        @ApiModelProperty(value = "标签名称")
-//        private String tag_name;
-//
-//        @ApiModelProperty(value = "标签代码")
-//        private String tag_code;
-//
-//        @ApiModelProperty(value = "标签路径")
-//        private String tag_path;
-//
-//        @ApiModelProperty(value = "是否 AI 推荐 true: AI 推荐的标签,false: 算法筛选出来但是被 LLM 淘汰的候选标签")
-//        private Boolean passr;
-//
-//        @ApiModelProperty(value = "解释说明")
-//        private String desc;
-//
-//        @ApiModelProperty(value = "分类 ID")
-//        private String category_id;
-//    }
-//}

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

@@ -9,9 +9,6 @@ import java.util.List;
 @Mapper
 public interface AitagTagCategoryMapper {
 
-    // 分页查询(偏移量 + 限制)
-    List<AitagTagCategory> selectPageCategories(@Param("offset") int offset, @Param("limit") int limit);
-
     // 带模糊筛选的分页查询
     List<AitagTagCategory> selectPageCategoriesWithFilter(
             @Param("categoryNm") String categoryNm,
@@ -21,9 +18,6 @@ public interface AitagTagCategoryMapper {
     // 带模糊筛选的总数
     long selectCountWithFilter(@Param("categoryNm") String categoryNm);
 
-    // 模糊查询(按名称)
-    List<AitagTagCategory> selectByCategoryNmLike(@Param("categoryNm") String categoryNm);
-
     // 按名称查重(未删除)
     int selectCountByNameAndNotDeleted(@Param("categoryNm") String categoryNm);
 

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

@@ -1,24 +0,0 @@
-package cn.com.yusys.yusp.mapper;
-
-import cn.com.yusys.yusp.commons.mybatisplus.mapper.BaseMapper;
-import cn.com.yusys.yusp.domain.vo.IconResVo;
-import cn.com.yusys.yusp.domain.entity.AitagTagDailyAggEntity;
-import cn.com.yusys.yusp.domain.vo.TagDistStatsReqVo;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-
-/**
- * 智能标签按天汇总信息
- *
- * @author 2507040827
- * @date 2026-02-26 11:07:40
- */
-
-@Mapper
-public interface AitagTagDailyAggDao extends BaseMapper<AitagTagDailyAggEntity> {
-
-    List<IconResVo> selectTagDistStats(TagDistStatsReqVo taggingResult);
-
-}

+ 4 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/mapper/AitagTagLogDao.java

@@ -4,6 +4,7 @@ import cn.com.yusys.yusp.commons.mybatisplus.mapper.BaseMapper;
 import cn.com.yusys.yusp.domain.vo.IconResVo;
 import cn.com.yusys.yusp.domain.vo.SmartTaggingResultVo;
 import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
+import cn.com.yusys.yusp.domain.vo.TagDistStatsReqVo;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -28,7 +29,9 @@ public interface AitagTagLogDao extends BaseMapper<AitagTagLogEntity> {
     List<IconResVo> selectTagReportByMonth(SmartTaggingResultVo taggingResult);
 
 
-    List<IconResVo> selectTagReportByWeek(SmartTaggingResultVo taggingResult);
 
     List<AitagTagLogEntity> selectByInsertTime(@Param("insertTime") String insertTime);
+
+
+    List<IconResVo> selectTagDistStats(TagDistStatsReqVo taggingResult);
 }

+ 6 - 6
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagApiLogService.java

@@ -8,16 +8,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 public interface AitagApiLogService {
 
     /**
-     * 记录API调用日志
+     * 记录 API 调用日志(使用 UUID 作为 ID)
      * @param apiLog 日志实体
      */
     void recordApiLog(AitagApiLog apiLog);
 
     /**
-     * 生成日志ID
-     * @return 日志ID
+     * 生成 UUID 日志 ID
+     * @return UUID 格式的日志 ID
      */
-    String generateLogId();
+    String generateUuid();
 
     /**
      * 分页查询日志列表
@@ -35,8 +35,8 @@ public interface AitagApiLogService {
     Page<AitagApiLogListVo> refreshApiLogs(Integer page, Integer pageSize);
 
     /**
-     * 根据ID查询日志详情
-     * @param id 日志ID
+     * 根据 ID 查询日志详情
+     * @param id 日志 ID
      * @return 日志详情
      */
     AitagApiLog getApiLogDetail(String id);

+ 1 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagInfoService.java

@@ -46,7 +46,7 @@ public interface AitagTagInfoService extends IService<AitagTagInfoEntity> {
 
     List<AitagTagInfoEntity> queryList(AitagTagInfoQueryVo aitagTagInfoQueryVo);
 
-    List<String> batchImport(MultipartFile file, String reviser, String categoryId) throws IOException;
+    List<String> batchImport(MultipartFile file, String categoryId) throws IOException;
 
 
     AiTaggingResponseVo callAiTag(Map<String, Object> body, String path);

+ 3 - 2
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/AitagTagLogService.java

@@ -1,5 +1,6 @@
 package cn.com.yusys.yusp.service;
 
+import cn.com.yusys.yusp.domain.dto.TagLogDto;
 import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
 import cn.com.yusys.yusp.domain.vo.*;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -20,9 +21,9 @@ public interface AitagTagLogService extends IService<AitagTagLogEntity> {
 
     List<IconResVo> taggingTrend(TaggingTrendReqVo TaggingTrendReqVo);
 
-    IPage<AitagTagLogEntity> taggingDetails(TaggingTransactionReqVo aitagTagLogEntity);
+    IPage<TagLogDto> taggingDetails(TaggingTransactionReqVo aitagTagLogEntity);
 
-    AitagTagLogEntity show(String id);
+    TagLogDto show(String id);
 
     List<IconResVo> tagDistStats(TagDistStatsReqVo resultDTO);
 

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

@@ -1,121 +0,0 @@
-package cn.com.yusys.yusp.service;
-
-
-import cn.com.yusys.yusp.commons.util.StringUtils;
-import cn.com.yusys.yusp.domain.entity.AitagTagDailyAggEntity;
-import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
-import cn.com.yusys.yusp.mapper.AitagTagDailyAggDao;
-import cn.com.yusys.yusp.mapper.AitagTagLogDao;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.StringRedisTemplate;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDate;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-import static cn.com.yusys.yusp.config.DataDictionary.FEEDBACK_RESULT_AGREE;
-import static cn.com.yusys.yusp.config.DataDictionary.FEEDBACK_RESULT_REJECT;
-
-@Component
-@Slf4j
-public class DistributedScheduledTask {
-
-
-
-    @Autowired
-    private StringRedisTemplate redisTemplate;
-
-
-    @Autowired
-    private AitagTagLogDao aitagTagLogDao;
-
-    @Autowired
-    private AitagTagDailyAggDao aitagTagDailyAggDao;
-
-    /**
-     * 每天凌晨统计昨天打标数据
-     */
-    @Scheduled(cron = "#{@environment.getProperty('application.scheduled.task-cron')}")
-    public void syncDataTask() {
-        log.info("智能标签统计,准备获取redis锁");
-        Boolean taskCron = redisTemplate.opsForValue().setIfAbsent("taskCron", "1", 4, TimeUnit.HOURS);
-        if(Boolean.TRUE.equals(taskCron)){
-            try{
-                log.info("获取redis锁成功,智能标签开始统计");
-                LambdaQueryWrapper<AitagTagDailyAggEntity> queryWrapper = new LambdaQueryWrapper<>();
-                String yesterday = LocalDate.now().minusDays(1).toString();
-                queryWrapper.eq(AitagTagDailyAggEntity::getAggDate,yesterday);
-                int count = aitagTagDailyAggDao.selectCount(queryWrapper);
-                if(count == 0){
-                    executeBusinessLogic(yesterday);
-                }
-                log.info("智能标签统计任务结束");
-            }finally {
-                log.info("删除redis锁信息");
-                redisTemplate.delete("taskCron");
-            }
-        }else{
-            log.warn("智能标签统计任务已有节点执行--本次执行跳过");
-        }
-    }
-
-    private void executeBusinessLogic(String yesterday) {
-        List<AitagTagLogEntity> aitagTagLogEntities = aitagTagLogDao.selectByInsertTime(yesterday);
-        if(aitagTagLogEntities != null){
-            Map<String,Integer> count = new HashMap<>();
-            for (AitagTagLogEntity aitagTagLog:aitagTagLogEntities){
-                String feedback = aitagTagLog.getFeedback();
-                String result = null;
-                if(FEEDBACK_RESULT_REJECT.equals(feedback)){
-                    result = aitagTagLog.getFeedbackResult();
-                }else{
-                    result = aitagTagLog.getResult();
-                }
-                if(StringUtils.isBlank(result)){
-                    continue;
-                }
-                duCount(result, count);
-            }
-            AitagTagDailyAggEntity aitagTagLog = new AitagTagDailyAggEntity();
-            for (String key : count.keySet()){
-                aitagTagLog.setId(StringUtils.getUUID());
-                aitagTagLog.setAggDate(yesterday);
-                aitagTagLog.setTagCount(count.get(key));
-                String[] split = key.split(":");
-                if(split.length==2){
-                    aitagTagLog.setCategoryId(split[1]);
-                }
-                aitagTagLog.setTagNm(split[0]);
-                aitagTagDailyAggDao.insert(aitagTagLog);
-            }
-
-        }
-    }
-
-    private static void duCount(String result, Map<String, Integer> count) {
-        List<Map> results = JSONArray.parseArray(result, Map.class);
-        for (int i = 0 ;i<results.size();i++){
-            Map resultMap = results.get(i);
-            String labe = resultMap.getOrDefault("tag_name","").toString()+":"
-                    + resultMap.getOrDefault("category_id","").toString();
-            if(count.containsKey(labe)){
-                Integer integer = count.get(labe);
-                count.put(labe,integer+1);
-            }else{
-                count.put(labe,1);
-            }
-        }
-    }
-
-
-}

+ 26 - 1
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/TagImportListener.java

@@ -1,5 +1,6 @@
 package cn.com.yusys.yusp.service;
 
+import cn.com.yusys.yusp.commons.exception.BizException;
 import cn.com.yusys.yusp.commons.util.StringUtils;
 import cn.com.yusys.yusp.domain.dto.TagImportDto;
 import cn.com.yusys.yusp.mapper.AitagTagInfoDao;
@@ -17,12 +18,36 @@ import java.util.*;
 public class TagImportListener extends AnalysisEventListener<TagImportDto> {
     private final List<TagImportDto> dataList = new ArrayList<>();
 
+    private final String[] expectedHeaders = {"标签名称","标签编号","父标签名称","标签定义","关键词","标签判断说明"};
+
+    private static final int MAX_ROWS = 1000; // 最多1000条
+
+    @Override
+    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
+        // 校验表头
+        if (headMap.size() != expectedHeaders.length) {
+            throw BizException.of("E014");
+        }
+
+        for (int i = 0; i < expectedHeaders.length; i++) {
+            if (!expectedHeaders[i].equals(headMap.get(i))) {
+                throw BizException.of("E015");
+            }
+        }
+    }
 
     @Override
     public void invoke(TagImportDto data, AnalysisContext context) {
         // 数据清洗
+
+        if (dataList.size() >= MAX_ROWS) {
+            throw BizException.of("E016");
+        }
         if (StringUtils.isBlank(data.getTagNm())) {
-            return;
+            throw BizException.of("E012");
+        }
+        if (StringUtils.isBlank(data.getTagCode())) {
+            throw BizException.of("E013");
         }
         data.setTagNm(data.getTagNm().trim());
         if (!StringUtils.isBlank(data.getParentName())) {

+ 21 - 0
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/esb/ESBService.java

@@ -0,0 +1,21 @@
+package cn.com.yusys.yusp.service.esb;
+
+import cn.com.yusys.yusp.domain.vo.EsbVo.CustomerProfileReqVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+
+@Service
+@Slf4j
+public class ESBService {
+
+
+    /**
+     * 打标结果同步
+     * @param customerProfileReqVo 客户画像系统请求参数
+     */
+    public void taggingResultSync(CustomerProfileReqVo customerProfileReqVo){
+       log.info("准备调用ESB,访问客户画像接口同步打标结果");
+
+    }
+}

+ 13 - 85
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagApiLogServiceImpl.java

@@ -10,15 +10,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
-import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
 @Service
@@ -29,56 +25,26 @@ public class AitagApiLogServiceImpl implements AitagApiLogService {
     @Autowired
     private AitagApiLogMapper apiLogMapper;
 
-    // 日期格式化器
-    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
-
-    // 缓存当天的最大序列号 key:日期 value:最大序列号
-    private static final Map<String, Long> DATE_SEQUENCE_CACHE = new ConcurrentHashMap<>();
-
-    // 当前缓存的日期
-    private volatile String cachedDate = "";
-
-    private static final ReentrantLock sequenceLock = new ReentrantLock();
-
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void recordApiLog(AitagApiLog apiLog) {
         try {
+            // 在插入前生成 UUID 作为 ID,确保不为空
+            String uuid = generateUuid();
+            apiLog.setId(uuid);
+
             apiLogMapper.insertApiLog(apiLog);
-            logger.info("API调用日志记录成功: {} - {}", apiLog.getId(), apiLog.getOperationType());
+            logger.info("API 调用日志记录成功,UUID: {}", apiLog.getId());
         } catch (Exception e) {
-            logger.error("记录API调用日志失败: {}", e.getMessage(), e);
-            // 不抛出异常,避免影响主业务流程
+            logger.error("记录 API 调用日志失败:{}", e.getMessage(), e);
+            throw e;
         }
     }
 
     @Override
-    public String generateLogId() {
-        sequenceLock.lock();
-        try {
-            String currentDate = DATE_FORMAT.format(new Date());
-
-            if (!currentDate.equals(cachedDate)) {
-                refreshCache(currentDate);
-            }
-
-            Long currentSequence = DATE_SEQUENCE_CACHE.get(currentDate);
-            if (currentSequence == null) {
-                loadFromDatabase(currentDate);
-                currentSequence = DATE_SEQUENCE_CACHE.get(currentDate);
-            }
-
-            currentSequence++;
-            DATE_SEQUENCE_CACHE.put(currentDate, currentSequence);
-
-            if (currentSequence > 999999) {
-                currentSequence = 1L;
-                DATE_SEQUENCE_CACHE.put(currentDate, currentSequence);
-            }
-
-            return String.format("API%s%06d", currentDate, currentSequence);
-        } finally {
-            sequenceLock.unlock();
-        }
+    public String generateUuid() {
+        // 生成不带横线的 UUID,格式:API + 32 位 UUID
+        return "API" + java.util.UUID.randomUUID().toString().replace("-", "");
     }
 
     @Override
@@ -98,7 +64,7 @@ public class AitagApiLogServiceImpl implements AitagApiLogService {
                 pageSize
         );
 
-        // 转换为VO
+        // 转换为 VO
         List<AitagApiLogListVo> voList = logs.stream().map(log -> {
             AitagApiLogListVo vo = new AitagApiLogListVo();
             BeanUtils.copyProperties(log, vo);
@@ -122,42 +88,4 @@ public class AitagApiLogServiceImpl implements AitagApiLogService {
     public AitagApiLog getApiLogDetail(String id) {
         return apiLogMapper.selectApiLogById(id);
     }
-
-    /**
-     * 刷新缓存 - 每天0点执行
-     */
-    @Scheduled(cron = "0 0 0 * * ?")
-    public void dailyCacheRefresh() {
-        String newDate = DATE_FORMAT.format(new Date());
-        refreshCache(newDate);
-        logger.info("日志ID缓存已刷新,新日期: {}", newDate);
-    }
-
-    /**
-     * 刷新缓存
-     */
-    private synchronized void refreshCache(String newDate) {
-        cachedDate = newDate;
-        DATE_SEQUENCE_CACHE.clear();
-        loadFromDatabase(newDate);
-    }
-
-    /**
-     * 从数据库加载序列号
-     */
-    private void loadFromDatabase(String dateStr) {
-        String maxId = apiLogMapper.getMaxIdByDate(dateStr);
-        long sequence = 0;
-
-        if (maxId != null && maxId.length() >= 17) {
-            try {
-                String sequenceStr = maxId.substring(11, 17);
-                sequence = Long.parseLong(sequenceStr);
-            } catch (Exception e) {
-                logger.warn("解析数据库最大ID序列号失败: {}", e.getMessage());
-            }
-        }
-
-        DATE_SEQUENCE_CACHE.put(dateStr, sequence);
-    }
 }

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

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -36,6 +37,8 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
             vo.setVisibilityLevel(category.getVisibilityLevel());
             vo.setState(category.getState());
             vo.setIsDelete(category.getIsDelete());
+            vo.setCreateTime(category.getCreateTime());
+            vo.setUpdateTime(category.getUpdateTime());
 
             // 动态计算 tagNum
             int tagNum = aiTagCategoryMapper.countTagsByCategoryId(category.getId());
@@ -63,6 +66,9 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
         category.setCategoryDesc(dto.getCategoryDesc());
         category.setState(1); // 默认停用
         category.setIsDelete(0); // 未删除
+        Date now = new Date();
+        category.setCreateTime(now);
+        category.setUpdateTime(now);
 
         aiTagCategoryMapper.insertCategory(category);
         return category;
@@ -84,6 +90,7 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
 
         existing.setCategoryNm(dto.getCategoryNm());
         existing.setCategoryDesc(dto.getCategoryDesc());
+        existing.setUpdateTime(new Date());
 
         aiTagCategoryMapper.updateCategory(existing);
         return existing;
@@ -101,7 +108,7 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
         // 检查标签数量
         int tagCount = aiTagCategoryMapper.countTagsByCategoryId(id);
         if (tagCount == 0) {
-            throw new RuntimeException("体系内无可用标签!");
+            throw new RuntimeException("该体系下无标签数据,请先完成标签配置");
         }
 
         // 检查当前状态
@@ -134,10 +141,10 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
             throw new RuntimeException("标签体系不存在");
         }
 
-        // 判断标签数量是否为0
+        // 判断标签数量是否为 0
         int tagCount = aiTagCategoryMapper.countTagsByCategoryId(id);
         if (tagCount > 0) {
-            throw new RuntimeException("该标签体系下仍有标签,无法删除");
+            throw new RuntimeException("该体系下存在标签数据,为确保业务数据完整性,禁止删除");
         }
 
         aiTagCategoryMapper.deleteCategory(id);
@@ -159,6 +166,8 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
             vo.setVisibilityLevel(category.getVisibilityLevel());
             vo.setState(category.getState());
             vo.setIsDelete(category.getIsDelete());
+            vo.setCreateTime(category.getCreateTime());
+            vo.setUpdateTime(category.getUpdateTime());
 
             // 动态计算 tagNum
             int tagNum = aiTagCategoryMapper.countTagsByCategoryId(category.getId());
@@ -187,6 +196,8 @@ public class AitagTagCategoryServiceImpl implements AitagTagCategoryService {
         vo.setVisibilityLevel(category.getVisibilityLevel());
         vo.setState(category.getState());
         vo.setIsDelete(category.getIsDelete());
+        vo.setCreateTime(category.getCreateTime());
+        vo.setUpdateTime(category.getUpdateTime());
 
         // 计算标签数量
         int tagNum = aiTagCategoryMapper.countTagsByCategoryId(category.getId());

+ 66 - 15
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagInfoServiceImpl.java

@@ -123,6 +123,7 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
     public String saveTag(AitagTagInfoEntity aitagTagInfo){
         aitagTagInfo.setId(StringUtils.getUUID());
         String parentId = aitagTagInfo.getParentId();
+        checkTagCodeDuplicate(aitagTagInfo.getTagCode());
         if(!StringUtils.isBlank(parentId)){
             LambdaQueryWrapper<AitagTagInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
             queryWrapper.eq(AitagTagInfoEntity::getParentId,parentId);
@@ -132,6 +133,9 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
                 AitagTagInfoEntity aitagTagInfoEntity = aitagTagInfoEntities.get(0);
                 aitagTagInfo.setTagPath(aitagTagInfoEntity.getTagPath()+"/"+aitagTagInfo.getTagNm());
                 aitagTagInfo.setTagLevel(aitagTagInfoEntity.getTagLevel()+1);
+                if(aitagTagInfoEntity.getTagLevel() > 4){
+                    throw BizException.of("E017");
+                }
             }else{
                 aitagTagInfo.setTagPath(aitagTagInfo.getTagNm());
                 aitagTagInfo.setTagLevel(1);
@@ -166,6 +170,7 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
             queryWrapper.like(AitagTagInfoEntity::getTagNm, aitagTagInfoQueryVo.getTagNm());
         }
         queryWrapper.eq(AitagTagInfoEntity::getIsDelete, TAG_UNDELETED);
+        queryWrapper.orderByAsc(AitagTagInfoEntity::getRevisionTime);
         List<AitagTagInfoEntity> aitagTagInfoEntities = this.baseMapper.selectList(queryWrapper);
         return this.buildTree(aitagTagInfoEntities);
     }
@@ -178,15 +183,38 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void updateTag(AitagTagInfoEntity aitagTagInfoEntity) {
-        AitagTagInfoEntity tagInfo = this.baseMapper.selectById(aitagTagInfoEntity.getId());
-        AitagTagInfoVersionEntity aitagTagInfoVersionEntity = JSON.parseObject(JSON.toJSONString(tagInfo), AitagTagInfoVersionEntity.class);
+        AitagTagInfoEntity oldTagInfo = this.baseMapper.selectById(aitagTagInfoEntity.getId());
+        if(oldTagInfo.valueEqualsValue(aitagTagInfoEntity)){
+            return;
+        }
+        String newTagCode = aitagTagInfoEntity.getTagCode();
+        String oldTagCode = oldTagInfo.getTagCode();
+        if(!StringUtils.isEmpty(newTagCode) && !StringUtils.equals(oldTagCode,newTagCode)){
+            checkTagCodeDuplicate(newTagCode);
+        }
+        AitagTagInfoVersionEntity aitagTagInfoVersionEntity = JSON.parseObject(JSON.toJSONString(oldTagInfo), AitagTagInfoVersionEntity.class);
+        aitagTagInfoVersionEntity.setPrimaryVersionId(aitagTagInfoEntity.getId());
         aitagTagInfoVersionEntity.setId(StringUtils.getUUID());
         this.aitagTagInfoVersionDao.insert(aitagTagInfoVersionEntity);
         aitagTagInfoEntity.setRevisionTime(DateUtils.getCurrDateTimeStr());
-        aitagTagInfoEntity.setTagVersion(Integer.parseInt(tagInfo.getTagVersion())+1+"");
-        AitagTagInfoEntity parentTagInfo = this.baseMapper.selectById(aitagTagInfoEntity.getParentId());
-        aitagTagInfoEntity.setTagPath(parentTagInfo.getTagPath()+"/"+aitagTagInfoEntity.getTagNm());
-        aitagTagInfoEntity.setTagLevel(parentTagInfo.getTagLevel()+1);
+        aitagTagInfoEntity.setTagVersion(Integer.parseInt(oldTagInfo.getTagVersion())+1+"");
+        if(!StringUtils.isBlank(aitagTagInfoEntity.getParentId())){
+            AitagTagInfoEntity parentTagInfo = this.baseMapper.selectById(aitagTagInfoEntity.getParentId());
+            if(parentTagInfo !=null){
+                aitagTagInfoEntity.setTagPath(parentTagInfo.getTagPath()+"/"+aitagTagInfoEntity.getTagNm());
+                aitagTagInfoEntity.setTagLevel(parentTagInfo.getTagLevel()+1);
+                if(aitagTagInfoEntity.getTagLevel() > 4){
+                    throw BizException.of("E017");
+                }
+            }else{
+                aitagTagInfoEntity.setTagPath(aitagTagInfoEntity.getTagNm());
+                aitagTagInfoEntity.setTagLevel(1);
+            }
+        }else{
+            aitagTagInfoEntity.setTagPath(aitagTagInfoEntity.getTagNm());
+            aitagTagInfoEntity.setTagLevel(1);
+        }
+
         aitagTagInfoEntity.setReviser(SessionCommonUtil.getUserInfo());
         this.baseMapper.updateById(aitagTagInfoEntity);
     }
@@ -196,6 +224,12 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
     public void removeTags(List<String> list) {
         AitagTagInfoEntity aitagTagInfoEntity = new AitagTagInfoEntity();
         for (String id:list){
+            LambdaQueryWrapper<AitagTagInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(AitagTagInfoEntity::getParentId,id);
+            queryWrapper.eq(AitagTagInfoEntity::getIsDelete,NON_DELETE);
+            if(this.baseMapper.selectCount(queryWrapper)>0){
+                throw BizException.of("E011");
+            }
             aitagTagInfoEntity.setId(id);
             aitagTagInfoEntity.setIsDelete(TAG_DELETED);
             aitagTagInfoEntity.setReviser(SessionCommonUtil.getUserInfo());
@@ -207,7 +241,7 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
     @Transactional(rollbackFor = Exception.class)
     public String versionRollback(VersionRollbackVo versionRollback) {
         LambdaQueryWrapper<AitagTagInfoVersionEntity> queryTagVersionWrapper = new LambdaQueryWrapper<>();
-        queryTagVersionWrapper.eq(AitagTagInfoVersionEntity::getTagCode,versionRollback.getTagCode());
+        queryTagVersionWrapper.eq(AitagTagInfoVersionEntity::getPrimaryVersionId,versionRollback.getId());
         queryTagVersionWrapper.eq(AitagTagInfoVersionEntity::getTagVersion,versionRollback.getVersion());
         List<AitagTagInfoVersionEntity> versionEntities = this.aitagTagInfoVersionDao.selectList(queryTagVersionWrapper);
         if(versionEntities !=null && !versionEntities.isEmpty()){
@@ -219,7 +253,7 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
             aitagTagInfoEntity.setReviser(SessionCommonUtil.getUserInfo());
             this.baseMapper.updateById(aitagTagInfoEntity);
             LambdaUpdateWrapper<AitagTagInfoVersionEntity> updateTagVersion = new LambdaUpdateWrapper<>();
-            updateTagVersion.eq(AitagTagInfoVersionEntity::getTagCode,versionRollback.getTagCode());
+            updateTagVersion.eq(AitagTagInfoVersionEntity::getPrimaryVersionId,versionRollback.getId());
             updateTagVersion.ge(AitagTagInfoVersionEntity::getTagVersion,versionRollback.getVersion());
             AitagTagInfoVersionEntity aitagTagInfoVersionEntity = new AitagTagInfoVersionEntity();
             aitagTagInfoVersionEntity.setReviser(SessionCommonUtil.getUserInfo());
@@ -269,19 +303,20 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
         if (aitagTagInfoQueryVo.getTagPrompt() != null) {
             queryWrapper.eq(AitagTagInfoEntity::getTagPrompt, aitagTagInfoQueryVo.getTagPrompt());
         }
+        queryWrapper.eq(AitagTagInfoEntity::getIsDelete,NON_DELETE);
         queryWrapper.select(AitagTagInfoEntity::getId,AitagTagInfoEntity::getTagNm,AitagTagInfoEntity::getParentId);
         return this.baseMapper.selectList(queryWrapper);
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public List<String> batchImport(MultipartFile file, String reviser, String categoryId) throws IOException {
+    public List<String> batchImport(MultipartFile file, String categoryId) throws IOException {
         log.info("开始处理上传文件:{}", file.getOriginalFilename());
 
         TagImportListener listener = new TagImportListener();
         EasyExcel.read(file.getInputStream(), TagImportDto.class, listener)
                 .headRowNumber(1)
-                .sheet(1)
+                .sheet(0)
                 .doRead();
         List<TagImportDto> tagImportDtos = listener.getDataList();
         if (tagImportDtos.isEmpty()) {
@@ -298,22 +333,29 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
 
         validateParentTags(nameToDtoMap);
         resolveHierarchy(nameToDtoMap);
-        saveToDatabase(tagImportDtos,reviser,categoryId);
+        saveToDatabase(tagImportDtos,categoryId);
         return  tagImportDtos.stream().map(TagImportDto::getId).collect(Collectors.toList());
     }
 
 
-    private void saveToDatabase(List<TagImportDto> tagImportDtos,String reviser,String categoryId) {
+    private void saveToDatabase(List<TagImportDto> tagImportDtos,String categoryId) {
         AitagTagCategory aitagTagCategory = aiTagCategoryMapper.selectById(categoryId);
-        String categoryNm = aitagTagCategory.getCategoryNm();
+        if(aitagTagCategory == null && aitagTagCategory.getIsDelete().intValue() != NON_DELETE){
+            throw BizException.of("E018");
+        }
         for (TagImportDto tagImportDto: tagImportDtos){
+
+            checkTagCodeDuplicate(tagImportDto.getTagCode());
             AitagTagInfoEntity aitagTagInfo = JSONObject.parseObject(JSONObject.toJSONString(tagImportDto),
                     AitagTagInfoEntity.class);
             String tagPath = tagImportDto.getTagPath();
-            aitagTagInfo.setTagPath(categoryNm+"/"+tagPath);
+            aitagTagInfo.setTagPath(tagPath);
             aitagTagInfo.setTagLevel(aitagTagInfo.getTagPath().split("/").length);
+            if(aitagTagInfo.getTagLevel() > 4){
+                throw BizException.of("E017");
+            }
             aitagTagInfo.setRevisionTime(DateUtils.getCurrDateTimeStr());
-            aitagTagInfo.setReviser(reviser);
+            aitagTagInfo.setReviser(SessionCommonUtil.getUserInfo());
             aitagTagInfo.setTagVersion("1");
             aitagTagInfo.setIsDelete(TAG_UNDELETED);
             aitagTagInfo.setCategoryId(categoryId);
@@ -324,6 +366,15 @@ public class AitagTagInfoServiceImpl extends ServiceImpl<AitagTagInfoDao, AitagT
 
     }
 
+    private void checkTagCodeDuplicate(String tagCode) {
+        LambdaQueryWrapper<AitagTagInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(AitagTagInfoEntity::getTagCode, tagCode);
+        queryWrapper.eq(AitagTagInfoEntity::getIsDelete,TAG_UNDELETED);
+        if (this.baseMapper.selectCount(queryWrapper) > 0) {
+            throw BizException.of("E010");
+        }
+    }
+
     /**
      * 校验父标签存在性并将赋值parentId
      * @param nameToDtoMap

+ 116 - 49
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/AitagTagLogServiceImpl.java

@@ -1,11 +1,10 @@
 package cn.com.yusys.yusp.service.impl;
 
-import cn.com.yusys.yusp.commons.exception.BizException;
+import cn.com.yusys.yusp.commons.util.date.DateFormatEnum;
 import cn.com.yusys.yusp.commons.util.date.DateUtils;
 import cn.com.yusys.yusp.config.DataDictionary;
-import cn.com.yusys.yusp.domain.entity.AitagTagInfoEntity;
+import cn.com.yusys.yusp.domain.dto.TagLogDto;
 import cn.com.yusys.yusp.domain.vo.*;
-import cn.com.yusys.yusp.mapper.AitagTagDailyAggDao;
 import cn.com.yusys.yusp.mapper.AitagTagInfoDao;
 import cn.com.yusys.yusp.mapper.AitagTagLogDao;
 import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
@@ -18,16 +17,20 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.net.URL;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
 
 import static cn.com.yusys.yusp.config.DataDictionary.*;
 
@@ -45,8 +48,11 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
     @Autowired
     private AitagTagInfoDao tagInfoDao;
 
-    @Autowired
-    private AitagTagDailyAggDao aggDao;
+
+    private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+    private static final DateTimeFormatter DATETIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
 
     @Override
     public DataOverviewVo dataOverview(SmartTaggingResultVo taggingResult) {
@@ -68,6 +74,7 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
         }
         queryWrapper.in(AitagTagLogEntity::getState, DataDictionary.RESULT_PUSHED,DataDictionary.MANAGER_CONFIRMED);
         queryWrapper.eq(AitagTagLogEntity::getTagScope, PROD_DATA);
+        queryWrapper.eq(AitagTagLogEntity::getIsDelete, NON_DELETE);
         Integer totalConfirmedCnt = this.baseMapper.selectCount(queryWrapper);
         log.info("查询出符合条件的打标完成数据:{} 条",totalConfirmedCnt);
 
@@ -93,6 +100,7 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
         return dataOverview;
     }
 
+
     @Override
     public List<IconResVo> taggingTrend(TaggingTrendReqVo taggingTrendReqVo) {
         String startTaggingTime = taggingTrendReqVo.getStartTaggingTime();
@@ -100,16 +108,95 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
 
         int daysByTwoDatesDef = DateUtils.getDaysByTwoDatesDef(startTaggingTime, endTaggingTime);
         List<IconResVo> taggingTrendResVo = null;
+        List<IconResVo> iconResVos = null;
         if(daysByTwoDatesDef>=15){
-            taggingTrendResVo = this.baseMapper.selectTagReportByDay(taggingTrendReqVo);
-        }else{
             taggingTrendResVo = this.baseMapper.selectTagReportByMonth(taggingTrendReqVo);
+            iconResVos = generateMonthlyStats(startTaggingTime, endTaggingTime);
+        }else{
+            taggingTrendResVo = this.baseMapper.selectTagReportByDay(taggingTrendReqVo);
+            iconResVos = generateDailyStats(startTaggingTime, endTaggingTime);
         }
-        return taggingTrendResVo;
+        return mergeLists(iconResVos,taggingTrendResVo);
     }
 
+
+    public List<IconResVo> mergeLists(List<IconResVo> iconResVos, List<IconResVo> taggingTrendResVo) {
+        // 将taggingTrendResVo转换为Map,key为stat,value为IconResVo对象
+        Map<String, IconResVo> taggingMap = taggingTrendResVo.stream()
+                .collect(Collectors.toMap(
+                        IconResVo::getStat,
+                        item -> item,
+                        (existing, replacement) -> replacement
+                ));
+
+        // 遍历iconResVos,如果stat在taggingMap中存在,则更新val值
+        return iconResVos.stream()
+                .map(iconRes -> {
+                    IconResVo taggingRes = taggingMap.get(iconRes.getStat());
+                    if (taggingRes != null) {
+                        // 创建新对象或修改原对象(根据需要选择)
+                        IconResVo merged = new IconResVo();
+                        merged.setStat(iconRes.getStat());
+                        merged.setVal(taggingRes.getVal());
+                        merged.setStatType(iconRes.getStatType());
+                        return merged;
+                    }
+                    return iconRes;
+                })
+                .collect(Collectors.toList());
+    }
+
+
+
+    /**
+     * Stream版本 - 按天生成
+     */
+    private List<IconResVo> generateDailyStats(String startDateStr, String endDateStr) {
+        return getDateStream(startDateStr.substring(0, 10), endDateStr.substring(0, 10))
+                .map(date -> new IconResVo(date.format(DAY_FORMATTER), 0,"day"))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 生成日期流(JDK 11 兼容)
+     */
+    private Stream<LocalDate> getDateStream(String startDateStr, String endDateStr) {
+        LocalDate startDate = LocalDate.parse(startDateStr.trim(), DAY_FORMATTER);
+        LocalDate endDate = LocalDate.parse(endDateStr.trim(), DAY_FORMATTER);
+        long daysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1;
+        return IntStream.iterate(0, i -> i + 1)
+                .limit(daysBetween)
+                .mapToObj(startDate::plusDays);
+    }
+
+    /**
+     * Stream版本 - 按月生成
+     */
+    private List<IconResVo> generateMonthlyStats(String startDateStr, String endDateStr) {
+        LocalDateTime startTime = LocalDateTime.parse(startDateStr.trim(), DATETIME);
+        LocalDateTime  endTime = LocalDateTime.parse(endDateStr.trim(), DATETIME);
+
+        List<IconResVo> result = new ArrayList<>();
+
+        // 获取开始和结束的年月
+        YearMonth startMonth = YearMonth.from(startTime);
+        YearMonth endMonth = YearMonth.from(endTime);
+
+        // 遍历每个月
+        for (YearMonth currentMonth = startMonth;
+             !currentMonth.isAfter(endMonth);
+             currentMonth = currentMonth.plusMonths(1)) {
+            String stat = currentMonth.format(MONTH_FORMATTER);
+            result.add(new IconResVo(stat, 0,"mother"));
+            // 如果当前月已经是结束月,则跳出循环
+            if (currentMonth.equals(endMonth)) {
+                break;
+            }
+        }
+        return result;
+    }
     @Override
-    public IPage<AitagTagLogEntity> taggingDetails(TaggingTransactionReqVo transactionReqVo) {
+    public IPage<TagLogDto> taggingDetails(TaggingTransactionReqVo transactionReqVo) {
         LambdaQueryWrapper<AitagTagLogEntity> queryWrapper = new LambdaQueryWrapper<>();
         if (StringUtils.isNotBlank(transactionReqVo.getCategoryId())) {
             queryWrapper.apply("result @> '"+transactionReqVo.getCategoryExp()+"'");
@@ -121,52 +208,30 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
             queryWrapper.le(AitagTagLogEntity::getInsertTime, transactionReqVo.getEndTaggingTime());
         }
         if (StringUtils.isNotBlank(transactionReqVo.getLoanApplicationNo())){
-            queryWrapper.le(AitagTagLogEntity::getBusinessAttr, transactionReqVo.getLoanApplicationNo());
+            queryWrapper.eq(AitagTagLogEntity::getBusinessAttr, transactionReqVo.getLoanApplicationNo());
         }
         queryWrapper.eq(AitagTagLogEntity::getTagScope, PROD_DATA);
-
-        return this.page(new Page<>(transactionReqVo.getPage() == null?0:transactionReqVo.getPage(),
-                transactionReqVo.getSize() == null?10:transactionReqVo.getSize()),queryWrapper);
+        queryWrapper.eq(AitagTagLogEntity::getIsDelete, NON_DELETE);
+        queryWrapper.orderByDesc(AitagTagLogEntity::getInsertTime);
+        Page<AitagTagLogEntity> page = this.page(new Page<>(transactionReqVo.getPage() == null ? 0 : transactionReqVo.getPage(),
+                transactionReqVo.getSize() == null ? 10 : transactionReqVo.getSize()), queryWrapper);
+        List<AitagTagLogEntity> records = page.getRecords();
+        List<TagLogDto> tagLogDtos = JSONArray.parseArray(JSONArray.toJSONString(records), TagLogDto.class);
+        IPage<TagLogDto> iPage = new Page<TagLogDto>(page.getCurrent(),page.getSize(),page.getTotal());
+        iPage.setRecords(tagLogDtos);
+        return iPage;
     }
 
     @Override
-    public AitagTagLogEntity show(String id) {
+    public TagLogDto show(String id) {
         AitagTagLogEntity aitagTagLog = this.baseMapper.selectById(id);
-        if(aitagTagLog !=null){
-            String result = "";
-            String feedback = aitagTagLog.getFeedback();
-            if(FEEDBACK_RESULT_REJECT.equals(feedback)){
-                result = aitagTagLog.getFeedbackResult();
-                result = this.getTagPath(result);
-                aitagTagLog.setFeedbackResult(result);
-            }else{
-                result = aitagTagLog.getResult();
-                result = getTagPath(result);
-                aitagTagLog.setResult(result);
-            }
-        }
-        return aitagTagLog;
+        return JSONObject.parseObject(JSONObject.toJSONString(aitagTagLog), TagLogDto.class);
     }
 
-    private String getTagPath(String result) {
-        if(result == null){
-            return "";
-        }
-        result = result.replace("\\", "");
-        List<Map> results = JSONArray.parseArray(result, Map.class);
-        for (Map resultMap: results){
-            String tagId = resultMap.getOrDefault("id","").toString();
-            AitagTagInfoEntity aitagTagInfoEntity = tagInfoDao.selectById(tagId);
-            if(aitagTagInfoEntity!=null){
-                resultMap.put("tag_path",aitagTagInfoEntity.getTagPath());
-            }
-        }
-        return JSONArray.toJSONString(results);
-    }
 
     @Override
     public List<IconResVo> tagDistStats(TagDistStatsReqVo resultVo) {
-        return aggDao.selectTagDistStats(resultVo);
+        return this.baseMapper.selectTagDistStats(resultVo);
     }
 
 
@@ -192,6 +257,8 @@ public class AitagTagLogServiceImpl extends ServiceImpl<AitagTagLogDao, AitagTag
         }
         query.in(AitagTagLogEntity::getState, DataDictionary.RESULT_PUSHED,DataDictionary.MANAGER_CONFIRMED);
         query.eq(AitagTagLogEntity::getTagScope, PROD_DATA);
+        query.eq(AitagTagLogEntity::getIsDelete, NON_DELETE);
+        query.orderByDesc(AitagTagLogEntity::getInsertTime);
         return this.baseMapper.selectList(query);
     }
 

+ 67 - 4
server/yusp-tagging-core/src/main/java/cn/com/yusys/yusp/service/impl/FastApiServiceImpl.java

@@ -1,13 +1,24 @@
 package cn.com.yusys.yusp.service.impl;
 
+import cn.com.yusys.yusp.commons.util.StringUtils;
 import cn.com.yusys.yusp.config.FastApiConfig;
+import cn.com.yusys.yusp.domain.dto.TagResultDto;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingFeedbackRequestDto;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingQueryRequestDto;
 import cn.com.yusys.yusp.domain.dto.fastapidto.AiTaggingRequestDto;
+import cn.com.yusys.yusp.domain.entity.AitagTagLogEntity;
+import cn.com.yusys.yusp.domain.vo.EsbVo.CustomerProfileNode;
+import cn.com.yusys.yusp.domain.vo.EsbVo.CustomerProfileReqVo;
 import cn.com.yusys.yusp.domain.vo.fastapivo.AiTaggingQueryResponseVo;
 import cn.com.yusys.yusp.domain.vo.fastapivo.AiTaggingResponseVo;
+import cn.com.yusys.yusp.mapper.AitagTagLogDao;
+import cn.com.yusys.yusp.service.AitagTagLogService;
 import cn.com.yusys.yusp.service.FastApiService;
+import cn.com.yusys.yusp.service.esb.ESBService;
+import cn.com.yusys.yusp.util.SessionCommonUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.config.RequestConfig;
@@ -23,6 +34,11 @@ import org.springframework.stereotype.Service;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.com.yusys.yusp.config.DataDictionary.CONFIRM_TAGGING;
+import static cn.com.yusys.yusp.config.DataDictionary.FEEDBACK_RESULT_AGREE;
 
 @Slf4j
 @Service
@@ -35,6 +51,12 @@ public class FastApiServiceImpl implements FastApiService {
     private static final String QUERY_PATH = "/api/aitag/v1/query";
     private static final String FEEDBACK_PATH = "/api/aitag/v1/feedback";
 
+    @Autowired
+    private ESBService esbService;
+
+    @Autowired
+    private AitagTagLogDao aitagTagLogDao;
+
     @Override
     public AiTaggingResponseVo tagging(AiTaggingRequestDto request) {
         String url = fastApiConfig.getUrl() + TAGGING_PATH;
@@ -45,8 +67,34 @@ public class FastApiServiceImpl implements FastApiService {
             HttpPost httpPost = new HttpPost(url);
             httpPost.setHeader("Content-Type", "application/json");
 
-            // 构造请求体
-            String requestBody = JSON.toJSONString(request);
+            // 手动构造符合 FastAPI 要求的 JSON 请求体(使用下划线格式)
+            com.alibaba.fastjson.JSONObject jsonBody = new com.alibaba.fastjson.JSONObject();
+            jsonBody.put("business_attr", request.getBusinessAttr());
+            jsonBody.put("phrase", request.getPhrase());
+            if (request.getTagCategoryId() != null) {
+                jsonBody.put("tag_category_id", request.getTagCategoryId());
+            }
+
+            // 添加新增的可选参数(转换为下划线格式)
+            if (request.getUserId() != null) {
+                jsonBody.put("user_id", request.getUserId());
+            }
+            if (request.getUserNm() != null) {
+                jsonBody.put("user_nm", request.getUserNm());
+            }
+            if (request.getContractNo() != null) {
+                jsonBody.put("contract_no", request.getContractNo());
+            }
+            if (request.getUserOrg() != null) {
+                jsonBody.put("user_org", request.getUserOrg());
+            }
+            if (request.getUserEndpoint() != null) {
+                jsonBody.put("user_endpoint", request.getUserEndpoint());
+            }
+
+            String requestBody = jsonBody.toJSONString();
+            log.info("请求体:{}", requestBody);
+
             httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
 
             try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
@@ -73,6 +121,7 @@ public class FastApiServiceImpl implements FastApiService {
         }
     }
 
+
     @Override
     public AiTaggingQueryResponseVo query(AiTaggingQueryRequestDto request) {
         String url = fastApiConfig.getUrl() + QUERY_PATH + "?business_attr=" + encodeParam(request.getBusinessAttr());
@@ -121,7 +170,20 @@ public class FastApiServiceImpl implements FastApiService {
             jsonBody.put("user_id", request.getUserId());
             jsonBody.put("user_nm", request.getUserNm());
             jsonBody.put("feedback", request.getFeedback());
-            jsonBody.put("feedback_result", request.getFeedbackResult());
+
+            // 将 JsonNode 转换为原始 JSON 字符串
+            String feedbackResultStr = null;
+            if (request.getFeedbackResult() != null) {
+                if (request.getFeedbackResult().isTextual()) {
+                    // 如果本身就是字符串
+                    feedbackResultStr = request.getFeedbackResult().asText();
+                } else {
+                    // 如果是数组或对象,序列化为 JSON 字符串
+                    feedbackResultStr = request.getFeedbackResult().toString();
+                }
+            }
+            jsonBody.put("feedback_result", feedbackResultStr);
+
             jsonBody.put("contract_no", request.getContractNo());
             jsonBody.put("user_org", request.getUserOrg());
             jsonBody.put("user_endpoint", request.getUserEndpoint());
@@ -144,7 +206,6 @@ public class FastApiServiceImpl implements FastApiService {
                     errorResponse.setMessage("AI 打标反馈接口调用失败:" + (responseBody.isEmpty() ? "服务器内部错误" : responseBody));
                     return errorResponse;
                 }
-
                 return JSON.parseObject(responseBody, AiTaggingResponseVo.class);
             }
         } catch (Exception e) {
@@ -155,6 +216,8 @@ public class FastApiServiceImpl implements FastApiService {
             return errorResponse;
         }
     }
+// ... existing code ...
+
 
 
     // 辅助方法用于 URL 编码

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

@@ -49,5 +49,31 @@ public class SessionCommonUtil {
         }
         throw BizException.of("E009");
     }
+    public static String getUserId() {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        if (requestAttributes != null) {
+            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
+            String authorization = request.getHeader("Authorization");
+            if (authorization != null) {
+                String jwtToken = authorization.startsWith("Bearer ") ? authorization.substring(7) : authorization;
+                String jsonStr = (String) redisTemplate.opsForValue().get("access:"+jwtToken);
+                if (jsonStr != null) {
+                    // 解析JSON数组
+                    JSONArray objects = JSONArray.parseArray(jsonStr);
+                    if (objects.size() >= 2) {
+                        String map = objects.get(1).toString();
+                        JSONObject jsonObject = JSONObject.parseObject(map);
+                        String userInfo = jsonObject.getString("userInfo");
+                        JSONObject userInfoMap = JSONObject.parseObject(userInfo);
+                        return userInfoMap.getString("userId");
+                    }
+                }
+            }else{
+                throw BizException.of("E009");
+            }
+        }
+        throw BizException.of("E009");
+    }
+
 
 }

+ 0 - 8
server/yusp-tagging-core/src/main/resources/mapper/AitagApiLogMapper.xml

@@ -25,14 +25,6 @@
                  )
     </insert>
 
-    <!-- 根据日期获取最大的日志ID -->
-    <select id="getMaxIdByDate" resultType="string">
-        SELECT id
-        FROM aitag_api_log
-        WHERE id LIKE CONCAT('API', #{dateStr}, '%')
-        ORDER BY id DESC
-            LIMIT 1
-    </select>
 
     <!-- 统计符合条件的日志数量 -->
     <select id="countApiLogs" resultType="long">

+ 5 - 17
server/yusp-tagging-core/src/main/resources/mapper/AitagTagCategoryMapper.xml

@@ -2,13 +2,6 @@
 <!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="selectPageCategoriesWithFilter" resultType="cn.com.yusys.yusp.domain.entity.AitagTagCategory">
         SELECT * FROM aitag_tag_category
@@ -16,6 +9,7 @@
         <if test="categoryNm != null and categoryNm != ''">
             AND category_nm LIKE CONCAT('%', #{categoryNm}, '%')
         </if>
+        ORDER BY state ASC, update_time DESC
         LIMIT #{offset}, #{limit}
     </select>
 
@@ -28,13 +22,6 @@
         </if>
     </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
@@ -43,8 +30,8 @@
 
     <!-- 新增 -->
     <insert id="insertCategory">
-        INSERT INTO aitag_tag_category (id, category_nm, category_desc, state, is_delete)
-        VALUES (#{id}, #{categoryNm}, #{categoryDesc}, 1, 0)
+        INSERT INTO aitag_tag_category (id, category_nm, category_desc, state, is_delete, create_time, update_time)
+        VALUES (#{id}, #{categoryNm}, #{categoryDesc}, 1, 0, #{createTime}, #{updateTime})
     </insert>
 
     <!-- 按 ID 查询 -->
@@ -55,7 +42,7 @@
     <!-- 更新 -->
     <update id="updateCategory">
         UPDATE aitag_tag_category
-        SET category_nm = #{categoryNm}, category_desc = #{categoryDesc}
+        SET category_nm = #{categoryNm}, category_desc = #{categoryDesc}, update_time = #{updateTime}
         WHERE id = #{id}
     </update>
 
@@ -83,6 +70,7 @@
     <select id="selectPageEnabledCategories" resultType="cn.com.yusys.yusp.domain.entity.AitagTagCategory">
         SELECT * FROM aitag_tag_category
         WHERE is_delete = 0 AND state = 0
+        ORDER BY update_time DESC
             LIMIT #{offset}, #{limit}
     </select>
 

+ 0 - 37
server/yusp-tagging-core/src/main/resources/mapper/AitagTagDailyAggMapper.xml

@@ -1,37 +0,0 @@
-<?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.AitagTagDailyAggDao">
-    <resultMap id="aitagTagDailyAgg" type="cn.com.yusys.yusp.domain.entity.AitagTagDailyAggEntity">
-        <id column="id" jdbcType="VARCHAR" property="id"/>
-        <result column="agg_date" jdbcType="VARCHAR" property="aggDate"/>
-        <result column="category_id" jdbcType="VARCHAR" property="categoryId"/>
-        <result column="tag_nm" jdbcType="VARCHAR" property="tagNm"/>
-        <result column="tag_count" jdbcType="INTEGER" property="tagCount"/>
-    </resultMap>
-
-
-    <select id="selectTagDistStats" resultType="cn.com.yusys.yusp.domain.vo.IconResVo" parameterType="cn.com.yusys.yusp.domain.vo.TagDistStatsReqVo">
-        SELECT
-        tag_nm as stat,
-        tag_count AS val
-        FROM aitag_tag_daily_agg
-        <where>
-            <if test="startTaggingTime != null and startTaggingTime != ''">
-                AND agg_date >= #{startTaggingTime}
-            </if>
-            <if test="endTaggingTime != null and endTaggingTime != ''">
-                AND #{endTaggingTime} >= agg_date
-            </if>
-            <if test="categoryId != null and categoryId != '' ">
-                AND #{categoryId} = category_id
-            </if>
-            <if test="sort == 'ase'">
-                order by tag_count
-            </if>
-            <if test="sort == 'desc' ">
-                order by tag_count desc
-            </if>
-        </where>
-        Limit 6
-    </select>
-</mapper>

+ 2 - 1
server/yusp-tagging-core/src/main/resources/mapper/AitagTagInfoMapper.xml

@@ -68,8 +68,9 @@
         FROM aitag_tag_info ti
                  LEFT JOIN (SELECT *
                             FROM aitag_tag_info_version
-                            WHERE is_delete = '0')  tiv ON ti.tag_code = tiv.tag_code
+                            WHERE is_delete = '0')  tiv ON ti.id = tiv.primary_version_id
         where ti.id= #{id}
+        order by version_revision_time desc
     </select>
 
 </mapper>

+ 64 - 25
server/yusp-tagging-core/src/main/resources/mapper/AitagTagLogMapper.xml

@@ -29,6 +29,7 @@
         FROM aitag_tag_log
         WHERE
             tag_scope = 0
+        AND is_delete = 0
             <if test="startTaggingTime != null and startTaggingTime != ''">
                 AND insert_time >= #{startTaggingTime}
             </if>
@@ -42,11 +43,13 @@
 
     <select id="selectTagReportByDay" resultType="cn.com.yusys.yusp.domain.vo.IconResVo" parameterType="cn.com.yusys.yusp.domain.vo.SmartTaggingResultVo">
         SELECT
-        DATE_FORMAT(insert_time, '%Y年%m月%d日') AS stat,
-        COUNT(1) AS val
+        DATE_FORMAT(insert_time, '%Y-%m-%d') AS stat,
+        COUNT(1) AS val,
+        'day' AS stat_type
         FROM aitag_tag_log
         WHERE
         tag_scope = 0
+        AND is_delete = 0
             <if test="startTaggingTime != null and startTaggingTime != ''">
                 AND insert_time >= #{startTaggingTime}
             </if>
@@ -56,17 +59,19 @@
             <if test="categoryId != null and categoryId != ''">
                 AND result @> #{categoryExp}::jsonb
             </if>
-        GROUP BY DATE_FORMAT(insert_time, '%Y年%m月%d日')
+        GROUP BY DATE_FORMAT(insert_time, '%Y-%m-%d')
         ORDER BY stat;
     </select>
 
     <select id="selectTagReportByMonth" resultType="cn.com.yusys.yusp.domain.vo.IconResVo" parameterType="cn.com.yusys.yusp.domain.vo.SmartTaggingResultVo">
         SELECT
-        DATE_FORMAT(insert_time, '%Y年%m月') AS stat,
-        COUNT(1) AS val
+        DATE_FORMAT(insert_time, '%Y-%m') AS stat,
+        COUNT(1) AS val,
+        'month' AS stat_type
         FROM aitag_tag_log
         WHERE
         tag_scope = 0
+        AND is_delete = 0
             <if test="startTaggingTime != null and startTaggingTime != ''">
                 AND insert_time >= #{startTaggingTime}
             </if>
@@ -76,37 +81,71 @@
             <if test="categoryId != null and categoryId != ''">
                 AND result @> #{categoryExp}::jsonb
             </if>
-        GROUP BY DATE_FORMAT(insert_time, '%Y年%m月')
+        GROUP BY DATE_FORMAT(insert_time, '%Y-%m')
         ORDER BY stat;
     </select>
 
-    <select id="selectTagReportByWeek" resultType="cn.com.yusys.yusp.domain.vo.IconResVo" parameterType="cn.com.yusys.yusp.domain.vo.SmartTaggingResultVo">
-        SELECT
-        YEARWEEK(insert_time, 1) AS stat,
-        COUNT(1) AS val
-        FROM aitag_tag_log
-        WHERE
-        tag_scope = 0
-            <if test="startTaggingTime != null and  startTaggingTime != ''">
-                AND insert_time >= #{startTaggingTime}
-            </if>
-            <if test="endTaggingTime != null and  endTaggingTime != ''">
-                AND #{endTaggingTime} >= insert_time
-            </if>
-            <if test="categoryId != null and categoryId != ''">
-                AND result @> #{categoryExp}::jsonb
-            </if>
-        GROUP BY YEARWEEK(insert_time, 1)
-        ORDER BY stat;
-    </select>
 
     <select id="selectByInsertTime" resultType="cn.com.yusys.yusp.domain.entity.AitagTagLogEntity">
         SELECT id, app_id, insert_time, business_attr, phrase, attachment, attachment_url, `result`, feedback_user_id, feedback_user_nm, feedback_time, feedback, feedback_result, state, consuming_time
         FROM aitag_tag_log
         WHERE
             tag_scope = 0
+        AND is_delete = 0
         AND DATE(insert_time) = #{insertTime}
         AND state in ('2','3')
     </select>
 
+
+    <select id="selectTagDistStats" resultType="cn.com.yusys.yusp.domain.vo.IconResVo" parameterType="cn.com.yusys.yusp.domain.vo.TagDistStatsReqVo">
+        SELECT
+        tag_name AS stat,
+        COUNT(1) AS val
+        FROM (
+        -- 从feedback_result获取
+        SELECT elem->>'tag_name' AS tag_name
+        FROM ai_tagging.aitag_tag_log t
+        CROSS JOIN LATERAL jsonb_array_elements(t.feedback_result) elem
+        WHERE t.is_delete = 0
+        AND t.tag_scope = 0
+        AND t.state in ('2','3')
+        AND t.feedback_result IS NOT NULL
+        AND jsonb_array_length(t.feedback_result) > 0
+        AND elem->>'passr' = 'true'
+        <if test="categoryId != null and categoryId != '' ">
+            AND elem->>'category_id' = #{categoryId}
+        </if>
+        <if test="startTaggingTime != null and startTaggingTime != ''">
+            AND insert_time >= #{startTaggingTime}
+        </if>
+        <if test="endTaggingTime != null and endTaggingTime != ''">
+            AND #{endTaggingTime} >= insert_time
+        </if>
+        UNION ALL
+        SELECT elem->>'tag_name' AS tag_name
+        FROM ai_tagging.aitag_tag_log t
+        CROSS JOIN LATERAL jsonb_array_elements(t.result) elem
+        WHERE t.is_delete = 0
+        AND t.tag_scope = 0
+        AND t.state in ('2','3')
+        AND t.feedback_result IS NULL
+        AND t.result IS NOT null
+        AND jsonb_array_length(t.result) > 0
+        AND elem->>'passr' = 'true'
+        <if test="categoryId != null and categoryId != '' ">
+            AND elem->>'category_id' = #{categoryId}
+        </if>
+        <if test="startTaggingTime != null and startTaggingTime != ''">
+            AND insert_time >= #{startTaggingTime}
+        </if>
+        <if test="endTaggingTime != null and endTaggingTime != ''">
+            AND #{endTaggingTime} >= insert_time
+        </if>
+        ) valid_tags
+        WHERE tag_name IS NOT NULL
+        GROUP BY tag_name
+        ORDER BY val desc
+        Limit 6
+    </select>
+
 </mapper>

+ 11 - 2
server/yusp-tagging-core/src/main/resources/messages/yusp_input_msg.properties

@@ -101,9 +101,18 @@ NO_DATA_PERIOD=\u8BE5\u7EDF\u8BA1\u5468\u671F\u6682\u672A\u5B9E\u73B0
 E001=\u6240\u5C5E\u5927\u7C7B\u4E0D\u80FD\u4E3A\u7A7A
 E002=\u4E0A\u4F20\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A
 E003=\u4EC5\u652F\u6301 .xlsx \u6216 .xls \u683C\u5F0F\u6587\u4EF6
-E004=Excel \u4E2D\u6CA1\u6709\u6709\u6548\u6570\u636E
+E004=\u6587\u4EF6\u4E2D\u6CA1\u6709\u6709\u6548\u6570\u636E
 E005=\u53D1\u73B0\u91CD\u590D\u7684\u6807\u7B7E\u540D\u79F0:[{0}]
 E006=\u6821\u9A8C\u5931\u8D25\uFF1A\u6807\u7B7E [{0}] \u7684\u7236\u6807\u7B7E [{1}] \u4E0D\u5B58\u5728\u4E8E\u6587\u4EF6\u4E2D\u3002
 E007=\u68C0\u6D4B\u5230\u6807\u7B7E\u5C42\u7EA7\u73AF\u8DEF:{0}
 E008=\u8C03\u7528AI\u6253\u6807\u63A5\u53E3\u5931\u8D25
-E009=\u83B7\u53D6\u7528\u6237\u767B\u9646\u4FE1\u606F\u4E3A\u7A7A
+E009=\u83B7\u53D6\u7528\u6237\u767B\u9646\u4FE1\u606F\u4E3A\u7A7A
+E010=\u6807\u7B7E\u7F16\u53F7\u91CD\u590D\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165
+E011=\u6807\u7B7E\u5B58\u5728\u4E0B\u7EA7\u6807\u7B7E\uFF0C\u65E0\u6CD5\u5220\u9664
+E012=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+E013=\u6807\u7B7E\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+E014=\u6807\u7B7E\u7F16\u53F7\u4E0D\u6B63\u786E
+E015=\u6A21\u677F\u683C\u5F0F\u4E0D\u6B63\u786E
+E016=\u6570\u636E\u884C\u6570\u8D85\u8FC7\u9650\u5236
+E017=\u6587\u4EF6\u5C42\u7EA7\u4E0D\u80FD\u8D85\u8FC74\u7EA7
+E018=\u6240\u5C5E\u5927\u7C7B\u8F93\u5165\u6709\u8BEF

binární
server/yusp-tagging-core/src/main/resources/template/tag_batch_Import_template.xlsx