Просмотр исходного кода

feat(report): DAN报告新增按维度重解析接口,修复历史误解析并刷新结果/展示块/汇总

Xiaogang Liao 4 дней назад
Родитель
Сommit
31def344d0

+ 14 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/ReportParserAdminController.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.*;
 import com.etotem.cfc.entity.*;
 import com.etotem.cfc.mapper.*;
 import com.etotem.cfc.mapper.*;
+import com.etotem.cfc.service.DanReportUploadService;
 import com.etotem.cfc.service.ReportImportService;
 import com.etotem.cfc.service.ReportImportService;
 import com.etotem.cfc.service.ReportScriptGeneratorService;
 import com.etotem.cfc.service.ReportScriptGeneratorService;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -44,6 +45,9 @@ public class ReportParserAdminController {
     @Resource
     @Resource
     private ReportImportService reportImportService;
     private ReportImportService reportImportService;
 
 
+    @Resource
+    private DanReportUploadService danReportUploadService;
+
     @Resource
     @Resource
     private ReportScriptGeneratorService reportScriptGeneratorService;
     private ReportScriptGeneratorService reportScriptGeneratorService;
 
 
@@ -333,4 +337,14 @@ public class ReportParserAdminController {
             return Result.error("生成失败: " + e.getMessage());
             return Result.error("生成失败: " + e.getMessage());
         }
         }
     }
     }
+
+    @PostMapping("/dan/reparse")
+    public Result<Map<String, Object>> reparseDan(@RequestBody Map<String, Object> params) {
+        Object uploadId = params.get("uploadId");
+        Object dimension = params.get("dimension");
+        if (uploadId == null || dimension == null) {
+            return Result.error("uploadId/dimension 必填");
+        }
+        return danReportUploadService.reparseUpload(((Number) uploadId).longValue(), String.valueOf(dimension));
+    }
 }
 }

+ 95 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/DanReportUploadService.java

@@ -21,7 +21,10 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
@@ -49,6 +52,9 @@ public class DanReportUploadService {
     @Resource
     @Resource
     private FileStorageService fileStorageService;
     private FileStorageService fileStorageService;
 
 
+    @Resource
+    private StorageService storageService;
+
     @Resource
     @Resource
     private DimensionScoreService dimensionScoreService;
     private DimensionScoreService dimensionScoreService;
 
 
@@ -282,6 +288,95 @@ if (!DanReportUpload.DIM_MIND.equals(dimension) && !DanReportUpload.DIM_WISDOM.e
         return Result.success(vo);
         return Result.success(vo);
     }
     }
 
 
+    // 维护:按正确维度重新解析已入库 DAN 上传,并刷新关联结果/展示块/汇总/五维能量
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map<String, Object>> reparseUpload(Long uploadId, String dimension) {
+        DanReportUpload upload = uploadMapper.selectById(uploadId);
+        if (upload == null || upload.getDeletedAt() != null) {
+            return Result.error("上传记录不存在");
+        }
+        if (!DanReportUpload.DIM_MIND.equals(dimension) && !DanReportUpload.DIM_WISDOM.equals(dimension)
+                && !DanReportUpload.DIM_COGNITION.equals(dimension) && !DanReportUpload.DIM_LEARNING.equals(dimension)
+                && !DanReportUpload.DIM_BEHAVIOR.equals(dimension) && !DanReportUpload.DIM_CAREER.equals(dimension)
+                && !DanReportUpload.DIM_CAMPUS.equals(dimension) && !DanReportUpload.DIM_ADOLESCENT.equals(dimension)) {
+            return Result.error("dimension 非法: " + dimension);
+        }
+        File pdf;
+        try {
+            pdf = new File(storageService.getFilePath(upload.getFileUrl()));
+        } catch (Exception e) {
+            return Result.error("文件路径解析失败: " + e.getMessage());
+        }
+        if (!pdf.exists()) {
+            return Result.error("文件不存在: " + pdf.getAbsolutePath());
+        }
+        DanReportParseService.DanParsedReport parsed;
+        try (InputStream in = new FileInputStream(pdf)) {
+            parsed = danReportParseService.parse(in, dimension);
+        } catch (Exception e) {
+            log.error("DAN重解析失败 uploadId={}", uploadId, e);
+            return Result.error("解析失败: " + e.getMessage());
+        }
+        if (parsed == null || parsed.getItems() == null || parsed.getItems().isEmpty()) {
+            return Result.error("未能提取有效指标");
+        }
+        try {
+            upload.setParsedItems(objectMapper.writeValueAsString(parsed.getItems()));
+            upload.setParsedExtra(objectMapper.writeValueAsString(parsed.getExtra()));
+        } catch (Exception e) {
+            return Result.error("序列化失败: " + e.getMessage());
+        }
+        upload.setDimension(dimension);
+        upload.setReportType(parsed.getReportType());
+        upload.setParsedSummary(parsed.getSummary());
+        upload.setParsedSuggestions(parsed.getSuggestions());
+        upload.setParsedAt(new Date());
+        upload.setUpdatedAt(new Date());
+        uploadMapper.updateById(upload);
+
+        Long resultId = upload.getLinkedResultId();
+        if (resultId != null) {
+            DanAssessmentResult result = resultMapper.selectById(resultId);
+            if (result != null) {
+                result.setDimension(dimension);
+                result.setStructuredAnalysis(upload.getParsedItems());
+                result.setAnalysisReport(upload.getParsedSummary());
+                result.setGrowthSuggestions(upload.getParsedSuggestions());
+                Integer overall = extractOverallScore(upload.getParsedItems());
+                if (overall != null) {
+                    result.setOverallScore(overall);
+                    result.setDanLevel(mapScoreToDanLevel(overall));
+                }
+                result.setUpdatedAt(new Date());
+                resultMapper.updateById(result);
+            }
+        }
+        try {
+            reportBlockService.save("dan", upload.getId(),
+                    reportBlockAssembler.assembleDanByType(upload.getParsedItems(),
+                            upload.getParsedSummary(), upload.getParsedSuggestions()));
+        } catch (Exception e) {
+            log.warn("DAN重解析保存展示块失败 uploadId={}: {}", uploadId, e.getMessage());
+        }
+        try {
+            reportSummaryService.syncFromDanReport(upload);
+        } catch (Exception e) {
+            log.warn("DAN重解析同步汇总失败 uploadId={}: {}", uploadId, e.getMessage());
+        }
+        if (resultId != null) {
+            try {
+                dimensionScoreService.refreshFromDanAssessment(upload.getFamilyMemberId(), resultId, dimension);
+            } catch (Exception ignored) {
+            }
+        }
+        Map<String, Object> data = new LinkedHashMap<>();
+        data.put("uploadId", upload.getId());
+        data.put("dimension", dimension);
+        data.put("reportType", upload.getReportType());
+        data.put("itemCount", parsed.getItems().size());
+        return Result.success(data);
+    }
+
     /**
     /**
      * DAN 确认后自动生成方案,维度按测评维度映射(mind→mind,wisdom→wisdom)。
      * DAN 确认后自动生成方案,维度按测评维度映射(mind→mind,wisdom→wisdom)。
      * 失败仅记日志,不阻断确认主流程。
      * 失败仅记日志,不阻断确认主流程。