Przeglądaj źródła

Merge remote-tracking branch 'origin/master' into master

yangyj 1 rok temu
rodzic
commit
39d4dd44db
14 zmienionych plików z 808 dodań i 11 usunięć
  1. 20 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialSummaryMonthlyEditDTO.java
  2. 32 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialSummaryMonthlyPageDTO.java
  3. 15 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/TrialSummaryMonthlyRemarks.java
  4. 20 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryClassificationConfigurationMatchDTO.java
  5. 20 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryClassificationConfigurationRelevancyDTO.java
  6. 34 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/TrialTreeVO.java
  7. 6 5
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveOfflineVersionInfoController.java
  8. 2 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml
  9. 22 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  10. 1 1
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TaskController.java
  11. 498 0
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TrialSummaryController.java
  12. 23 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TrialSummaryClassificationConfigurationController.java
  13. 9 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/ITrialSummaryClassificationConfigurationService.java
  14. 106 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/TrialSummaryClassificationConfigurationServiceImpl.java

+ 20 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialSummaryMonthlyEditDTO.java

@@ -0,0 +1,20 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialSummaryMonthlyEditDTO implements Serializable {
+
+    @ApiModelProperty(value = "合同段id")
+    private Long contractId;
+
+    @ApiModelProperty(value = "关联id")
+    private Long recordId;
+
+    @ApiModelProperty(value = "备注文本信息")
+    private String remarks;
+
+}

+ 32 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialSummaryMonthlyPageDTO.java

@@ -0,0 +1,32 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialSummaryMonthlyPageDTO implements Serializable {
+
+    @ApiModelProperty(value = "合同段id")
+    private Long contractId;
+
+    @ApiModelProperty(value = "检测类别")
+    private Integer type;
+
+    @ApiModelProperty(value = "开始时间,格式yyyy-MM-dd")
+    private String startTime;
+
+    @ApiModelProperty(value = "结束时间,格式yyyy-MM-dd")
+    private String endTime;
+
+    @ApiModelProperty(value = "选择的试验划分树节点的pKeyId英文逗号字符串拼接")
+    private String ids;
+
+    @ApiModelProperty(value = "每页条数")
+    private Integer size;
+
+    @ApiModelProperty(value = "当前页码")
+    private Integer current;
+
+}

+ 15 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/TrialSummaryMonthlyRemarks.java

@@ -0,0 +1,15 @@
+package org.springblade.business.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialSummaryMonthlyRemarks implements Serializable {
+
+    private Long id;
+    private Long contractId;
+    private Long recordId;
+    private String remarks;
+
+}

+ 20 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryClassificationConfigurationMatchDTO.java

@@ -0,0 +1,20 @@
+package org.springblade.manager.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialSummaryClassificationConfigurationMatchDTO implements Serializable {
+
+    @ApiModelProperty(value = "项目id")
+    private String projectId;
+
+    @ApiModelProperty(value = "分类id(page接口的id)")
+    private String classId;
+
+    @ApiModelProperty(value = "选择节点的pKeyId英文逗号拼接成ids")
+    private String ids;
+
+}

+ 20 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryClassificationConfigurationRelevancyDTO.java

@@ -0,0 +1,20 @@
+package org.springblade.manager.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialSummaryClassificationConfigurationRelevancyDTO implements Serializable {
+
+    @ApiModelProperty(value = "项目id")
+    private String projectId;
+
+    @ApiModelProperty(value = "分类id(page接口的id)")
+    private String classId;
+
+    @ApiModelProperty(value = "选择节点的pKeyId英文逗号拼接成ids")
+    private String ids;
+
+}

+ 34 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/TrialTreeVO.java

@@ -0,0 +1,34 @@
+package org.springblade.manager.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class TrialTreeVO implements Serializable {
+
+    @ApiModelProperty(value = "主键id")
+    private Long pKeyId;
+
+    @ApiModelProperty(value = "id")
+    private Long id;
+
+    @ApiModelProperty(value = "父级id")
+    private Long parentId;
+
+    @ApiModelProperty(value = "节点名称")
+    private String nodeName;
+
+    @ApiModelProperty(value = "子节点数组")
+    private List<TrialTreeVO> childNode;
+
+    @ApiModelProperty(value = "是否存在子节点 true=存在子级 false=不存在子级")
+    private boolean hasChild;
+
+    @ApiModelProperty(value = "回显状态 0=未勾选 1=勾选")
+    private Integer status;
+
+}

+ 6 - 5
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveOfflineVersionInfoController.java

@@ -44,11 +44,12 @@ public class ArchiveOfflineVersionInfoController {
     @ApiOperation(value = "打包数据")
     @GetMapping("/packData")
     public R<String> packData(Long projectId) throws Exception {
-        //先获取状态,同时只能有一个项目打包
-        offlineVersionInfoService.getPackStatus();
-        //异步调用自动打包上传,完成后修改数据库信息
-        offlineVersionInfoService.packData(projectId);
-        return R.data("最新数据后台自动打包中,打包完成后会更新打包日期");
+//        //先获取状态,同时只能有一个项目打包
+//        offlineVersionInfoService.getPackStatus();
+//        //异步调用自动打包上传,完成后修改数据库信息
+//        offlineVersionInfoService.packData(projectId);
+//        return R.data("最新数据后台自动打包中,打包完成后会更新打包日期");
+        return R.data("暂时停止使用离线档案,请联系管理员");
     }
 
     /**

+ 2 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml

@@ -1053,7 +1053,7 @@
                     and aei.expert_id = #{userId} and is_pass = 0) > 0 then '整改' else null end) as updateStatusName
         from m_archive_tree_contract atc right join u_archives_auto uaa on atc.id = uaa.node_id
         WHERE uaa.is_apply = 1 and uaa.project_id = #{projectId} and atc.project_id = #{projectId} and atc.is_deleted = 0 and uaa.is_deleted =0
-          and FIND_IN_SET(#{id}, atc.ancestors)
+          and (atc.id =#{id} or FIND_IN_SET(#{id}, atc.ancestors))
         <if test="searchValue != null and searchValue != ''">
             <if test="searchType == 1">
                 and uaa.name like concat('%',#{searchValue},'%')
@@ -1073,7 +1073,7 @@
             and aei.expert_id = #{userId} and is_pass = 0) > 0 then '整改' else null end) as updateStatusName
         from m_archive_tree_contract atc right join u_archives_auto uaa on atc.id = uaa.node_id
         WHERE uaa.is_apply = 1 and uaa.project_id = #{projectId} and atc.project_id = #{projectId} and atc.is_deleted = 0 and uaa.is_deleted =0
-        and FIND_IN_SET(#{id}, atc.ancestors)
+        and (atc.id =#{id} or FIND_IN_SET(#{id}, atc.ancestors))
         <if test="userId != null">
             and FIND_IN_SET(#{userId}, uaa.expert_id)
         </if>

+ 22 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -2910,13 +2910,33 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		ProjectInfo project = projectClient.getById(dto.getProjectId() + "");
 		List<Long> longs = Func.toLongList(dto.getArchiveIds());
 		List<ArchivesAutoVO2> vo2s = baseMapper.getAllArchive(longs);
+		String pdfUrl = null;
 		try {
-			String pdfUrl = createAppPdf(project.getProjectName(),vo2s);
-			task.setAttachmentPdfUrl(pdfUrl);
+			pdfUrl = createAppPdf(project.getProjectName(),vo2s);
+			if (StringUtils.isBlank(pdfUrl)){
+				throw new ServiceException("生成上报PDF失败");
+			}
 		}catch (Exception e){
 			throw new ServiceException(e.getMessage());
 		}
 		//如果存在附件则拼接
+		if (StringUtils.isNotBlank(dto.getAttachmentPdfUrl())){
+			String localFile = "/www/wwwroot/Users/hongchuangyanfa/Desktop/archiveCheck/"+dto.getProjectId()+".pdf";
+			List<String> urlList = new ArrayList<>();
+			urlList.add(pdfUrl);
+			urlList.add(dto.getAttachmentPdfUrl());
+			FileUtils.mergePdfPublicMethods(urlList, localFile);
+			System.out.println("生成的pdf:"+pdfUrl);
+			System.out.println("附件的pdf:"+dto.getAttachmentPdfUrl());
+			BladeFile bladeFile = this.newIOSSClient.uploadFile(  "123.pdf", localFile);
+			if (bladeFile == null || StringUtils.isBlank(bladeFile.getLink())){
+				throw new ServiceException("合并PDF失败");
+			}
+			System.out.println("合并的pdf:"+bladeFile.getLink());
+			task.setAttachmentPdfUrl(bladeFile.getLink());
+		}else {
+			task.setAttachmentPdfUrl(pdfUrl);
+		}
 		//保存任务
 		taskClient.saveTask(task);
 		//根据任务人设置task_parallel

+ 1 - 1
blade-service/blade-business/src/main/java/org/springblade/business/controller/TaskController.java

@@ -668,7 +668,7 @@ public class TaskController extends BladeController {
                                     dto.setAccount("expert" + phone);
                                     //如果当前账户已经存在,则代表账号已经注册,直接跳过当前
                                     R<User> r = userClient.userByAccount(AuthUtil.getTenantId(), dto.getAccount());
-                                    if (r.getData() != null) {
+                                    if (r.getData() != null && r.getData().getId() != null) {
                                         if (expertIds.length() == 0){
                                             expertIds.append(r.getData().getId());
                                         }else {

+ 498 - 0
blade-service/blade-business/src/main/java/org/springblade/business/controller/TrialSummaryController.java

@@ -0,0 +1,498 @@
+package org.springblade.business.controller;
+
+import com.aspose.cells.PageSetup;
+import com.aspose.cells.PaperSizeType;
+import com.aspose.cells.PdfSaveOptions;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang.StringUtils;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.*;
+import org.springblade.business.dto.TrialSummaryMonthlyEditDTO;
+import org.springblade.business.dto.TrialSummaryMonthlyPageDTO;
+import org.springblade.business.entity.TrialSelfInspectionRecord;
+import org.springblade.business.entity.TrialSummaryMonthlyRemarks;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.oss.model.BladeFile;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.resource.feign.NewIOSSClient;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLDecoder;
+import java.util.*;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/trial/summary")
+@Api(value = "试验、月报汇总", tags = "试验、月报汇总接口")
+public class TrialSummaryController {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final NewIOSSClient newIOSSClient;
+
+    @PostMapping("/monthly/page")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "月报汇总分页查询", notes = "传入TrialSummaryMonthlyPageDTO")
+    public R<IPage<Map<String, Object>>> monthlyPage(@RequestBody TrialSummaryMonthlyPageDTO dto) {
+        if (ObjectUtil.isEmpty(dto.getContractId()) || ObjectUtil.isEmpty(dto.getType()) || ObjectUtil.isEmpty(dto.getIds()) || StringUtils.isBlank(dto.getStartTime()) || StringUtils.isBlank(dto.getEndTime())) {
+            throw new ServiceException("入参异常");
+        }
+        List<Object> params = new ArrayList<>();
+        StringBuilder sqlString = new StringBuilder("SELECT * FROM u_trial_self_inspection_record WHERE 1=1 AND is_deleted = 0");
+        if (ObjectUtil.isNotEmpty(dto.getContractId())) {
+            sqlString.append(" AND contract_id = ?");
+            params.add(dto.getContractId());
+        }
+        if (ObjectUtil.isNotEmpty(dto.getType())) {
+            sqlString.append(" AND detection_category = ?");
+            params.add(dto.getType());
+        } else {
+            sqlString.append(" AND detection_category = 1");
+        }
+        if (StringUtils.isNotBlank(dto.getStartTime()) && StringUtils.isNotBlank(dto.getEndTime())) {
+            sqlString.append(" AND report_date BETWEEN ? AND ?");
+            params.add(dto.getStartTime());
+            params.add(dto.getEndTime());
+        }
+        if (StringUtils.isNotEmpty(dto.getIds())) {
+            sqlString.append(" AND node_id in(?)");
+            params.add(dto.getIds());
+        }
+
+        String sqlPage = sqlString.append(" ORDER BY create_time;").toString();
+        List<TrialSelfInspectionRecord> resultList = jdbcTemplate.query(
+                sqlPage,
+                new BeanPropertyRowMapper<>(TrialSelfInspectionRecord.class),
+                params.toArray()
+        );
+
+        Map<String, List<TrialSelfInspectionRecord>> groupTOTrialProjectName = resultList.stream()
+                .collect(Collectors.groupingBy(TrialSelfInspectionRecord::getTrialProjectName, LinkedHashMap::new, Collectors.toList()));
+
+        int current = dto.getCurrent();
+        int size = dto.getSize();
+        int start = (current - 1) * size;
+        Map<String, List<TrialSelfInspectionRecord>> paginatedMap = groupTOTrialProjectName.entrySet()
+                .stream()
+                .skip(start)
+                .limit(size)
+                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
+
+        if (paginatedMap.size() <= 0) {
+            return R.data(null);
+        }
+
+        Set<String> names = paginatedMap.keySet();
+        String commaSeparatedQuotedNames = names.stream()
+                .map(name -> "'" + name + "'")
+                .collect(Collectors.joining(","));
+        String sql = "SELECT * FROM u_trial_self_inspection_record WHERE is_deleted = 0 AND contract_id = " + dto.getContractId() + " AND trial_project_name IN (" + commaSeparatedQuotedNames + ")";
+        List<TrialSelfInspectionRecord> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TrialSelfInspectionRecord.class));
+        Map<String, List<TrialSelfInspectionRecord>> groupAllTOTrialProjectName = query.stream().collect(Collectors.groupingBy(TrialSelfInspectionRecord::getTrialProjectName));
+
+        String sqlRemarks = "SELECT record_id,remarks FROM m_trial_summary_monthly_remarks WHERE contract_id = ?";
+        List<TrialSummaryMonthlyRemarks> trialSummaryMonthlyRemarks = jdbcTemplate.query(sqlRemarks, new Object[]{dto.getContractId()}, new BeanPropertyRowMapper<>(TrialSummaryMonthlyRemarks.class));
+        Map<Long, TrialSummaryMonthlyRemarks> trialSummaryMonthlyRemarksMaps = trialSummaryMonthlyRemarks.stream().collect(Collectors.toMap(TrialSummaryMonthlyRemarks::getRecordId, Function.identity()));
+
+        IPage<Map<String, Object>> page = new Page<>();
+        List<Map<String, Object>> result = new LinkedList<>();
+
+        for (Map.Entry<String, List<TrialSelfInspectionRecord>> stringListEntry : paginatedMap.entrySet()) {
+            String key = stringListEntry.getKey();
+            if (ObjectUtil.isEmpty(key)) {
+                continue;
+            }
+
+            Map<String, Object> map = new LinkedHashMap<>();
+            map.put("trialProjectName", key);
+
+            List<TrialSelfInspectionRecord> currentMonthValue = stringListEntry.getValue();
+            Map<String, Object> currentMonthValueMap = new HashMap<>();
+            Optional<TrialSelfInspectionRecord> minIdRecord = currentMonthValue.stream()
+                    .min(Comparator.comparingLong(TrialSelfInspectionRecord::getId));
+            TrialSelfInspectionRecord minIdRecordOrNull = minIdRecord.orElse(null);
+            if (minIdRecordOrNull != null) {
+                long recordId = minIdRecordOrNull.getId() + 1; //防止与累加id重复,+1
+                currentMonthValueMap.put("recordId", recordId);
+                TrialSummaryMonthlyRemarks orDefault = trialSummaryMonthlyRemarksMaps.getOrDefault(recordId, null);
+                currentMonthValueMap.put("remarks", ObjectUtil.isNotEmpty(orDefault) ? ObjectUtil.isNotEmpty(orDefault.getRemarks()) ? orDefault.getRemarks() : "" : "");
+            }
+            currentMonthValueMap.put("qualifiedTotal", currentMonthValue.stream().filter(f -> f.getDetectionResult() == 1).count());
+            currentMonthValueMap.put("unQualifiedTotal", currentMonthValue.stream().filter(f -> f.getDetectionResult() == 0).count());
+            map.put("currentMonth", currentMonthValueMap);
+
+            List<TrialSelfInspectionRecord> totalMonthValue = groupAllTOTrialProjectName.getOrDefault(key, null);
+            Map<String, Object> totalMonthValueMap = new HashMap<>();
+            Optional<TrialSelfInspectionRecord> minIdRecordAll = totalMonthValue.stream()
+                    .min(Comparator.comparingLong(TrialSelfInspectionRecord::getId));
+            TrialSelfInspectionRecord minIdRecordOrNullAll = minIdRecordAll.orElse(null);
+            if (minIdRecordOrNullAll != null) {
+                long recordId = minIdRecordOrNullAll.getId() + 2; //防止与本月id重复,+2
+                totalMonthValueMap.put("recordId", recordId);
+                TrialSummaryMonthlyRemarks orDefault = trialSummaryMonthlyRemarksMaps.getOrDefault(recordId, null);
+                totalMonthValueMap.put("remarks", ObjectUtil.isNotEmpty(orDefault) ? ObjectUtil.isNotEmpty(orDefault.getRemarks()) ? orDefault.getRemarks() : "" : "");
+            }
+            totalMonthValueMap.put("qualifiedTotal", totalMonthValue.stream().filter(f -> f.getDetectionResult() == 1).count());
+            totalMonthValueMap.put("unQualifiedTotal", totalMonthValue.stream().filter(f -> f.getDetectionResult() == 0).count());
+            map.put("totalMonth", totalMonthValueMap);
+
+            result.add(map);
+        }
+
+        page.setRecords(result);
+        page.setSize(size);
+        page.setCurrent(current);
+        page.setTotal(groupTOTrialProjectName.size());
+        page.setPages((page.getTotal() + size - 1) / size);
+        return R.data(page);
+    }
+
+    @PostMapping("/monthly/edit")
+    @ApiOperationSupport(order = 2)
+    @ApiOperation(value = "月报汇总编辑备注", notes = "传入TrialSummaryMonthlyEditDTO")
+    public R<Object> monthlyEdit(@RequestBody TrialSummaryMonthlyEditDTO dto) {
+        if (ObjectUtil.isEmpty(dto.getRecordId()) || ObjectUtil.isEmpty(dto.getContractId())) {
+            throw new ServiceException("入参异常");
+        }
+        if (ObjectUtil.isNotEmpty(dto.getRemarks()) && dto.getRemarks().length() > 2000) {
+            throw new ServiceException("备注信息最长2000个字符,请重新输入");
+        }
+        try {
+            String sql = "SELECT COUNT(*) FROM m_trial_summary_monthly_remarks WHERE record_id = ? AND contract_id = ?";
+            Integer count = jdbcTemplate.queryForObject(sql, Integer.class, dto.getRecordId(), dto.getContractId());
+            int rowCount = (count != null) ? count : 0;
+            if (rowCount == 0) {
+                String insertSql = "INSERT INTO m_trial_summary_monthly_remarks(id, contract_id, record_id, remarks) VALUES (?, ?, ?, ?)";
+                Object[] insertParams = {
+                        SnowFlakeUtil.getId(),
+                        dto.getContractId(),
+                        dto.getRecordId(),
+                        ObjectUtil.isNotEmpty(dto.getRemarks()) ? dto.getRemarks() : null
+                };
+                jdbcTemplate.update(insertSql, insertParams);
+            } else {
+                String updateSql = "UPDATE m_trial_summary_monthly_remarks SET remarks = ? WHERE record_id = ? AND contract_id = ?";
+                Object[] updateParams = {
+                        ObjectUtil.isNotEmpty(dto.getRemarks()) ? dto.getRemarks() : null,
+                        dto.getRecordId(),
+                        dto.getContractId()
+                };
+                jdbcTemplate.update(updateSql, updateParams);
+            }
+
+            return R.data(200, null, "操作成功");
+        } catch (Exception e) {
+            return R.fail("操作失败" + e.getMessage());
+        }
+    }
+
+    @PostMapping("/monthly/download")
+    @ApiOperationSupport(order = 3)
+    @ApiOperation(value = "月报汇总下载", notes = "传入TrialSummaryMonthlyPageDTO")
+    public void monthlyDownload(@RequestBody TrialSummaryMonthlyPageDTO dto, HttpServletResponse response) {
+        List<Map<String, Object>> records = monthlyPage(dto).getData().getRecords();
+        try (Workbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet("Sheet1");
+            Row titleRow_1 = sheet.createRow(0);
+            sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 1));
+            titleRow_1.setHeight((short) 500);
+            setCellValueWithNullCheck(titleRow_1.createCell(0), "", "");
+
+            CreationHelper helper = workbook.getCreationHelper();
+            XSSFDrawing xssfDrawing = (XSSFDrawing) sheet.createDrawingPatriarch();
+            ClientAnchor anchor = helper.createClientAnchor();
+            anchor.setCol1(0);
+            anchor.setCol2(2);
+            anchor.setRow1(0);
+            anchor.setRow2(2);
+            XSSFSimpleShape simpleShape = xssfDrawing.createSimpleShape((XSSFClientAnchor) anchor);
+            simpleShape.setShapeType(ShapeTypes.LINE);
+            simpleShape.setLineWidth(0.5);
+            simpleShape.setLineStyle(0);
+            simpleShape.setLineStyleColor(0, 0, 0);
+
+            int leftBottomX = anchor.getDx1();
+            int leftBottomY = anchor.getDy1();
+            int rightTopX = anchor.getDx2();
+            int rightTopY = anchor.getDy2();
+            XSSFDrawing xssfDrawingText1 = (XSSFDrawing) sheet.createDrawingPatriarch();
+            XSSFClientAnchor textBoxAnchor1 = new XSSFClientAnchor(leftBottomX, leftBottomY, leftBottomX, leftBottomY, 1, 0, 2, 1);
+            XSSFTextBox textBox1 = xssfDrawingText1.createTextbox(textBoxAnchor1);
+            textBox1.setText("试验组数");
+            XSSFDrawing xssfDrawingText2 = (XSSFDrawing) sheet.createDrawingPatriarch();
+            XSSFClientAnchor textBoxAnchor2 = new XSSFClientAnchor(rightTopX, rightTopY, rightTopX, rightTopY, 0, 1, 1, 2);
+            XSSFTextBox textBox2 = xssfDrawingText2.createTextbox(textBoxAnchor2);
+            textBox2.setText("试验项目");
+
+            sheet.addMergedRegion(new CellRangeAddress(0, 0, 2, 4));
+            setCellValueWithNullCheck(titleRow_1.createCell(2), "承包人", "");
+            Row titleRow_2 = sheet.createRow(1);
+            titleRow_2.setHeight((short) 500);
+            setCellValueWithNullCheck(titleRow_2.createCell(2), "合格数", "");
+            setCellValueWithNullCheck(titleRow_2.createCell(3), "不合格数", "");
+            setCellValueWithNullCheck(titleRow_2.createCell(4), "备注", "");
+
+            int rowNum = 2;
+            for (Map<String, Object> record : records) {
+                Row currentMonthRow = sheet.createRow(rowNum);
+                sheet.addMergedRegion(new CellRangeAddress(rowNum, rowNum + 1, 0, 0));
+                setCellValueWithNullCheck(currentMonthRow.createCell(0), "", record.get("trialProjectName"));
+
+                Map<String, Object> currentMonth = (Map<String, Object>) record.get("currentMonth");
+                setCellValueWithNullCheck(currentMonthRow.createCell(1), "本月", "");
+                setCellValueWithNullCheck(currentMonthRow.createCell(2), "", currentMonth.get("qualifiedTotal"));
+                setCellValueWithNullCheck(currentMonthRow.createCell(3), "", currentMonth.get("unQualifiedTotal"));
+                setCellValueWithNullCheck(currentMonthRow.createCell(4), "", currentMonth.get("remarks"));
+
+                Map<String, Object> totalMonth = (Map<String, Object>) record.get("totalMonth");
+                Row totalMonthRow = sheet.createRow(++rowNum);
+                setCellValueWithNullCheck(totalMonthRow.createCell(1), "累计", "");
+                setCellValueWithNullCheck(totalMonthRow.createCell(2), "", totalMonth.get("qualifiedTotal"));
+                setCellValueWithNullCheck(totalMonthRow.createCell(3), "", totalMonth.get("unQualifiedTotal"));
+                setCellValueWithNullCheck(totalMonthRow.createCell(4), "", totalMonth.get("remarks"));
+
+                rowNum++;
+            }
+
+            sheet.setColumnWidth(0, 5000);
+            sheet.setColumnWidth(1, 5000);
+            sheet.setColumnWidth(2, 5000);
+            sheet.setColumnWidth(3, 5000);
+            sheet.setColumnWidth(4, 5000);
+
+            try (ServletOutputStream outputStream = response.getOutputStream();
+                 ByteArrayOutputStream byteArrayOutputStreamResult = new ByteArrayOutputStream()) {
+                String fileName = dto.getStartTime() + "~" + dto.getEndTime() + ".xlsx";
+                String decodedFileName = URLDecoder.decode(fileName, "UTF-8");
+                response.setHeader("Content-Disposition", "attachment; filename=\"" + decodedFileName + "\"");
+                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+                workbook.write(byteArrayOutputStreamResult);
+                byte[] excelBytesResult = byteArrayOutputStreamResult.toByteArray();
+                outputStream.write(excelBytesResult);
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                workbook.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void setCellValueWithNullCheck(Cell cell, String defaultValue, Object value) {
+        if (ObjectUtil.isNotEmpty(value)) {
+            cell.setCellValue(String.valueOf(value));
+        } else {
+            cell.setCellValue(defaultValue);
+        }
+    }
+
+    @PostMapping("/monthly/print")
+    @ApiOperationSupport(order = 4)
+    @ApiOperation(value = "月报汇总打印", notes = "传入TrialSummaryMonthlyPageDTO")
+    public R<Object> monthlyPrint(@RequestBody TrialSummaryMonthlyPageDTO dto) {
+        List<Map<String, Object>> records = monthlyPage(dto).getData().getRecords();
+        try (Workbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet("Sheet1");
+            Row titleRow_1 = sheet.createRow(0);
+            sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 1));
+            titleRow_1.setHeight((short) 500);
+            setCellValueWithNullCheck(titleRow_1.createCell(0), "", "");
+
+            CreationHelper helper = workbook.getCreationHelper();
+            XSSFDrawing xssfDrawing = (XSSFDrawing) sheet.createDrawingPatriarch();
+            ClientAnchor anchor = helper.createClientAnchor();
+            anchor.setCol1(0);
+            anchor.setCol2(2);
+            anchor.setRow1(0);
+            anchor.setRow2(2);
+            XSSFSimpleShape simpleShape = xssfDrawing.createSimpleShape((XSSFClientAnchor) anchor);
+            simpleShape.setShapeType(ShapeTypes.LINE);
+            simpleShape.setLineWidth(0.5);
+            simpleShape.setLineStyle(0);
+            simpleShape.setLineStyleColor(0, 0, 0);
+
+            int leftBottomX = anchor.getDx1();
+            int leftBottomY = anchor.getDy1();
+            int rightTopX = anchor.getDx2();
+            int rightTopY = anchor.getDy2();
+            XSSFDrawing xssfDrawingText1 = (XSSFDrawing) sheet.createDrawingPatriarch();
+            XSSFClientAnchor textBoxAnchor1 = new XSSFClientAnchor(leftBottomX, leftBottomY, leftBottomX, leftBottomY, 1, 0, 2, 1);
+            XSSFTextBox textBox1 = xssfDrawingText1.createTextbox(textBoxAnchor1);
+            textBox1.setText("试验组数");
+            XSSFDrawing xssfDrawingText2 = (XSSFDrawing) sheet.createDrawingPatriarch();
+            XSSFClientAnchor textBoxAnchor2 = new XSSFClientAnchor(rightTopX, rightTopY, rightTopX, rightTopY, 0, 1, 1, 2);
+            XSSFTextBox textBox2 = xssfDrawingText2.createTextbox(textBoxAnchor2);
+            textBox2.setText("试验项目");
+
+            sheet.addMergedRegion(new CellRangeAddress(0, 0, 2, 4));
+            setCellValueWithNullCheck(titleRow_1.createCell(2), "承包人", "");
+            Row titleRow_2 = sheet.createRow(1);
+            titleRow_2.setHeight((short) 500);
+            setCellValueWithNullCheck(titleRow_2.createCell(2), "合格数", "");
+            setCellValueWithNullCheck(titleRow_2.createCell(3), "不合格数", "");
+            setCellValueWithNullCheck(titleRow_2.createCell(4), "备注", "");
+
+            int rowNum = 2;
+            for (Map<String, Object> record : records) {
+                Row currentMonthRow = sheet.createRow(rowNum);
+                sheet.addMergedRegion(new CellRangeAddress(rowNum, rowNum + 1, 0, 0));
+                setCellValueWithNullCheck(currentMonthRow.createCell(0), "", record.get("trialProjectName"));
+
+                Map<String, Object> currentMonth = (Map<String, Object>) record.get("currentMonth");
+                setCellValueWithNullCheck(currentMonthRow.createCell(1), "本月", "");
+                setCellValueWithNullCheck(currentMonthRow.createCell(2), "", currentMonth.get("qualifiedTotal"));
+                setCellValueWithNullCheck(currentMonthRow.createCell(3), "", currentMonth.get("unQualifiedTotal"));
+                setCellValueWithNullCheck(currentMonthRow.createCell(4), "", currentMonth.get("remarks"));
+
+                Map<String, Object> totalMonth = (Map<String, Object>) record.get("totalMonth");
+                Row totalMonthRow = sheet.createRow(++rowNum);
+                setCellValueWithNullCheck(totalMonthRow.createCell(1), "累计", "");
+                setCellValueWithNullCheck(totalMonthRow.createCell(2), "", totalMonth.get("qualifiedTotal"));
+                setCellValueWithNullCheck(totalMonthRow.createCell(3), "", totalMonth.get("unQualifiedTotal"));
+                setCellValueWithNullCheck(totalMonthRow.createCell(4), "", totalMonth.get("remarks"));
+
+                rowNum++;
+            }
+
+            sheet.setColumnWidth(0, 5000);
+            sheet.setColumnWidth(1, 5000);
+            sheet.setColumnWidth(2, 5000);
+            sheet.setColumnWidth(3, 5000);
+            sheet.setColumnWidth(4, 5000);
+
+            try (ByteArrayOutputStream byteArrayOutputStreamResult = new ByteArrayOutputStream()) {
+                workbook.write(byteArrayOutputStreamResult);
+                byte[] excelBytesResult = byteArrayOutputStreamResult.toByteArray();
+                MultipartFile multipartFile = convertExcelToPdf(excelBytesResult);
+                if (multipartFile != null) {
+                    BladeFile bladeFile = newIOSSClient.uploadFileByInputStream(multipartFile);
+                    if (bladeFile != null) {
+                        return R.data(bladeFile.getLink());
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                workbook.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return R.fail("操作失败");
+    }
+
+    private MultipartFile convertExcelToPdf(byte[] excelBytes) {
+        ByteArrayInputStream byteArrayInputStream = null;
+        ByteArrayOutputStream outReport = null;
+        try {
+            byteArrayInputStream = new ByteArrayInputStream(excelBytes);
+            Workbook ss = WorkbookFactory.create(byteArrayInputStream);
+            Sheet sheet = ss.getSheetAt(0);
+            for (int r = 0; r <= sheet.getLastRowNum(); r++) {
+                Row row = sheet.getRow(r);
+                if (row != null) {
+                    for (int c = 0; c < row.getLastCellNum(); c++) {
+                        Cell cell = row.getCell(c);
+                        if (cell != null) {
+                            CellStyle cellStyle = ss.createCellStyle();
+                            cellStyle.cloneStyleFrom(cell.getCellStyle());
+                            cellStyle.setBorderBottom(BorderStyle.THIN);
+                            cellStyle.setBorderTop(BorderStyle.THIN);
+                            cellStyle.setBorderRight(BorderStyle.THIN);
+                            cellStyle.setBorderLeft(BorderStyle.THIN);
+                            cell.setCellStyle(cellStyle);
+                            CellRangeAddress mergedRegion = getMergedRegion(sheet, r, c);
+                            if (mergedRegion != null) {
+                                setMergedRegionBorders(sheet, mergedRegion, cellStyle);
+                            }
+                        }
+                    }
+                }
+                sheet.setPrintGridlines(false);
+                sheet.setFitToPage(true);
+            }
+
+            outReport = new ByteArrayOutputStream();
+            ss.write(outReport);
+
+            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
+            pdfSaveOptions.setOnePagePerSheet(true);
+
+            com.aspose.cells.Workbook wb = new com.aspose.cells.Workbook(new ByteArrayInputStream(outReport.toByteArray()));
+            PageSetup pageSetup = wb.getWorksheets().get(0).getPageSetup();
+            pageSetup.setPaperSize(PaperSizeType.PAPER_A_4);
+
+            ByteArrayOutputStream pdfByteArrayOutputStream = new ByteArrayOutputStream();
+            wb.save(pdfByteArrayOutputStream, pdfSaveOptions);
+
+            ByteArrayResource resource = new ByteArrayResource(pdfByteArrayOutputStream.toByteArray());
+
+            return new MockMultipartFile("file", SnowFlakeUtil.getId() + ".pdf", "application/pdf", resource.getInputStream());
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            if (outReport != null) {
+                try {
+                    outReport.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+            if (byteArrayInputStream != null) {
+                try {
+                    byteArrayInputStream.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void setMergedRegionBorders(Sheet sheet, CellRangeAddress region, CellStyle style) {
+        for (int row = region.getFirstRow(); row <= region.getLastRow(); row++) {
+            Row currentRow = sheet.getRow(row);
+            if (currentRow == null) {
+                currentRow = sheet.createRow(row);
+            }
+            for (int col = region.getFirstColumn(); col <= region.getLastColumn(); col++) {
+                Cell currentCell = currentRow.getCell(col);
+                if (currentCell == null) {
+                    currentCell = currentRow.createCell(col);
+                }
+                currentCell.setCellStyle(style);
+            }
+        }
+    }
+
+    private CellRangeAddress getMergedRegion(Sheet sheet, int row, int col) {
+        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
+            CellRangeAddress mergedRegion = sheet.getMergedRegion(i);
+            if (mergedRegion.isInRange(row, col)) {
+                return mergedRegion;
+            }
+        }
+        return null;
+    }
+}

+ 23 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TrialSummaryClassificationConfigurationController.java

@@ -9,9 +9,11 @@ import org.springblade.core.boot.ctrl.BladeController;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
 import org.springblade.core.tool.api.R;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationMatchDTO;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationRelevancyDTO;
 import org.springblade.manager.entity.TrialSummaryClassificationConfiguration;
 import org.springblade.manager.service.ITrialSummaryClassificationConfigurationService;
-import org.springblade.manager.vo.ContractInfoVO;
+import org.springblade.manager.vo.TrialTreeVO;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
@@ -54,5 +56,25 @@ public class TrialSummaryClassificationConfigurationController extends BladeCont
         return R.data(pages);
     }
 
+    @ApiOperationSupport(order = 5)
+    @ApiOperation(value = "匹配划分", notes = "传入TrialSummaryClassificationConfigurationMatchDTO")
+    @RequestMapping(value = "/matching", method = RequestMethod.POST)
+    public R<Object> matching(@RequestBody TrialSummaryClassificationConfigurationMatchDTO dto) {
+        return R.data(iTrialSummaryClassificationConfigurationService.matching(dto));
+    }
+
+    @ApiOperationSupport(order = 6)
+    @ApiOperation(value = "关联清表", notes = "传入TrialSummaryClassificationConfigurationRelevancyDTO")
+    @RequestMapping(value = "/relevancy", method = RequestMethod.POST)
+    public R<Object> relevancy(@RequestBody TrialSummaryClassificationConfigurationRelevancyDTO dto) {
+        return R.data(iTrialSummaryClassificationConfigurationService.relevancy(dto));
+    }
+
+    @ApiOperationSupport(order = 7)
+    @ApiOperation(value = "试验树全加载", notes = "传入项目id、classId=page接口数据id、type=1匹配划分 =2关联清表")
+    @RequestMapping(value = "/tree", method = RequestMethod.GET)
+    public R<TrialTreeVO> tree(@RequestParam String projectId, @RequestParam String classId, @RequestParam String type) {
+        return R.data(iTrialSummaryClassificationConfigurationService.tree(projectId, classId, type));
+    }
 
 }

+ 9 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/ITrialSummaryClassificationConfigurationService.java

@@ -2,7 +2,10 @@ package org.springblade.manager.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.core.mp.base.BaseService;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationMatchDTO;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationRelevancyDTO;
 import org.springblade.manager.entity.TrialSummaryClassificationConfiguration;
+import org.springblade.manager.vo.TrialTreeVO;
 
 public interface ITrialSummaryClassificationConfigurationService extends BaseService<TrialSummaryClassificationConfiguration> {
 
@@ -12,4 +15,10 @@ public interface ITrialSummaryClassificationConfigurationService extends BaseSer
 
     IPage<Object> selectContractInfoPage(IPage<Object> page, TrialSummaryClassificationConfiguration obj);
 
+    boolean matching(TrialSummaryClassificationConfigurationMatchDTO dto);
+
+    boolean relevancy(TrialSummaryClassificationConfigurationRelevancyDTO dto);
+
+    TrialTreeVO tree(String projectId, String classId, String type);
+
 }

+ 106 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/TrialSummaryClassificationConfigurationServiceImpl.java

@@ -1,14 +1,27 @@
 package org.springblade.manager.service.impl;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
+import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationMatchDTO;
+import org.springblade.manager.dto.TrialSummaryClassificationConfigurationRelevancyDTO;
 import org.springblade.manager.entity.TrialSummaryClassificationConfiguration;
+import org.springblade.manager.entity.WbsTreePrivate;
 import org.springblade.manager.mapper.TrialSummaryClassificationConfigurationMapper;
 import org.springblade.manager.service.ITrialSummaryClassificationConfigurationService;
+import org.springblade.manager.vo.TrialTreeVO;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 @Service
 @AllArgsConstructor
@@ -16,6 +29,9 @@ public class TrialSummaryClassificationConfigurationServiceImpl
         extends BaseServiceImpl<TrialSummaryClassificationConfigurationMapper, TrialSummaryClassificationConfiguration>
         implements ITrialSummaryClassificationConfigurationService {
 
+    private final JdbcTemplate jdbcTemplate;
+    private final WbsTreePrivateServiceImpl wbsTreePrivateServiceImpl;
+
     @Override
     public TrialSummaryClassificationConfiguration detail(Long id) {
         return baseMapper.selectById(id);
@@ -32,4 +48,94 @@ public class TrialSummaryClassificationConfigurationServiceImpl
         return page.setRecords(data);
     }
 
+    @Override
+    public boolean matching(TrialSummaryClassificationConfigurationMatchDTO dto) {
+        if (ObjectUtil.isNotEmpty(dto.getIds()) && ObjectUtil.isNotEmpty(dto.getClassId()) && ObjectUtil.isNotEmpty(dto.getProjectId())) {
+            String delSql = "DELETE FROM m_trial_summary_class_config_record WHERE type = ? AND class_id = ?";
+            Object[] delParams = {1, dto.getClassId(),};
+            jdbcTemplate.update(delSql, delParams);
+
+            for (String pKeyId : dto.getIds().split(",")) {
+                String insertSql = "INSERT INTO m_trial_summary_class_config_record(id,type,project_id,class_id,p_key_id) VALUES (?,?,?,?,?)";
+                Object[] insertParams = {
+                        SnowFlakeUtil.getId(),
+                        1,
+                        dto.getProjectId(),
+                        dto.getClassId(),
+                        pKeyId
+                };
+                jdbcTemplate.update(insertSql, insertParams);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean relevancy(TrialSummaryClassificationConfigurationRelevancyDTO dto) {
+        if (ObjectUtil.isNotEmpty(dto.getIds()) && ObjectUtil.isNotEmpty(dto.getClassId()) && ObjectUtil.isNotEmpty(dto.getProjectId())) {
+            String delSql = "DELETE FROM m_trial_summary_class_config_record WHERE type = ? AND class_id = ?";
+            Object[] delParams = {2, dto.getClassId(),};
+            jdbcTemplate.update(delSql, delParams);
+
+            for (String pKeyId : dto.getIds().split(",")) {
+                String sql = "INSERT INTO m_trial_summary_class_config_record(id,type,project_id,class_id,p_key_id) VALUES (?,?,?,?,?)";
+                Object[] insertParams = {
+                        SnowFlakeUtil.getId(),
+                        2,
+                        dto.getProjectId(),
+                        dto.getClassId(),
+                        pKeyId
+                };
+                jdbcTemplate.update(sql, insertParams);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public TrialTreeVO tree(String projectId, String classId, String type) {
+        List<WbsTreePrivate> wbsTreePrivatesNodes = wbsTreePrivateServiceImpl.getBaseMapper().selectList(Wrappers.<WbsTreePrivate>lambdaQuery()
+                .select(WbsTreePrivate::getId, WbsTreePrivate::getPKeyId, WbsTreePrivate::getParentId, WbsTreePrivate::getNodeName)
+                .eq(WbsTreePrivate::getProjectId, projectId)
+                .eq(WbsTreePrivate::getWbsType, 2)
+                .eq(WbsTreePrivate::getType, 1)
+                .eq(WbsTreePrivate::getStatus, 1)
+        );
+        return buildTree(wbsTreePrivatesNodes, projectId, classId, type);
+    }
+
+    private TrialTreeVO buildTree(List<WbsTreePrivate> wbsTreePrivatesNodes, String projectId, String classId, String type) {
+        List<TrialTreeVO> trialTreeVOS = BeanUtil.copyProperties(wbsTreePrivatesNodes, TrialTreeVO.class);
+        if (!trialTreeVOS.isEmpty()) {
+            String sql = "SELECT p_key_id FROM m_trial_summary_class_config_record WHERE class_id = ? AND project_id = ? AND type = ?";
+            Object[] params = {classId, projectId, type};
+            List<TrialTreeVO> query = jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(TrialTreeVO.class));
+            Map<Long, TrialTreeVO> statusMap = query.stream().collect(Collectors.toMap(TrialTreeVO::getPKeyId, Function.identity()));
+
+            Map<Long, List<TrialTreeVO>> map = trialTreeVOS.stream().collect(Collectors.groupingBy(TrialTreeVO::getParentId));
+            List<TrialTreeVO> list = trialTreeVOS.stream().filter(f -> f.getParentId() == 0L).collect(Collectors.toList());
+            buildChildNodes(list, map, statusMap);
+            return trialTreeVOS.get(0);
+        }
+        return null;
+    }
+
+    private void buildChildNodes(List<TrialTreeVO> list, Map<Long, List<TrialTreeVO>> map, Map<Long, TrialTreeVO> statusMap) {
+        for (TrialTreeVO trialTreeVO : list) {
+            TrialTreeVO orDefault = statusMap.getOrDefault(trialTreeVO.getPKeyId(), null);
+            if (orDefault != null) {
+                trialTreeVO.setStatus(1);
+            } else {
+                trialTreeVO.setStatus(0);
+            }
+            List<TrialTreeVO> childrenList = map.getOrDefault(trialTreeVO.getId(), null);
+            if (childrenList != null && childrenList.size() > 0) {
+                trialTreeVO.setChildNode(childrenList);
+                trialTreeVO.setHasChild(true);
+                buildChildNodes(childrenList, map, statusMap);
+            }
+        }
+    }
 }