diff --git a/ccdi-project/pom.xml b/ccdi-project/pom.xml
index 77febc35..37eb6a99 100644
--- a/ccdi-project/pom.xml
+++ b/ccdi-project/pom.xml
@@ -49,6 +49,12 @@
easyexcel
+
+
+ org.apache.pdfbox
+ pdfbox
+
+
org.springframework.boot
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java
index 605fe824..2bbc9e4d 100644
--- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java
@@ -29,6 +29,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@@ -181,4 +182,14 @@ public class CcdiProjectOverviewController extends BaseController {
public void exportRiskDetails(HttpServletResponse response, Long projectId) {
overviewService.exportRiskDetails(response, projectId);
}
+
+ /**
+ * 一键导出结果总览报告
+ */
+ @RequestMapping(value = "/report/export", method = { RequestMethod.GET, RequestMethod.POST })
+ @Operation(summary = "一键导出结果总览报告")
+ @PreAuthorize("@ss.hasPermi('ccdi:project:query')")
+ public void exportOverviewReport(HttpServletResponse response, Long projectId) {
+ overviewService.exportOverviewReport(response, projectId);
+ }
}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportModelSummaryVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportModelSummaryVO.java
new file mode 100644
index 00000000..792cef84
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportModelSummaryVO.java
@@ -0,0 +1,20 @@
+package com.ruoyi.ccdi.project.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 结果总览报告风险模型汇总
+ */
+@Data
+public class CcdiProjectOverviewReportModelSummaryVO {
+
+ private String modelCode;
+
+ private String modelName;
+
+ private Integer warningCount;
+
+ private Integer peopleCount;
+
+ private String peopleNames;
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportParamVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportParamVO.java
new file mode 100644
index 00000000..4fce77d3
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportParamVO.java
@@ -0,0 +1,20 @@
+package com.ruoyi.ccdi.project.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 结果总览报告参数配置项
+ */
+@Data
+public class CcdiProjectOverviewReportParamVO {
+
+ private String modelName;
+
+ private String paramName;
+
+ private String paramValue;
+
+ private String paramUnit;
+
+ private String paramDesc;
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportSuspiciousTransactionVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportSuspiciousTransactionVO.java
new file mode 100644
index 00000000..44d28923
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportSuspiciousTransactionVO.java
@@ -0,0 +1,35 @@
+package com.ruoyi.ccdi.project.domain.vo;
+
+import java.math.BigDecimal;
+import lombok.Data;
+
+/**
+ * 结果总览报告涉疑交易明细
+ */
+@Data
+public class CcdiProjectOverviewReportSuspiciousTransactionVO {
+
+ private Long bankStatementId;
+
+ private String trxDate;
+
+ private String leAccountNo;
+
+ private String leAccountName;
+
+ private String customerAccountName;
+
+ private String customerAccountNo;
+
+ private String relatedStaffName;
+
+ private String relatedStaffCode;
+
+ private String userMemo;
+
+ private String cashType;
+
+ private String hitTags;
+
+ private BigDecimal displayAmount;
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportUploadSubjectVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportUploadSubjectVO.java
new file mode 100644
index 00000000..98da9f5b
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportUploadSubjectVO.java
@@ -0,0 +1,22 @@
+package com.ruoyi.ccdi.project.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 结果总览报告上传主体汇总
+ */
+@Data
+public class CcdiProjectOverviewReportUploadSubjectVO {
+
+ private String subjectName;
+
+ private String accountNos;
+
+ private String minTrxDate;
+
+ private String maxTrxDate;
+
+ private Integer fileCount;
+
+ private String dataPeriod;
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportVO.java
new file mode 100644
index 00000000..fa7b0576
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewReportVO.java
@@ -0,0 +1,33 @@
+package com.ruoyi.ccdi.project.domain.vo;
+
+import com.ruoyi.ccdi.project.domain.CcdiProject;
+import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
+import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
+
+/**
+ * 结果总览一键导出报告
+ */
+@Data
+public class CcdiProjectOverviewReportVO {
+
+ private CcdiProject project;
+
+ private List uploadSubjects = new ArrayList<>();
+
+ private List params = new ArrayList<>();
+
+ private CcdiProjectOverviewDashboardVO dashboard = new CcdiProjectOverviewDashboardVO();
+
+ private List modelSummaries = new ArrayList<>();
+
+ private List riskPeople = new ArrayList<>();
+
+ private List suspiciousTransactions = new ArrayList<>();
+
+ private List illegalPeople = new ArrayList<>();
+
+ private List abnormalAccounts = new ArrayList<>();
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java
index 496d145c..4fb4b43a 100644
--- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java
@@ -13,6 +13,9 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportSuspiciousTransactionVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportUploadSubjectVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
@@ -72,6 +75,40 @@ public interface CcdiProjectOverviewMapper {
*/
List selectRiskModelCardsByProjectId(@Param("projectId") Long projectId);
+ /**
+ * 查询报告上传主体汇总
+ *
+ * @param projectId 项目ID
+ * @return 上传主体汇总
+ */
+ List selectReportUploadSubjects(@Param("projectId") Long projectId);
+
+ /**
+ * 查询报告风险模型汇总
+ *
+ * @param projectId 项目ID
+ * @return 风险模型汇总
+ */
+ List selectReportRiskModelSummaries(@Param("projectId") Long projectId);
+
+ /**
+ * 查询报告风险人员与异常点
+ *
+ * @param projectId 项目ID
+ * @return 风险人员与异常点
+ */
+ List selectReportRiskPeople(@Param("projectId") Long projectId);
+
+ /**
+ * 查询报告涉疑交易明细
+ *
+ * @param query 查询条件
+ * @return 涉疑交易明细
+ */
+ List selectReportSuspiciousTransactionList(
+ @Param("query") CcdiProjectSuspiciousTransactionQueryDTO query
+ );
+
/**
* 分页查询风险模型命中人员
*
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java
index 854906b4..4c53fc5f 100644
--- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java
@@ -125,6 +125,15 @@ public interface ICcdiProjectOverviewService {
default void exportRiskDetails(HttpServletResponse response, Long projectId) {
}
+ /**
+ * 一键导出结果总览报告
+ *
+ * @param response 响应流
+ * @param projectId 项目ID
+ */
+ default void exportOverviewReport(HttpServletResponse response, Long projectId) {
+ }
+
/**
* 导出项目员工负面征信
*
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewReportPdfExporter.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewReportPdfExporter.java
new file mode 100644
index 00000000..9310d10f
--- /dev/null
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewReportPdfExporter.java
@@ -0,0 +1,603 @@
+package com.ruoyi.ccdi.project.service.impl;
+
+import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
+import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportSuspiciousTransactionVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportUploadSubjectVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewStatVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.file.FileUtils;
+import jakarta.servlet.http.HttpServletResponse;
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import org.apache.fontbox.ttf.TrueTypeCollection;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.springframework.stereotype.Component;
+
+/**
+ * 结果总览PDF报告导出器
+ */
+@Component
+public class CcdiProjectOverviewReportPdfExporter {
+
+ private static final String CONTENT_TYPE = "application/pdf";
+ private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+ private static final DecimalFormat MONEY_FORMAT = new DecimalFormat("#,##0.00");
+
+ public void export(HttpServletResponse response, CcdiProjectOverviewReportVO report) throws IOException {
+ response.setContentType(CONTENT_TYPE);
+ FileUtils.setAttachmentResponseHeader(
+ response,
+ safeFileName(report.getProject().getProjectName()) + "_初核结果报告.pdf"
+ );
+
+ try (PDDocument document = new PDDocument()) {
+ PDType0Font font = loadChineseFont(document);
+ PdfPageWriter writer = new PdfPageWriter(document, font);
+ writer.newPage();
+ writeCover(writer, report);
+ writeUploadSubjects(writer, report.getUploadSubjects());
+ writeParams(writer, report.getParams());
+ writeRiskModels(writer, report);
+ writeRiskDetails(writer, report);
+ writer.close();
+ document.save(response.getOutputStream());
+ }
+ }
+
+ private void writeCover(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
+ writer.title("初核结果报告");
+ writer.text("项目名称:" + safeText(report.getProject().getProjectName()), 12, Color.GRAY);
+ writer.text("导出时间:" + LocalDateTime.now().format(EXPORT_TIME_FORMATTER), 12, Color.GRAY);
+ writer.separator();
+ }
+
+ private void writeUploadSubjects(
+ PdfPageWriter writer,
+ List rows
+ ) throws IOException {
+ writer.section("一、上传文件");
+ writer.table(
+ List.of("序号", "主体名称", "主体账号", "数据周期", "文件数"),
+ indexedRows(rows).stream()
+ .map(item -> List.of(
+ item.index(),
+ safeText(item.row().getSubjectName()),
+ maskAccountList(item.row().getAccountNos()),
+ safeText(item.row().getDataPeriod()),
+ formatCount(item.row().getFileCount(), "个")
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.07F, 0.2F, 0.45F, 0.14F, 0.14F },
+ "暂无上传文件数据"
+ );
+ }
+
+ private void writeParams(PdfPageWriter writer, List rows) throws IOException {
+ writer.section("二、参数配置");
+ writer.table(
+ List.of("模型名称", "监测项", "参数值", "单位", "描述"),
+ rows.stream()
+ .map(item -> List.of(
+ safeText(item.getModelName()),
+ safeText(item.getParamName()),
+ safeText(item.getParamValue()),
+ safeText(item.getParamUnit()),
+ safeText(item.getParamDesc())
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.18F, 0.26F, 0.14F, 0.12F, 0.3F },
+ "暂无参数配置数据"
+ );
+ }
+
+ private void writeRiskModels(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
+ writer.section("三、风险模型");
+ writer.metrics(report.getDashboard().getStats());
+ writer.subsection("风险模型汇总");
+ writer.table(
+ List.of("模型名称", "预警数量", "涉及人员"),
+ report.getModelSummaries().stream()
+ .map(item -> List.of(
+ safeText(item.getModelName()),
+ String.valueOf(defaultZero(item.getWarningCount())),
+ formatPeopleSummary(item)
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.26F, 0.14F, 0.6F },
+ "暂无风险模型汇总数据"
+ );
+
+ writer.subsection("风险人员与异常点");
+ writer.table(
+ List.of("姓名", "工号", "身份证号", "所属部门", "命中模型", "异常标签"),
+ report.getRiskPeople().stream()
+ .map(item -> List.of(
+ safeText(item.getStaffName()),
+ safeText(item.getStaffCode()),
+ maskIdCard(item.getIdNo()),
+ safeText(item.getDepartment()),
+ joinText(item.getModelNames()),
+ formatHitTags(item.getHitTagList())
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.1F, 0.11F, 0.16F, 0.14F, 0.24F, 0.25F },
+ "暂无风险人员与异常点数据"
+ );
+ }
+
+ private void writeRiskDetails(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
+ writer.section("四、风险明细");
+ writer.subsection("1. 涉疑交易明细表(共" + report.getSuspiciousTransactions().size() + "条)");
+ writer.table(
+ List.of("交易时间", "本方账户", "对方账户", "关联员工", "摘要/交易类型", "异常标签", "交易金额"),
+ report.getSuspiciousTransactions().stream()
+ .map(item -> List.of(
+ safeText(item.getTrxDate()),
+ formatAccount(item.getLeAccountNo(), item.getLeAccountName()),
+ formatAccount(item.getCustomerAccountNo(), item.getCustomerAccountName()),
+ formatRelatedStaff(item.getRelatedStaffName(), item.getRelatedStaffCode()),
+ formatSummaryAndCashType(item.getUserMemo(), item.getCashType()),
+ safeText(item.getHitTags()),
+ formatMoney(item.getDisplayAmount())
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.14F, 0.16F, 0.16F, 0.12F, 0.17F, 0.16F, 0.09F },
+ "暂无涉疑交易明细"
+ );
+
+ writer.subsection("2. 违法信息人员表(共" + report.getIllegalPeople().size() + "人)");
+ writer.table(
+ List.of("姓名", "身份证号", "最近查询日期", "民事案件笔数", "民事案件金额", "强制执行笔数", "强制执行金额", "行政处罚笔数", "行政处罚金额"),
+ report.getIllegalPeople().stream()
+ .map(item -> List.of(
+ safeText(item.getPersonName()),
+ maskIdCard(item.getPersonId()),
+ safeText(item.getQueryDate()),
+ String.valueOf(defaultZero(item.getCivilCnt())),
+ formatMoney(item.getCivilLmt()),
+ String.valueOf(defaultZero(item.getEnforceCnt())),
+ formatMoney(item.getEnforceLmt()),
+ String.valueOf(defaultZero(item.getAdmCnt())),
+ formatMoney(item.getAdmLmt())
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.09F, 0.15F, 0.12F, 0.1F, 0.11F, 0.1F, 0.11F, 0.1F, 0.12F },
+ "暂无违法信息人员数据"
+ );
+
+ writer.subsection("3. 异常账户信息表(共" + report.getAbnormalAccounts().size() + "条)");
+ writer.table(
+ List.of("账号", "开户人", "银行", "异常类型", "异常发生时间", "状态"),
+ report.getAbnormalAccounts().stream()
+ .map(item -> List.of(
+ maskAccount(item.getAccountNo()),
+ safeText(item.getAccountName()),
+ safeText(item.getBankName()),
+ safeText(item.getAbnormalType()),
+ safeText(item.getAbnormalTime()),
+ safeText(item.getStatus())
+ ))
+ .collect(Collectors.toList()),
+ new float[] { 0.18F, 0.13F, 0.2F, 0.23F, 0.14F, 0.12F },
+ "暂无异常账户信息"
+ );
+ }
+
+ private PDType0Font loadChineseFont(PDDocument document) throws IOException {
+ List candidates = List.of(
+ "C:/Windows/Fonts/NotoSansSC-VF.ttf",
+ "C:/Windows/Fonts/simhei.ttf",
+ "C:/Windows/Fonts/simsunb.ttf",
+ "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttf",
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttf",
+ "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
+ );
+ for (String path : candidates) {
+ File file = new File(path);
+ if (!file.exists() || !file.isFile()) {
+ continue;
+ }
+ String lowerPath = path.toLowerCase();
+ if (lowerPath.endsWith(".ttf")) {
+ return PDType0Font.load(document, file);
+ }
+ if (lowerPath.endsWith(".ttc")) {
+ PDType0Font font = loadFirstCollectionFont(document, file);
+ if (font != null) {
+ return font;
+ }
+ }
+ }
+ throw new ServiceException("未找到可用中文字体,无法导出PDF报告");
+ }
+
+ private PDType0Font loadFirstCollectionFont(PDDocument document, File file) throws IOException {
+ AtomicReference font = new AtomicReference<>();
+ try (TrueTypeCollection collection = new TrueTypeCollection(file)) {
+ collection.processAllFonts(typeFont -> {
+ if (font.get() == null) {
+ font.set(PDType0Font.load(document, typeFont, true));
+ }
+ });
+ }
+ return font.get();
+ }
+
+ private List indexedRows(List rows) {
+ List result = new ArrayList<>();
+ for (int i = 0; i < rows.size(); i++) {
+ result.add(new IndexedUploadSubject(String.valueOf(i + 1), rows.get(i)));
+ }
+ return result;
+ }
+
+ private String formatPeopleSummary(CcdiProjectOverviewReportModelSummaryVO item) {
+ String names = safeText(item.getPeopleNames());
+ if ("-".equals(names)) {
+ return names;
+ }
+ List people = Arrays.stream(names.split("、"))
+ .filter(value -> value != null && !value.isBlank())
+ .distinct()
+ .toList();
+ if (people.size() <= 4) {
+ return String.join("、", people);
+ }
+ return String.join("、", people.subList(0, 4)) + "等" + defaultZero(item.getPeopleCount()) + "人";
+ }
+
+ private String formatHitTags(List tags) {
+ if (tags == null || tags.isEmpty()) {
+ return "-";
+ }
+ String text = tags.stream()
+ .map(CcdiProjectRiskHitTagVO::getRuleName)
+ .filter(Objects::nonNull)
+ .filter(value -> !value.isBlank())
+ .distinct()
+ .collect(Collectors.joining("、"));
+ return text.isBlank() ? "-" : text;
+ }
+
+ private String joinText(List values) {
+ if (values == null || values.isEmpty()) {
+ return "-";
+ }
+ String text = values.stream()
+ .filter(Objects::nonNull)
+ .filter(value -> !value.isBlank())
+ .distinct()
+ .collect(Collectors.joining("、"));
+ return text.isBlank() ? "-" : text;
+ }
+
+ private String formatRelatedStaff(String name, String code) {
+ if (name == null || name.isBlank()) {
+ return "-";
+ }
+ if (code == null || code.isBlank()) {
+ return name;
+ }
+ return name + "(" + code + ")";
+ }
+
+ private String formatSummaryAndCashType(String summary, String cashType) {
+ return safeText(summary) + "/" + safeText(cashType);
+ }
+
+ private String formatAccount(String accountNo, String accountName) {
+ String masked = maskAccount(accountNo);
+ String name = safeText(accountName);
+ if ("-".equals(name)) {
+ return masked;
+ }
+ return masked + "\n" + name;
+ }
+
+ private String maskAccountList(String value) {
+ if (value == null || value.isBlank()) {
+ return "-";
+ }
+ return Arrays.stream(value.split("、|,|,"))
+ .map(String::trim)
+ .filter(item -> !item.isBlank())
+ .map(this::maskAccount)
+ .distinct()
+ .collect(Collectors.joining("、"));
+ }
+
+ private String maskAccount(String value) {
+ if (value == null || value.isBlank()) {
+ return "-";
+ }
+ String text = value.trim().replaceAll("\\s+", "");
+ if (text.length() <= 8) {
+ return text.length() <= 4 ? text : text.substring(0, 2) + "****" + text.substring(text.length() - 2);
+ }
+ return text.substring(0, 4) + "****" + text.substring(text.length() - 4);
+ }
+
+ private String maskIdCard(String value) {
+ if (value == null || value.isBlank()) {
+ return "-";
+ }
+ String text = value.trim();
+ if (text.length() < 10) {
+ return text;
+ }
+ return text.substring(0, 3) + "***********" + text.substring(text.length() - 4);
+ }
+
+ private String formatCount(Integer value, String unit) {
+ return defaultZero(value) + unit;
+ }
+
+ private String formatMoney(BigDecimal value) {
+ if (value == null) {
+ return "-";
+ }
+ return MONEY_FORMAT.format(value);
+ }
+
+ private Integer defaultZero(Integer value) {
+ return value == null ? 0 : value;
+ }
+
+ private String safeText(String value) {
+ return value == null || value.isBlank() ? "-" : value;
+ }
+
+ private String safeFileName(String value) {
+ String text = safeText(value);
+ return text.replaceAll("[\\\\/:*?\"<>|]", "_");
+ }
+
+ private record IndexedUploadSubject(String index, CcdiProjectOverviewReportUploadSubjectVO row) {
+ }
+
+ private static class PdfPageWriter {
+
+ private static final float MARGIN = 36F;
+ private static final PDRectangle LANDSCAPE_A4 = new PDRectangle(
+ PDRectangle.A4.getHeight(),
+ PDRectangle.A4.getWidth()
+ );
+ private static final float CONTENT_WIDTH = LANDSCAPE_A4.getWidth() - MARGIN * 2;
+ private static final float PAGE_TOP = LANDSCAPE_A4.getHeight() - MARGIN;
+ private static final float PAGE_BOTTOM = MARGIN;
+ private static final float BODY_FONT_SIZE = 9F;
+ private static final float HEADER_FONT_SIZE = 9F;
+ private static final float TITLE_FONT_SIZE = 22F;
+ private static final float SECTION_FONT_SIZE = 15F;
+ private static final float SUBSECTION_FONT_SIZE = 12F;
+ private static final float LINE_HEIGHT = 12F;
+ private static final float CELL_PADDING = 5F;
+
+ private final PDDocument document;
+ private final PDType0Font font;
+ private PDPageContentStream content;
+ private float y;
+
+ PdfPageWriter(PDDocument document, PDType0Font font) {
+ this.document = document;
+ this.font = font;
+ }
+
+ void newPage() throws IOException {
+ close();
+ PDPage page = new PDPage(LANDSCAPE_A4);
+ document.addPage(page);
+ content = new PDPageContentStream(document, page);
+ y = PAGE_TOP;
+ }
+
+ void close() throws IOException {
+ if (content != null) {
+ content.close();
+ content = null;
+ }
+ }
+
+ void title(String text) throws IOException {
+ writeLine(text, TITLE_FONT_SIZE, new Color(18, 56, 93), 0F, 28F);
+ }
+
+ void text(String text, float fontSize, Color color) throws IOException {
+ writeLine(text, fontSize, color, 0F, 18F);
+ }
+
+ void section(String text) throws IOException {
+ ensureSpace(32F);
+ writeLine(text, SECTION_FONT_SIZE, new Color(18, 56, 93), 0F, 26F);
+ }
+
+ void subsection(String text) throws IOException {
+ ensureSpace(26F);
+ writeLine(text, SUBSECTION_FONT_SIZE, new Color(51, 65, 85), 0F, 22F);
+ }
+
+ void separator() throws IOException {
+ ensureSpace(16F);
+ content.setStrokingColor(new Color(31, 78, 121));
+ content.setLineWidth(1.4F);
+ content.moveTo(MARGIN, y);
+ content.lineTo(MARGIN + CONTENT_WIDTH, y);
+ content.stroke();
+ y -= 22F;
+ }
+
+ void metrics(List stats) throws IOException {
+ ensureSpace(58F);
+ float cellWidth = CONTENT_WIDTH / Math.max(stats.size(), 1);
+ float x = MARGIN;
+ float rowHeight = 50F;
+ for (CcdiProjectOverviewStatVO stat : stats) {
+ drawRect(x, y - rowHeight, cellWidth, rowHeight, null);
+ drawCenteredText(String.valueOf(stat.getValue()), x, y - 18F, cellWidth, 18F, new Color(31, 78, 121));
+ drawCenteredText(stat.getLabel(), x, y - 36F, cellWidth, 10F, Color.GRAY);
+ x += cellWidth;
+ }
+ y -= rowHeight + 18F;
+ }
+
+ void table(
+ List headers,
+ List> rows,
+ float[] widthRatios,
+ String emptyText
+ ) throws IOException {
+ List> safeRows = rows.isEmpty()
+ ? List.of(List.of(emptyText))
+ : rows;
+ List safeHeaders = rows.isEmpty()
+ ? List.of(headers.get(0))
+ : headers;
+ float[] widths = rows.isEmpty()
+ ? new float[] { CONTENT_WIDTH }
+ : calculateWidths(widthRatios);
+
+ drawHeader(safeHeaders, widths);
+ for (List row : safeRows) {
+ drawRow(row, widths, false);
+ }
+ y -= 8F;
+ }
+
+ private float[] calculateWidths(float[] ratios) {
+ float[] widths = new float[ratios.length];
+ for (int i = 0; i < ratios.length; i++) {
+ widths[i] = CONTENT_WIDTH * ratios[i];
+ }
+ return widths;
+ }
+
+ private void drawHeader(List headers, float[] widths) throws IOException {
+ drawRow(headers, widths, true);
+ }
+
+ private void drawRow(List cells, float[] widths, boolean header) throws IOException {
+ List> wrappedCells = new ArrayList<>();
+ float rowHeight = 0F;
+ for (int i = 0; i < widths.length; i++) {
+ String text = i < cells.size() ? cells.get(i) : "";
+ List lines = wrapText(text, widths[i] - CELL_PADDING * 2, header ? HEADER_FONT_SIZE : BODY_FONT_SIZE);
+ wrappedCells.add(lines);
+ rowHeight = Math.max(rowHeight, lines.size() * LINE_HEIGHT + CELL_PADDING * 2);
+ }
+ rowHeight = Math.max(rowHeight, 24F);
+ ensureSpace(rowHeight + 4F);
+
+ float x = MARGIN;
+ for (int i = 0; i < widths.length; i++) {
+ Color background = header ? new Color(234, 241, 248) : null;
+ drawRect(x, y - rowHeight, widths[i], rowHeight, background);
+ drawCellText(wrappedCells.get(i), x + CELL_PADDING, y - CELL_PADDING - (header ? HEADER_FONT_SIZE : BODY_FONT_SIZE), header);
+ x += widths[i];
+ }
+ y -= rowHeight;
+ }
+
+ private void drawRect(float x, float bottomY, float width, float height, Color fill) throws IOException {
+ if (fill != null) {
+ content.setNonStrokingColor(fill);
+ content.addRect(x, bottomY, width, height);
+ content.fill();
+ }
+ content.setStrokingColor(new Color(205, 217, 229));
+ content.setLineWidth(0.5F);
+ content.addRect(x, bottomY, width, height);
+ content.stroke();
+ }
+
+ private void drawCellText(List lines, float x, float startY, boolean header) throws IOException {
+ content.beginText();
+ content.setNonStrokingColor(header ? new Color(24, 59, 90) : new Color(31, 41, 55));
+ content.setFont(font, header ? HEADER_FONT_SIZE : BODY_FONT_SIZE);
+ content.newLineAtOffset(x, startY);
+ for (int i = 0; i < lines.size(); i++) {
+ if (i > 0) {
+ content.newLineAtOffset(0, -LINE_HEIGHT);
+ }
+ content.showText(lines.get(i));
+ }
+ content.endText();
+ }
+
+ private void drawCenteredText(String text, float x, float baselineY, float width, float fontSize, Color color)
+ throws IOException {
+ float textWidth = textWidth(text, fontSize);
+ content.beginText();
+ content.setNonStrokingColor(color);
+ content.setFont(font, fontSize);
+ content.newLineAtOffset(x + (width - textWidth) / 2F, baselineY);
+ content.showText(text);
+ content.endText();
+ }
+
+ private void writeLine(String text, float fontSize, Color color, float indent, float advance) throws IOException {
+ ensureSpace(advance);
+ content.beginText();
+ content.setNonStrokingColor(color);
+ content.setFont(font, fontSize);
+ content.newLineAtOffset(MARGIN + indent, y);
+ content.showText(text);
+ content.endText();
+ y -= advance;
+ }
+
+ private void ensureSpace(float height) throws IOException {
+ if (y - height < PAGE_BOTTOM) {
+ newPage();
+ }
+ }
+
+ private List wrapText(String text, float maxWidth, float fontSize) throws IOException {
+ String safeText = text == null || text.isBlank() ? "-" : text;
+ List result = new ArrayList<>();
+ for (String part : safeText.split("\\n")) {
+ wrapPart(part, maxWidth, fontSize, result);
+ }
+ return result.isEmpty() ? List.of("-") : result;
+ }
+
+ private void wrapPart(String text, float maxWidth, float fontSize, List result) throws IOException {
+ StringBuilder current = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ String next = String.valueOf(text.charAt(i));
+ if (textWidth(current + next, fontSize) > maxWidth && current.length() > 0) {
+ result.add(current.toString());
+ current.setLength(0);
+ }
+ current.append(next);
+ }
+ if (current.length() > 0) {
+ result.add(current.toString());
+ }
+ }
+
+ private float textWidth(String text, float fontSize) throws IOException {
+ return font.getStringWidth(text) / 1000F * fontSize;
+ }
+ }
+}
diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java
index ab6570f8..2747e5e1 100644
--- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java
+++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java
@@ -26,6 +26,8 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
+import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewEmployeeHitRowVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewStatVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
@@ -37,15 +39,18 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
+import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
+import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
import com.ruoyi.common.exception.ServiceException;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.annotation.Resource;
import java.io.IOException;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -81,6 +86,12 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
@Resource
private CcdiProjectRiskDetailWorkbookExporter workbookExporter;
+ @Resource
+ private CcdiProjectOverviewReportPdfExporter reportPdfExporter;
+
+ @Resource
+ private ICcdiModelParamService modelParamService;
+
@Override
public CcdiProjectOverviewDashboardVO getDashboard(Long projectId) {
CcdiProject project = overviewMapper.selectDashboardBaseByProjectId(projectId);
@@ -303,6 +314,38 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
}
}
+ @Override
+ public void exportOverviewReport(HttpServletResponse response, Long projectId) {
+ CcdiProject project = getRequiredProject(projectId);
+
+ CcdiProjectSuspiciousTransactionQueryDTO suspiciousQuery = new CcdiProjectSuspiciousTransactionQueryDTO();
+ suspiciousQuery.setProjectId(projectId);
+ suspiciousQuery.setSuspiciousType("ALL");
+
+ CcdiProjectOverviewReportVO report = new CcdiProjectOverviewReportVO();
+ report.setProject(project);
+ report.setDashboard(getDashboard(projectId));
+ report.setUploadSubjects(defaultList(overviewMapper.selectReportUploadSubjects(projectId)).stream()
+ .peek(item -> item.setDataPeriod(formatDataPeriod(item.getMinTrxDate(), item.getMaxTrxDate())))
+ .toList());
+ report.setParams(buildReportParams(projectId));
+ report.setModelSummaries(defaultList(overviewMapper.selectReportRiskModelSummaries(projectId)));
+ report.setRiskPeople(defaultList(overviewMapper.selectReportRiskPeople(projectId)).stream()
+ .peek(item -> item.setActionLabel(ACTION_LABEL))
+ .toList());
+ report.setSuspiciousTransactions(defaultList(
+ overviewMapper.selectReportSuspiciousTransactionList(suspiciousQuery)
+ ));
+ report.setIllegalPeople(exportEmployeeCreditNegative(projectId));
+ report.setAbnormalAccounts(exportAbnormalAccountPeople(projectId));
+
+ try {
+ reportPdfExporter.export(response, report);
+ } catch (IOException e) {
+ throw new ServiceException("导出结果总览报告失败");
+ }
+ }
+
@Override
public List exportEmployeeCreditNegative(Long projectId) {
ensureProjectExists(projectId);
@@ -511,6 +554,31 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
return row;
}
+ private List buildReportParams(Long projectId) {
+ ModelParamAllVO response = modelParamService.selectAllParams(projectId);
+ return defaultList(response == null ? null : response.getModels()).stream()
+ .flatMap(model -> defaultList(model.getParams()).stream().map(param -> {
+ CcdiProjectOverviewReportParamVO row = new CcdiProjectOverviewReportParamVO();
+ row.setModelName(model.getModelName());
+ row.setParamName(param.getParamName());
+ row.setParamValue(param.getParamValue());
+ row.setParamUnit(param.getParamUnit());
+ row.setParamDesc(param.getParamDesc());
+ return row;
+ }))
+ .toList();
+ }
+
+ private String formatDataPeriod(String minTrxDate, String maxTrxDate) {
+ if (minTrxDate == null || minTrxDate.isBlank() || maxTrxDate == null || maxTrxDate.isBlank()) {
+ return "-";
+ }
+ LocalDate start = LocalDate.parse(minTrxDate);
+ LocalDate end = LocalDate.parse(maxTrxDate);
+ int months = (end.getYear() - start.getYear()) * 12 + end.getMonthValue() - start.getMonthValue() + 1;
+ return Math.max(months, 1) + "个月";
+ }
+
private String formatRelatedStaff(String relatedStaffName, String relatedStaffCode) {
if (relatedStaffName == null || relatedStaffName.isBlank()) {
return null;
diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml
index 68d9fe13..84344fdd 100644
--- a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml
+++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml
@@ -57,6 +57,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
select 0 as digit
union all select 1
@@ -338,6 +372,87 @@
order by warning_count desc, model_code asc
+
+
+
+
+
+
+
+
+
+
+ org.apache.pdfbox
+ pdfbox
+ ${pdfbox.version}
+
+
com.alibaba
diff --git a/ruoyi-ui/src/api/ccdi/projectOverview.js b/ruoyi-ui/src/api/ccdi/projectOverview.js
index 1beddebd..8732ebd1 100644
--- a/ruoyi-ui/src/api/ccdi/projectOverview.js
+++ b/ruoyi-ui/src/api/ccdi/projectOverview.js
@@ -91,3 +91,12 @@ export function getOverviewAbnormalAccountPeople(params) {
}
})
}
+
+export function exportOverviewReport(projectId) {
+ return request({
+ url: '/ccdi/project/overview/report/export',
+ method: 'post',
+ responseType: 'blob',
+ params: { projectId }
+ })
+}
diff --git a/ruoyi-ui/src/views/ccdiAccountInfo/index.vue b/ruoyi-ui/src/views/ccdiAccountInfo/index.vue
index 6872cd24..c66843d0 100644
--- a/ruoyi-ui/src/views/ccdiAccountInfo/index.vue
+++ b/ruoyi-ui/src/views/ccdiAccountInfo/index.vue
@@ -113,7 +113,8 @@
-
+
@@ -872,19 +874,89 @@ export default {
.account-page {
min-height: calc(100vh - 84px);
- background: #f5f7fa;
+ padding: 24px;
+ background: #f5f6f8;
}
.board {
- padding: 20px;
- border-radius: 8px;
+ padding: 0;
+ border-radius: 0;
+ background: transparent;
+ border: 0;
+}
+
+.query-form {
+ margin-bottom: 16px;
+ padding: 18px 20px 2px;
+ border: 1px solid #dde3ec;
+ border-radius: 3px;
background: #fff;
- border: 1px solid #ebeef5;
+}
+
+.query-form ::v-deep .el-form-item {
+ margin-bottom: 16px;
+}
+
+.query-form ::v-deep .el-form-item__label {
+ color: #637187;
+ font-weight: 600;
+}
+
+.query-form ::v-deep .el-input__inner,
+.query-form ::v-deep .el-select .el-input__inner {
+ border-color: #dde3ec;
+ border-radius: 3px;
+}
+
+.mb8 {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ margin-bottom: 16px;
+ padding: 14px 20px;
+ border: 1px solid #dde3ec;
+ border-radius: 3px;
+ background: #ffffff;
+}
+
+.mb8 ::v-deep .el-button {
+ border-radius: 4px;
+}
+
+.mb8 ::v-deep .top-right-btn {
+ margin-left: auto;
+}
+
+.formal-table-shell {
+ padding: 4px 0 16px;
+ border: 1px solid #dde3ec;
+ border-radius: 3px;
+ background: #ffffff;
+ overflow: hidden;
}
.account-table ::v-deep .el-table__header th {
- background: #f8f8f9;
- color: #515a6e;
+ background: #f6f8fb;
+ color: #607086;
+ padding: 9px 0;
+}
+
+.account-table ::v-deep .el-table td,
+.account-table ::v-deep .el-table th.is-leaf {
+ border-bottom-color: #edf1f5;
+}
+
+.account-table ::v-deep .el-table td {
+ padding: 8px 0;
+}
+
+.account-table ::v-deep .el-table th > .cell,
+.account-table ::v-deep .el-table td > .cell {
+ line-height: 1.4;
+}
+
+.formal-table-shell ::v-deep .pagination-container {
+ padding: 16px 20px 0;
}
.form-section {
@@ -913,4 +985,18 @@ export default {
font-weight: 700;
}
+::v-deep .el-dialog {
+ border-radius: 6px;
+ overflow: hidden;
+}
+
+::v-deep .el-dialog__header {
+ border-bottom: 1px solid #dde3ec;
+ background: #ffffff;
+}
+
+::v-deep .el-dialog__body {
+ background: #f8fafc;
+}
+
diff --git a/ruoyi-ui/src/views/ccdiBaseStaff/index.vue b/ruoyi-ui/src/views/ccdiBaseStaff/index.vue
index 50210638..6f6da26a 100644
--- a/ruoyi-ui/src/views/ccdiBaseStaff/index.vue
+++ b/ruoyi-ui/src/views/ccdiBaseStaff/index.vue
@@ -112,64 +112,66 @@
-
-
-
-
-
-
-
-
-
-
- {{ formatPartyMember(scope.row.partyMember) }}
-
-
-
-
- 在职
- 离职
-
-
-
-
- {{ parseTime(scope.row.createTime) }}
-
-
-
-
- 详情
- 编辑
- 删除
-
-
-
+
@@ -1472,6 +1474,12 @@ export default {
diff --git a/ruoyi-ui/src/views/ccdiCreditInfo/index.vue b/ruoyi-ui/src/views/ccdiCreditInfo/index.vue
index 038d364b..4d19a0d2 100644
--- a/ruoyi-ui/src/views/ccdiCreditInfo/index.vue
+++ b/ruoyi-ui/src/views/ccdiCreditInfo/index.vue
@@ -40,7 +40,8 @@
-
+
diff --git a/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue b/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue
index b4e4a378..cd88da08 100644
--- a/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue
+++ b/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue
@@ -90,7 +90,8 @@
-
+
@@ -841,6 +843,12 @@ export default {
diff --git a/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue b/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue
index 474c3614..fc893124 100644
--- a/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue
+++ b/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue
@@ -91,7 +91,8 @@
-
+
@@ -1097,6 +1099,12 @@ export default {
diff --git a/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue b/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue
index 360760b5..3ab635fe 100644
--- a/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue
+++ b/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue
@@ -1,5 +1,5 @@
-
+
+
diff --git a/ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue b/ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue
index 2b15851f..aa1cce6e 100644
--- a/ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue
+++ b/ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue
@@ -1,51 +1,52 @@