From 319630873a6295138597a455ae050034958d8a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B5=B7=E8=88=AA?= <11918452+yang-haihang@user.noreply.gitee.com> Date: Wed, 4 Jun 2025 16:12:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E8=B4=B5=E9=98=B3=E5=B8=82=E5=B1=80=E9=99=88=E6=9C=88=E7=8C=9B?= =?UTF-8?q?=E4=B8=BB=E4=BB=BB=E9=9C=80=E8=A6=81=E7=9A=84=E5=A7=94=E6=89=98?= =?UTF-8?q?=E5=AD=A3=E5=BA=A6=E7=BB=9F=E8=AE=A1=E6=8A=A5=E8=A1=A8=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...trustMaterialCheckoutResultController.java | 12 + ...tmentIdentificationMaterialController.java | 5 +- .../EntrustMaterialCheckoutResultConvert.java | 4 + .../dto/EntrustMaterialCheckoutResultDTO.java | 7 + .../dto/GenerateQuarterlyReportDTO.java | 11 + .../entrustment/dto/ResultExcelDTO.java | 1 + .../entity/EntrustMaterialCheckoutResult.java | 6 + .../EntrustmentIdentificationMaterial.java | 14 + .../EntrustMaterialCheckoutResultService.java | 5 + ...rustMaterialCheckoutResultServiceImpl.java | 516 +++++++++++++++++- .../vo/EntrustMaterialCheckoutResultVO.java | 6 + .../EntrustMaterialCheckoutResultMapper.xml | 1 + 12 files changed, 572 insertions(+), 16 deletions(-) create mode 100644 src/main/java/digital/laboratory/platform/entrustment/dto/GenerateQuarterlyReportDTO.java diff --git a/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustMaterialCheckoutResultController.java b/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustMaterialCheckoutResultController.java index 6a571a0..1078259 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustMaterialCheckoutResultController.java +++ b/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustMaterialCheckoutResultController.java @@ -9,6 +9,7 @@ import digital.laboratory.platform.common.mybatis.security.service.DLPUser; import digital.laboratory.platform.common.security.util.SecurityUtils; import digital.laboratory.platform.entrustment.convert.EntrustMaterialCheckoutResultConvert; import digital.laboratory.platform.entrustment.dto.EntrustMaterialCheckoutResultDTO; +import digital.laboratory.platform.entrustment.dto.GenerateQuarterlyReportDTO; import digital.laboratory.platform.entrustment.dto.ResultExcelDTO; import digital.laboratory.platform.entrustment.query.BaseQuery; import digital.laboratory.platform.entrustment.query.EntrustMaterialCheckoutResultQuery; @@ -24,8 +25,10 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; +import java.sql.Array; import java.sql.SQLException; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; /** @@ -114,4 +117,13 @@ public class EntrustMaterialCheckoutResultController { return R.ok(entrustMaterialCheckoutResultService.pushSuspectDetectionResultTask(dlpUser)); } + + @GetMapping("/generateQuarterlyReportExcel") + public R generateQuarterlyReportExcel(@RequestBody GenerateQuarterlyReportDTO dto, HttpServletResponse response) throws IOException { + if (dto == null || dto.getQuarterlyList() == null || dto.getQuarterlyList().size() == 0 || dto.getYear() == 0) { + return R.failed("请输入正确的参数"); + } + entrustMaterialCheckoutResultService.generateQuarterlyReportExcel(dto, response); + return R.ok("导出成功!"); + } } diff --git a/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustmentIdentificationMaterialController.java b/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustmentIdentificationMaterialController.java index db0d4e6..68aefe8 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustmentIdentificationMaterialController.java +++ b/src/main/java/digital/laboratory/platform/entrustment/controller/EntrustmentIdentificationMaterialController.java @@ -49,6 +49,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.activation.MimetypesFileTypeMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigDecimal; @@ -342,7 +343,7 @@ public class EntrustmentIdentificationMaterialController { @ApiOperation(value = "新增检材信息11-20修改版", notes = "新增检材信息11-20修改版") @PostMapping(value = "/create/entrustmentIdent/im") @PreAuthorize("@pms.hasPermission('EntrustmentIdentificationMaterialCreate')") - public R> createNewIm(@RequestBody List identificationMaterialList, HttpServletRequest httpServletRequest) { + public R> createNewIm(@RequestBody @Valid List identificationMaterialList, HttpServletRequest httpServletRequest) { Principal principal = httpServletRequest.getUserPrincipal(); DLPUser dlpUser = (DLPUser) ((OAuth2Authentication) principal).getUserAuthentication().getPrincipal(); List newIm = entrustmentIdentificationMaterialService.createNewIm(identificationMaterialList, dlpUser); @@ -1200,7 +1201,7 @@ public class EntrustmentIdentificationMaterialController { @GetMapping("/getMaterialPDF/{acceptNo}") @ApiOperation(value = "查看检材图片") @Inner(value = false) - public void getMaterialPDF(@PathVariable String acceptNo, HttpServletResponse response) { + public void getMaterialPDF(@PathVariable String acceptNo, HttpServletResponse response) { try { entrustmentIdentificationMaterialService.getMaterialPDF(acceptNo, response); } catch (Exception e) { diff --git a/src/main/java/digital/laboratory/platform/entrustment/convert/EntrustMaterialCheckoutResultConvert.java b/src/main/java/digital/laboratory/platform/entrustment/convert/EntrustMaterialCheckoutResultConvert.java index 5c4b91e..0ac649c 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/convert/EntrustMaterialCheckoutResultConvert.java +++ b/src/main/java/digital/laboratory/platform/entrustment/convert/EntrustMaterialCheckoutResultConvert.java @@ -19,6 +19,9 @@ public class EntrustMaterialCheckoutResultConvert { } else { entrustMaterialCheckoutResult.setQualitativeResult(null); } + if (CollUtil.isNotEmpty(dto.getMetaboliteResult())){ + entrustMaterialCheckoutResult.setMetaboliteResult(JSON.toJSONString(dto.getMetaboliteResult())); + } entrustMaterialCheckoutResult.setQuantitativeResult(dto.getQuantitativeResult()); entrustMaterialCheckoutResult.setOtherResult(dto.getOtherResult()); entrustMaterialCheckoutResult.setCheckoutRemark(dto.getCheckoutRemark()); @@ -34,6 +37,7 @@ public class EntrustMaterialCheckoutResultConvert { vo.setQuantitativeResult(entity.getQuantitativeResult()); vo.setOtherResult(entity.getOtherResult()); vo.setCheckoutRemark(entity.getCheckoutRemark()); + vo.setMetaboliteResult(entity.getMetaboliteResult()); return vo; } diff --git a/src/main/java/digital/laboratory/platform/entrustment/dto/EntrustMaterialCheckoutResultDTO.java b/src/main/java/digital/laboratory/platform/entrustment/dto/EntrustMaterialCheckoutResultDTO.java index 22b0baf..a2f5c49 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/dto/EntrustMaterialCheckoutResultDTO.java +++ b/src/main/java/digital/laboratory/platform/entrustment/dto/EntrustMaterialCheckoutResultDTO.java @@ -1,5 +1,7 @@ package digital.laboratory.platform.entrustment.dto; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; import digital.laboratory.platform.sys.entity.DrugLite; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -34,6 +36,11 @@ public class EntrustMaterialCheckoutResultDTO { @ApiModelProperty("定性结果") private List qualitativeResult; + /** + * 代谢物结果 + */ + @TableField(updateStrategy = FieldStrategy.IGNORED) + private List metaboliteResult; /** * 定量结果 */ diff --git a/src/main/java/digital/laboratory/platform/entrustment/dto/GenerateQuarterlyReportDTO.java b/src/main/java/digital/laboratory/platform/entrustment/dto/GenerateQuarterlyReportDTO.java new file mode 100644 index 0000000..05af6ac --- /dev/null +++ b/src/main/java/digital/laboratory/platform/entrustment/dto/GenerateQuarterlyReportDTO.java @@ -0,0 +1,11 @@ +package digital.laboratory.platform.entrustment.dto; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +@Data +public class GenerateQuarterlyReportDTO { + int year; + List quarterlyList; +} diff --git a/src/main/java/digital/laboratory/platform/entrustment/dto/ResultExcelDTO.java b/src/main/java/digital/laboratory/platform/entrustment/dto/ResultExcelDTO.java index 02c13ab..ab88ca4 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/dto/ResultExcelDTO.java +++ b/src/main/java/digital/laboratory/platform/entrustment/dto/ResultExcelDTO.java @@ -11,4 +11,5 @@ public class ResultExcelDTO { List oldResult; LocalDateTime startTime; LocalDateTime endTime; + boolean isMetabolite; } diff --git a/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustMaterialCheckoutResult.java b/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustMaterialCheckoutResult.java index 37632a0..23d6df9 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustMaterialCheckoutResult.java +++ b/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustMaterialCheckoutResult.java @@ -36,6 +36,12 @@ public class EntrustMaterialCheckoutResult extends BaseEntity { @TableField(updateStrategy = FieldStrategy.IGNORED) private String quantitativeResult; + /** + * 代谢物结果 + */ + @TableField(updateStrategy = FieldStrategy.IGNORED) + private String metaboliteResult; + /** * 其他鉴定结果 */ diff --git a/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustmentIdentificationMaterial.java b/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustmentIdentificationMaterial.java index 8e3d105..babf344 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustmentIdentificationMaterial.java +++ b/src/main/java/digital/laboratory/platform/entrustment/entity/EntrustmentIdentificationMaterial.java @@ -13,6 +13,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang.StringUtils; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -70,6 +73,7 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { /** * 检材名称 */ + @NotBlank(message = "检材名称不能为空") @ApiModelProperty(value = "检材名称") private String name; @@ -95,6 +99,7 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { /** * 检材性状:继承所取物证性状或从物证性状类别选择 */ + @NotBlank(message = "检材性状不能为空") @ApiModelProperty(value = "检材性状:继承所取物证性状或从物证性状类别选择") private String form; @@ -102,6 +107,7 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { * 检材性状:继承所取物证性状或从物证性状类别选择 * * 2023/5/31 在咸阳分中心添加 */ + @NotBlank(message = "检材性状名称不能为空") @ApiModelProperty(value = "检材性状名称:继承所取物证性状或从物证性状类别选择") private String formName; @@ -128,11 +134,13 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { */ @ApiModelProperty(value = "检材数量, 例如 3.8 克 或 4.5毫升") @JsonSerialize(using = DynamicBigDecimalSerializer.class) + @NotNull(message = "检材质量不能为空") private BigDecimal quantity; /** * 计量单位, 例如 3.8 克 或 4.5毫升 */ + @NotBlank(message = "计量单位不能为空") @ApiModelProperty(value = "计量单位, 例如 3.8 克 或 4.5毫升") private String unit; @@ -489,10 +497,12 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { * 1.定性分析 2.定量分析 3.定性定量分析 4.关联性判断 5。其他 */ @ApiModelProperty(value = "分析项目 1.定性分析 2.定量分析 3.定性定量分析 4.关联性判断 5。其他") + @NotNull(message = "分析项目不能为空") private Integer analysisOption; @ApiModelProperty(value = "将分析项目转为字符串类型") @TableField(exist = false) + private String analysisOptionValue; /** @@ -517,6 +527,7 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { /** * 提取时间--贵阳新增需求 */ + @NotNull(message = "提取时间不能为空") @ApiModelProperty(value = "提取时间--贵阳新增需求") private LocalDateTime drawTime; /** @@ -529,9 +540,11 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { /** * 提取地点--贵阳新增需求 */ + @NotBlank(message = "提取地点不能为空") @ApiModelProperty(value = "提取地点--贵阳新增需求") private String drawPlace; + @NotNull(message = "包装是否完整不能为空") /** * 包装是否完整--贵阳新增需求 */ @@ -540,6 +553,7 @@ public class EntrustmentIdentificationMaterial extends BaseEntity { /* 留存样个数--贵阳新增需求 */ + @NotNull(message = "留存样个数不能为空") @ApiModelProperty(value = "留存样个数--贵阳新增需求") private Integer rtSampleQuantity; diff --git a/src/main/java/digital/laboratory/platform/entrustment/service/EntrustMaterialCheckoutResultService.java b/src/main/java/digital/laboratory/platform/entrustment/service/EntrustMaterialCheckoutResultService.java index 18b8304..51dd2d2 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/service/EntrustMaterialCheckoutResultService.java +++ b/src/main/java/digital/laboratory/platform/entrustment/service/EntrustMaterialCheckoutResultService.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import digital.laboratory.platform.common.core.util.R; import digital.laboratory.platform.common.mybatis.security.service.DLPUser; import digital.laboratory.platform.entrustment.dto.EntrustMaterialCheckoutResultDTO; +import digital.laboratory.platform.entrustment.dto.GenerateQuarterlyReportDTO; import digital.laboratory.platform.entrustment.dto.ResultExcelDTO; import digital.laboratory.platform.entrustment.entity.EntrustMaterialCheckoutResult; import digital.laboratory.platform.entrustment.query.EntrustMaterialCheckoutResultQuery; @@ -18,7 +19,9 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.SQLException; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author ChenJiangBao @@ -82,4 +85,6 @@ public interface EntrustMaterialCheckoutResultService extends IService oldResults = excelDTO.getOldResult(); Integer entrustType = excelDTO.getEntrustType(); + boolean metabolite = excelDTO.isMetabolite(); try { // 1. 构建查询条件 LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); @@ -1047,8 +1057,8 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl materialMap = entrustmentIdentificationMaterialService.lambdaQuery() @@ -1092,7 +1102,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl entrusts = entrustmentService.lambdaQuery() .in(Entrustment::getId, entrustSet) .ge(Entrustment::getStatus, EntrustStatusConstants.ENTRUST_STATUS_ACCEPTED.getStatus()) // status >= 9 - .and(qw ->qw - .eq(Entrustment::getPlatformFlag,false) + .and(qw -> qw + .eq(Entrustment::getPlatformFlag, false) .or() .isNull(Entrustment::getPlatformFlag)) .list(); @@ -1228,7 +1239,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl caseMap = caseEventService.lambdaQuery() .in(CaseEvent::getId, entrusts.stream().map(Entrustment::getCaseId).collect(Collectors.toList())) .list() @@ -1258,7 +1269,7 @@ public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl>> generateQuarterlyReportData(int year, List quarterlyList) { + // 先对季度排序,保证顺序一致 + quarterlyList.sort(Comparator.naturalOrder()); + + HashMap>> resultMap = new HashMap<>(); + + for (Integer quarter : quarterlyList) { + // 计算季度起止时间 + LocalDateTime startDateTime = getQuarterStartDateTime(year, quarter); + LocalDateTime endDateTime = getQuarterEndDateTime(year, quarter); + + System.out.println("开始时间:" + startDateTime); + System.out.println("结束时间:" + endDateTime); + + // 获取该季度所有委托数据(首次鉴定、补充鉴定、重新鉴定) + List entrusts = queryEntrustments(startDateTime, endDateTime, 0, + Arrays.asList("首次鉴定", "补充鉴定", "重新鉴定")); + + if (entrusts == null || entrusts.isEmpty()) { + // 如果无委托则跳过该季度 + continue; + } + + // 统计该季度普通材料检测情况 + Map> quarterMap = calculateMaterialStats(entrusts); + + // 统计生物样本检测情况(委托类型为1) + List inVivoEntrusts = queryEntrustments(startDateTime, endDateTime, 1, null); + if (inVivoEntrusts != null && !inVivoEntrusts.isEmpty()) { + quarterMap.put("生物样本", calculateEntrustmentStats(inVivoEntrusts)); + } + + // 统计初筛检测情况(委托类型0,鉴定结果为“初筛”) + List initialScreeningEntrusts = queryEntrustments(startDateTime, endDateTime, 0, Collections.singletonList("初筛")); + if (initialScreeningEntrusts != null && !initialScreeningEntrusts.isEmpty()) { + quarterMap.put("初筛", calculateEntrustmentStats(initialScreeningEntrusts)); + } + List entrustments = entrustmentService.lambdaQuery() + .between(Entrustment::getAcceptTime, startDateTime, endDateTime) + .ge(Entrustment::getStatus, EntrustStatusConstants.ENTRUST_STATUS_WAITING_CHECK_CLAIM.getStatus()) + .orderByAsc(Entrustment::getAcceptTime) + .list(); + ArrayList integers = new ArrayList<>(); + integers.add(Integer.valueOf(entrustments.get(0).getAcceptNo().split("-")[1])); + integers.add(Integer.valueOf(entrustments.get(entrustments.size() - 1).getAcceptNo().split("-")[1])); + quarterMap.put("entrustCount", integers); + resultMap.put(quarter, quarterMap); + } + + System.out.println(resultMap); + return resultMap; + } + + /** + * 获取指定年份季度开始时间 + */ + private LocalDateTime getQuarterStartDateTime(int year, int quarter) { + Month startMonth = Month.of((quarter - 1) * 3 + 1); + return LocalDateTime.of(LocalDate.of(year, startMonth, 1), LocalTime.MIN); + } + + /** + * 获取指定年份季度结束时间 + */ + private LocalDateTime getQuarterEndDateTime(int year, int quarter) { + Month startMonth = Month.of((quarter - 1) * 3 + 1); + Month endMonth = startMonth.plus(2); + LocalDate endDate = LocalDate.of(year, endMonth, 1).with(TemporalAdjusters.lastDayOfMonth()); + return LocalDateTime.of(endDate, LocalTime.MAX); + } + + /** + * 根据条件查询委托 + * + * @param start 开始时间(包含) + * @param end 结束时间(包含) + * @param entrustmentType 委托类型,0或1 + * @param oldIdentificationResults 鉴定结果列表,null表示不筛选此条件 + * @return 符合条件的委托列表 + */ + private List queryEntrustments(LocalDateTime start, LocalDateTime end, int entrustmentType, List oldIdentificationResults) { + LambdaQueryChainWrapper query = entrustmentService.lambdaQuery() + .between(Entrustment::getAcceptTime, start, end) + .ge(Entrustment::getStatus, EntrustStatusConstants.ENTRUST_STATUS_ACCEPTED.getStatus()) + .eq(Entrustment::getEntrustmentType, entrustmentType); + + if (oldIdentificationResults != null && !oldIdentificationResults.isEmpty()) { + query.in(Entrustment::getOldIdentificationResult, oldIdentificationResults); + } + return query.list(); + } + + /** + * 根据委托列表,计算普通材料的统计数据 + * + * @param entrusts 委托列表 + * @return key为药品名称字符串,value为对应统计值列表(案件总数、检出数、未检出数等) + */ + private Map> calculateMaterialStats(List entrusts) { + Map> quarterMap = new HashMap<>(); + + if (entrusts == null || entrusts.isEmpty()) return quarterMap; + + // 获取所有委托ID + Set entrustIds = entrusts.stream() + .map(Entrustment::getId) + .collect(Collectors.toSet()); + + if (entrustIds.isEmpty()) return quarterMap; + + // 查询材料列表 + List materialList = entrustmentIdentificationMaterialService.lambdaQuery() + .in(EntrustmentIdentificationMaterial::getEntrustmentId, entrustIds) + .list(); + if (materialList == null || materialList.isEmpty()) return quarterMap; + + // 获取材料ID集合 + Set materialIds = materialList.stream() + .map(EntrustmentIdentificationMaterial::getId) + .collect(Collectors.toSet()); + if (materialIds.isEmpty()) return quarterMap; + + // 查询检测结果列表,排除空或null结果 + List resultList = this.lambdaQuery() + .in(EntrustMaterialCheckoutResult::getId, materialIds) + .notLike(EntrustMaterialCheckoutResult::getQualitativeResult, "null") + .isNotNull(EntrustMaterialCheckoutResult::getQualitativeResult) + .list(); + + if (resultList == null) resultList = Collections.emptyList(); + + // 按材料ID分组检测结果,方便判断是否检测 + Map> resultMap = resultList.stream() + .collect(Collectors.groupingBy(EntrustMaterialCheckoutResult::getId)); + + // 按候选药品名称分组材料(名称排序后拼接) + Map> drugGroupMap = materialList.stream() + .collect(Collectors.groupingBy(item -> { + List drugNames = DrugLiteConvert.convertDirtyLiteByJSON(item.getCandidateDrugs()) + .stream() + .map(DrugLite::getName) + .sorted() + .collect(Collectors.toList()); + return String.join("、", drugNames); + })); + + // 遍历药品分组,统计数据 + for (Map.Entry> entry : drugGroupMap.entrySet()) { + String drugNames = entry.getKey(); + List drugMaterials = entry.getValue(); + + // 按委托ID分组材料 + Map> entrustToMaterials = drugMaterials.stream() + .collect(Collectors.groupingBy(EntrustmentIdentificationMaterial::getEntrustmentId)); + + int caseCount = entrustToMaterials.size(); // 案件总数 + int checked = 0, notChecked = 0, partChecked = 0, materialCount = drugMaterials.size(), resultCount = 0; + + // 遍历每个委托的材料,判断检测情况 + for (Map.Entry> e : entrustToMaterials.entrySet()) { + List materials = e.getValue(); + int mCount = materials.size(); + + // 材料中有检测结果的数量 + int rCount = (int) materials.stream() + .filter(m -> resultMap.containsKey(m.getId())) + .count(); + + resultCount += rCount; + + if (rCount == 0) { + notChecked++; + } else if (rCount == mCount) { + checked++; + } else { + partChecked++; + } + } + + int materialNotChecked = materialCount - resultCount; + + // 按顺序添加统计值 + ArrayList integers = new ArrayList<>(); + integers.add(caseCount); // 案件总数 + integers.add(checked); // 检出数(案件数) + integers.add(notChecked); // 未检出数(案件数) + integers.add(partChecked); // 部分检出数(案件数) + integers.add(materialCount); // 材料总数 + integers.add(resultCount); // 材料检出数 + integers.add(materialNotChecked);// 材料未检出数 + + quarterMap.put(drugNames, integers); + } + + return quarterMap; + } + + /** + * 根据委托列表计算生物样本或初筛的统计数据 + * 该统计逻辑和材料统计类似,但针对委托整体 + * + * @param entrusts 委托列表 + * @return 统计数据列表,顺序同材料统计 + */ + private List calculateEntrustmentStats(List entrusts) { + if (entrusts == null || entrusts.isEmpty()) return Collections.emptyList(); + + Set entrustIds = entrusts.stream() + .map(Entrustment::getId) + .collect(Collectors.toSet()); + + if (entrustIds.isEmpty()) return Collections.emptyList(); + + // 查询所有相关材料 + List materials = entrustmentIdentificationMaterialService.lambdaQuery() + .in(EntrustmentIdentificationMaterial::getEntrustmentId, entrustIds) + .list(); + if (materials == null) materials = Collections.emptyList(); + + Set materialIds = materials.stream() + .map(EntrustmentIdentificationMaterial::getId) + .collect(Collectors.toSet()); + + if (materialIds.isEmpty()) return Collections.emptyList(); + + // 查询所有检测结果 + List results = this.lambdaQuery() + .in(EntrustMaterialCheckoutResult::getId, materialIds) + .notLike(EntrustMaterialCheckoutResult::getQualitativeResult, "null") + .isNotNull(EntrustMaterialCheckoutResult::getQualitativeResult) + .list(); + + if (results == null) results = Collections.emptyList(); + + // 按材料ID分组结果 + Map> resultMap = results.stream() + .collect(Collectors.groupingBy(EntrustMaterialCheckoutResult::getId)); + + int caseCount = entrusts.size(); + int materialCount = materials.size(); + int resultCount = results.size(); + + int checked = 0, notChecked = 0, partChecked = 0; + + // 按委托ID分组材料,判断检测状态 + Map> entrustToMaterials = materials.stream() + .collect(Collectors.groupingBy(EntrustmentIdentificationMaterial::getEntrustmentId)); + + for (List matList : entrustToMaterials.values()) { + int totalM = matList.size(); + int detected = (int) matList.stream() + .filter(m -> resultMap.containsKey(m.getId())) + .count(); + + if (detected == 0) notChecked++; + else if (detected == totalM) checked++; + else partChecked++; + } + + ArrayList statistics = new ArrayList<>(); + statistics.add(caseCount); // 案件总数 + statistics.add(checked); // 检出数(案件数) + statistics.add(notChecked); // 未检出数(案件数) + statistics.add(partChecked); // 部分检出数(案件数) + statistics.add(materialCount); // 材料总数 + statistics.add(resultCount); // 材料检出数 + statistics.add(materialCount - resultCount);// 材料未检出数 + + return statistics; + } + + + @Override + public void generateQuarterlyReportExcel(GenerateQuarterlyReportDTO dto, HttpServletResponse response) throws IOException { + HashMap>> map = this.generateQuarterlyReportData(dto.getYear(), dto.getQuarterlyList()); + XSSFWorkbook workbook = new XSSFWorkbook(); + Set quarterlySet = map.keySet(); + quarterlySet.stream().sorted(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1 - o2; + } + }); + for (Integer quarterly : quarterlySet) { + this.generateQuarterlyReportSheet(workbook, dto.getYear(), quarterly, map.get(quarterly)); + } + workbook.write(response.getOutputStream()); + + } + + + public void generateQuarterlyReportSheet(XSSFWorkbook workbook, Integer year, Integer quarterly, Map> map) { + String quarterlyName = ""; + switch (quarterly) { + case 1: + quarterlyName = "一"; + break; + case 2: + quarterlyName = "二"; + break; + case 3: + quarterlyName = "三"; + break; + case 4: + quarterlyName = "四"; + break; + } + XSSFSheet sheet = workbook.createSheet(year + "年第" + quarterlyName + "季度统计"); + int rowIndex = 0; + + // 表头样式(大号黑色字体,居中) + XSSFCellStyle headStyle = workbook.createCellStyle(); + headStyle.setAlignment(HorizontalAlignment.CENTER); + headStyle.setVerticalAlignment(VerticalAlignment.CENTER); + XSSFFont headFont = workbook.createFont(); + headFont.setFontHeightInPoints((short) 16); + headFont.setFontName("宋体"); + headFont.setColor(IndexedColors.BLACK.getIndex()); + headStyle.setFont(headFont); + + // 创建边框居中样式基础 + XSSFCellStyle baseStyle = workbook.createCellStyle(); + baseStyle.setAlignment(HorizontalAlignment.CENTER); + baseStyle.setVerticalAlignment(VerticalAlignment.CENTER); + baseStyle.setBorderTop(BorderStyle.THIN); + baseStyle.setBorderBottom(BorderStyle.THIN); + baseStyle.setBorderLeft(BorderStyle.THIN); + baseStyle.setBorderRight(BorderStyle.THIN); + baseStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + baseStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + baseStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + baseStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + + // 黑色字体样式 + XSSFCellStyle blackStyle = workbook.createCellStyle(); + blackStyle.cloneStyleFrom(baseStyle); + XSSFFont blackFont = workbook.createFont(); + blackFont.setFontHeightInPoints((short) 12); + blackFont.setFontName("宋体"); + blackFont.setColor(IndexedColors.BLACK.getIndex()); + blackStyle.setFont(blackFont); + + // 红色字体样式 + XSSFCellStyle redStyle = workbook.createCellStyle(); + redStyle.cloneStyleFrom(baseStyle); + XSSFFont redFont = workbook.createFont(); + redFont.setFontHeightInPoints((short) 12); + redFont.setFontName("宋体"); + redFont.setColor(IndexedColors.RED.getIndex()); + redStyle.setFont(redFont); + + List integers = map.get("entrustCount"); + + String entrustCount = year + "年" + integers.get(0) + "-" + integers.get(1) + "号种类"; + String[] nameRows = {entrustCount, "案件送检登记数", "案件检出数", "案件未检出数", "案件检出及未检出数", "检材送检登记数", "检材检出数", "检材未检出数"}; + map.remove("entrustCount"); + + int[] wides = {25, 16, 12, 12, 19, 15, 12, 13}; + + // 第一行(合并单元格) + XSSFRow headRow = sheet.createRow(rowIndex); + headRow.setHeight((short) (20.25 * 20)); + Cell headCell = headRow.createCell(0); + headCell.setCellValue(year + "年第" + quarterlyName + "季度"); + headCell.setCellStyle(headStyle); + sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, nameRows.length - 1)); + + // 第二行 表头列名 + XSSFRow nameRow = sheet.createRow(rowIndex + 1); + nameRow.setHeight((short) (14.25 * 20)); + + for (int i = 0; i < nameRows.length; i++) { + sheet.setColumnWidth(i, wides[i] * 256); + Cell cell = nameRow.createCell(i); + cell.setCellValue(nameRows[i]); + // 第1列(索引1)和第5列(索引5)用红色字体样式,其他用黑色 + if (i == 1 || i == 5) { + cell.setCellStyle(redStyle); + } else { + cell.setCellStyle(blackStyle); + } + } + + // 第三行 空白行 + XSSFRow gapRow = sheet.createRow(rowIndex + 2); + gapRow.setHeight((short) (14.25 * 20)); + for (int i = 0; i < nameRows.length; i++) { + Cell cell = gapRow.createCell(i); + cell.setCellStyle(blackStyle); + } + + rowIndex = 3; + + // 拆分 entry 顺序 + List>> normalEntries = new ArrayList<>(); + List>> specialEntries = new ArrayList<>(); + + for (Map.Entry> entry : map.entrySet()) { + String key = entry.getKey(); + if ("生物样本".equals(key) || "初筛".equals(key)) { + specialEntries.add(entry); + } else { + normalEntries.add(entry); + } + } + + // 合并成最终顺序:普通的在前,特殊的在后 + List>> orderedEntries = new ArrayList<>(); + orderedEntries.addAll(normalEntries); + orderedEntries.addAll(specialEntries); + + Integer va1 = 0, va2 = 0, va3 = 0, va4 = 0, va5 = 0, va6 = 0, va7 = 0; + Integer[] ints = {va1, va2, va3, va4, va5, va6, va7}; + + for (Map.Entry> entry : orderedEntries) { + XSSFRow dataRow = sheet.createRow(rowIndex); + dataRow.setHeight((short) (14.25 * 20)); + + for (int i = 0; i < nameRows.length; i++) { + Cell cell = dataRow.createCell(i); + + if (i == 0) { + cell.setCellValue(entry.getKey()); + } else { + Integer val = entry.getValue().get(i - 1); + if (val != null && val != 0) { + cell.setCellValue(val); + ints[i-1] += entry.getValue().get(i - 1); + } + } + + if (i == 1 || i == 5) { + cell.setCellStyle(redStyle); + } else { + cell.setCellStyle(blackStyle); + } + + + } + + // 空白行 + XSSFRow emptyRow = sheet.createRow(rowIndex + 1); + emptyRow.setHeight((short) (14.25 * 20)); + for (int i = 0; i < nameRows.length; i++) { + Cell cell = emptyRow.createCell(i); + cell.setCellStyle(blackStyle); + } + + rowIndex += 2; + } + XSSFRow lastRow = sheet.createRow(rowIndex-1); + lastRow.setHeight((short) (14.25 * 20)); + for (int i = 0; i < nameRows.length; i++) { + if (i == 0){ + Cell cell = lastRow.createCell(i); + cell.setCellStyle(blackStyle); + } + if (i != 0) { + Cell cell = lastRow.createCell(i); + cell.setCellValue(ints[i - 1]); + if (i == 1 || i == 5) { + cell.setCellStyle(redStyle); + } else { + cell.setCellStyle(blackStyle); + } + } + } + } + + } diff --git a/src/main/java/digital/laboratory/platform/entrustment/vo/EntrustMaterialCheckoutResultVO.java b/src/main/java/digital/laboratory/platform/entrustment/vo/EntrustMaterialCheckoutResultVO.java index 5af771a..bb4e570 100644 --- a/src/main/java/digital/laboratory/platform/entrustment/vo/EntrustMaterialCheckoutResultVO.java +++ b/src/main/java/digital/laboratory/platform/entrustment/vo/EntrustMaterialCheckoutResultVO.java @@ -49,6 +49,12 @@ public class EntrustMaterialCheckoutResultVO { @ApiModelProperty("定量结果") private String quantitativeResult; + /** + * 定量结果 + */ + @ApiModelProperty("定量结果") + private String metaboliteResult; + /** * 其他鉴定结果 */ diff --git a/src/main/resources/mapper/EntrustMaterialCheckoutResultMapper.xml b/src/main/resources/mapper/EntrustMaterialCheckoutResultMapper.xml index c1d24bc..1070a02 100644 --- a/src/main/resources/mapper/EntrustMaterialCheckoutResultMapper.xml +++ b/src/main/resources/mapper/EntrustMaterialCheckoutResultMapper.xml @@ -42,6 +42,7 @@ emr.entrust_id, emr.qualitative_result, emr.quantitative_result, + emr.metabolite_result, emr.other_result, emr.checkout_remark, em.name,