Browse Source

代码提交-待优化

wangsy 3 months ago
parent
commit
2b966e733a

+ 248 - 0
src/main/java/com/qizhen/healsphere/facade/IsolateFacade.java

@@ -0,0 +1,248 @@
+package com.qizhen.healsphere.facade;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qizhen.healsphere.common.util.BeanUtil;
+import com.qizhen.healsphere.repository.mapper.EpifluMapper;
+import com.qizhen.healsphere.repository.mapper.EpifluQueryMapper;
+import com.qizhen.healsphere.repository.mapper.entity.Isolate;
+import com.qizhen.healsphere.service.impl.IsolateServiceImpl;
+import com.qizhen.healsphere.web.dto.*;
+import com.qizhen.healsphere.web.param.*;
+import com.qizhen.healsphere.web.vo.InputVO;
+import org.apache.poi.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 用户日志业务层
+ * @author: gaodm
+ * @time: 2018/8/6 9:11
+ */
+@Component
+public class IsolateFacade extends IsolateServiceImpl {
+
+    @Autowired
+    private EpifluMapper epifluMapper;
+
+    @Autowired
+    private EpifluQueryMapper epifluQueryMapper;
+
+    /**
+     * 获取参数
+     *
+     * @param inputVO
+     * @return List<GraphInfo>
+     */
+    public List<IsolateDTO> getIsolate(InputVO inputVO) {
+        List<IsolateDTO> resultList = new ArrayList<>();
+        if (StringUtil.isNotBlank(inputVO.getContinent())) {
+            List<Epiflu> subtypeContinent = getSubtypeContinent(inputVO.getContinent());
+            // 创建并填充DTO
+            IsolateDTO isolateDTO = new IsolateDTO();
+            isolateDTO.setEpifluentName("甲流");
+            boolean flag = StringUtil.isBlank(inputVO.getSubtype())
+                    || subtypeContinent.stream()
+                    .map(Epiflu::getSubtype)
+                    .noneMatch(st -> Objects.equals(st, inputVO.getSubtype()));
+
+            if(flag){
+               return getIsolateDTOList(inputVO);
+            }
+            resultList.add(isolateDTO);
+        } else {
+            return getIsolateDTOList(inputVO);
+        }
+
+        return resultList;
+    }
+
+    public List<IsolateDTO> getIsolateDTOList(InputVO inputVO) {
+        List<GraphInfo> graphInfoList = this.selectGraphInfo(inputVO);
+
+        // 收集所有唯一的subtype
+        List<String> subtypes = graphInfoList.stream()
+                .map(GraphInfo::getSubtype)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (subtypes.isEmpty()) {
+            return Collections.emptyList(); // 根据实际需求返回适当值
+        }
+
+        // 批量查询所有相关的Isolate记录,按subtype和日期排序
+        List<Isolate> allIsolates = getBaseMapper().selectList(
+                new QueryWrapper<Isolate>()
+                        .in("subtype", subtypes)
+                        .orderByAsc("subtype", "collection_date")
+        );
+
+        // 按subtype分组,优化数据处理
+        Map<String, List<Isolate>> isolatesBySubtype = allIsolates.stream()
+                .collect(Collectors.groupingBy(Isolate::getSubtype));
+
+        List<IsolateDTO> resultList = new ArrayList<>();
+        // 定义日期格式
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+        for (GraphInfo graphInfo : graphInfoList) {
+            String subtype = graphInfo.getSubtype();
+            List<Isolate> isolateList = isolatesBySubtype.getOrDefault(subtype, Collections.emptyList());
+
+            if (isolateList.isEmpty()) {
+                continue; // 跳过无数据的subtype
+            }
+
+            // 处理首个隔离信息
+            Isolate firstIsolate = isolateList.get(0);
+            String firstLocation = firstIsolate.getLocation() != null ? firstIsolate.getLocation() : "未知地区";
+            Date firstDate = firstIsolate.getCollectionDate();
+            // 格式化日期(处理空值)
+            String formattedDate = (firstDate != null) ? dateFormat.format(firstDate) : "未知日期";
+
+            // 收集传播地点(排除首个)
+            Set<String> propagationLocations = new LinkedHashSet<>();
+            for (int i = 1; i < isolateList.size(); i++) {
+                String location = isolateList.get(i).getLocation();
+                if (location != null) {
+                    String lastSegment = getLastSegment(location);
+                    propagationLocations.add(lastSegment);
+                }
+            }
+
+            String propagation = propagationLocations.isEmpty() ? "无" : String.join(",", propagationLocations);
+
+            // 构建分析文本
+            String analyse = String.format("%s首次爆发日期:%s在%s爆发,之后在%s传播。",
+                    subtype, formattedDate, firstLocation, propagation);
+
+            // 创建并填充DTO
+            IsolateDTO isolateDTO = new IsolateDTO();
+            BeanUtil.copyProperties(graphInfo, isolateDTO);
+            isolateDTO.setEpifluentName("甲流");
+            isolateDTO.setSubtype(subtype);
+            isolateDTO.setCumulative(getCumulativeList(subtype));
+            isolateDTO.setAnalyse(analyse);
+            isolateDTO.setGeoInfoList(getGeoInfoList(inputVO,subtype));
+            resultList.add(isolateDTO);
+        }
+        return resultList;
+    }
+
+    public Map<String, Integer> getCumulativeList(String subtype) {
+        Date maxCollectionDate = epifluMapper.findMaxCollectionDateBySubtype(subtype);
+        // 更安全的日期转换方式
+        LocalDate startDate = maxCollectionDate.toInstant()
+                .atZone(ZoneId.of("UTC"))  // 明确指定时区为UTC
+                .toLocalDate()
+                .withDayOfYear(1);
+        LocalDate endDate = startDate.plusMonths(12).minusDays(1);
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+        Map<String, Integer> cumulative = new TreeMap<>(); // 使用 TreeMap 保持键的有序性
+        for (int i = 0; i < 12; i++) {
+            LocalDate monthStartDate = startDate.plusMonths(i);
+            LocalDate monthEndDate = monthStartDate.plusMonths(1).minusDays(1);
+            String monthKey = monthStartDate.format(formatter);
+            //如果要把逻辑改成每个月占比,把下面的 startDate改成monthStartDate
+            int countInMonth = epifluMapper.selectCountBySubtypeAndDateRange(subtype, startDate, monthEndDate);
+            int countInYear = epifluMapper.selectCountBySubtypeAndDateRange(subtype, startDate, endDate);
+            int percentage = (int) countInMonth / countInYear * 100;
+            cumulative.put(monthKey, (int) (Math.ceil(percentage * 10.0) / 10.0));
+        }
+        return cumulative;
+    }
+
+    public List<GeoInfo> getGeoInfoList(InputVO inputVO,String input) {
+
+        List<String> continentList = StringUtil.isBlank(inputVO.getContinent())
+                ? Arrays.asList("Asia", "Europe", "Africa", "North America", "South America", "Oceania")
+                : Collections.singletonList(inputVO.getContinent().trim());
+
+        List<GeoInfo> geoInfoList = new ArrayList<>();
+
+        for (String continent : continentList) {
+            GeoInfo geoInfo = new GeoInfo();
+
+            // 1. 获取clade统计数据
+            List<CladeCountResult> cladeCountResult = epifluMapper.selectCladeCountByContinent(continent, input);
+
+            // 2. 转换数据格式
+            List<GeoDataItem> dataItems = cladeCountResult.stream()
+                    .map(result -> new GeoDataItem(result.getClade(), result.getCount()))
+                    .collect(Collectors.toList());
+
+            // 3. 设置中心坐标(注意这里传入的是Double数组)
+            ContinentCoordinates coordinates = ContinentCoordinates.fromName(continent.toUpperCase().replace(" ", "_"));
+            geoInfo.setContinentName(continent);
+            geoInfo.setCenter(new Double[]{
+                    Double.parseDouble(coordinates.getLongitude()),
+                    Double.parseDouble(coordinates.getLatitude())
+            });
+
+            // 4. 设置数据
+            geoInfo.setData(dataItems);
+
+            geoInfoList.add(geoInfo);
+        }
+        return geoInfoList;
+    }
+
+
+    public List<Epiflu> getSubtypeContinent(String continent) {
+        QueryWrapper<Epiflu> wrapper = new QueryWrapper<>();
+        wrapper.select("subtype", "clade", "COUNT(*) AS count")
+                .eq("continent_location", continent)
+                .groupBy("subtype", "clade")
+                .orderByDesc("count");
+
+        return epifluMapper.selectList(wrapper);
+    }
+
+
+
+    /**
+     * 获取中国的病毒分布情况
+     *
+     * @param inputVO
+     * @return List<GraphInfo>
+     */
+    public List<IsolateDTO> getChinaIsolate(InputVO inputVO) {
+        List<IsolateDTO> resultList = new ArrayList<>();
+        if (StringUtil.isNotBlank(inputVO.getContinent())) {
+            List<Epiflu> subtypeContinent = getSubtypeContinent(inputVO.getContinent());
+            // 创建并填充DTO
+            IsolateDTO isolateDTO = new IsolateDTO();
+            isolateDTO.setEpifluentName("甲流");
+            boolean flag = StringUtil.isBlank(inputVO.getSubtype())
+                    || subtypeContinent.stream()
+                    .map(Epiflu::getSubtype)
+                    .noneMatch(st -> Objects.equals(st, inputVO.getSubtype()));
+
+            if(flag){
+                return getIsolateDTOList(inputVO);
+            }
+            resultList.add(isolateDTO);
+        } else {
+            return getIsolateDTOList(inputVO);
+        }
+
+        return resultList;
+    }
+
+    public static String getLastSegment(String input) {
+        Pattern pattern = Pattern.compile(".*/([^/]+)/?");
+        Matcher matcher = pattern.matcher(input);
+        return matcher.find() ? matcher.group(1) : "";
+    }
+
+}

+ 68 - 20
src/main/java/com/qizhen/healsphere/repository/mapper/IsolateMapper.java

@@ -1,32 +1,80 @@
 package com.qizhen.healsphere.repository.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.qizhen.healsphere.web.param.Isolate;
+import com.qizhen.healsphere.repository.mapper.entity.Isolate;
+import com.qizhen.healsphere.web.param.GraphInfo;
 import com.qizhen.healsphere.web.vo.InputVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
-import java.util.Date;
 import java.util.List;
 
 public interface IsolateMapper extends BaseMapper<Isolate> {
-    @Select("<script>" +
-            "SELECT COUNT(*) FROM public.isolate WHERE 1=1 " +
-            "<if test='constellation != null and constellation.size() > 0'>" +
-            "   AND clade IN <foreach collection='constellation' item='item' open='(' separator=',' close=')'>#{item}</foreach>" +
-            "</if>" +
-            "<if test='subtype != null and subtype != \"\"'>" +
-            "   AND subtype = #{subtype}" +
-            "</if>" +
-            "<if test='isChina != null'>" +
-            "   AND is_china = #{isChina}" +
-            "</if>" +
-            "<if test='startDate != null'>" +
-            "   AND collection_date &gt;= #{startDate}" +
-            "</if>" +
-            "<if test='endDate != null'>" +
-            "   AND collection_date &lt;= #{endDate}" +
-            "</if>" +
+//
+//    @Select("<script>" +
+//            "SELECT COUNT(*) FROM public.isolate WHERE 1=1 " +
+//            "<if test='constellation != null and constellation.size() > 0'>" +
+//            "   AND clade IN <foreach collection='constellation' item='item' open='(' separator=',' close=')'>#{item}</foreach>" +
+//            "</if>" +
+//            "<if test='subtype != null and subtype != \"\"'>" +
+//            "   AND subtype = #{subtype}" +
+//            "</if>" +
+//            "<if test='isChina != null'>" +
+//            "   AND is_china = #{isChina}" +
+//            "</if>" +
+//            "<if test='startDate != null'>" +
+//            "   AND collection_date &gt;= #{startDate}" +
+//            "</if>" +
+//            "<if test='endDate != null'>" +
+//            "   AND collection_date &lt;= #{endDate}" +
+//            "</if>" +
+//            "</script>")
+//    int selectCount(@Param("constellation") List<String> constellation, @Param("subtype") String subtype, @Param("isChina") Integer isChina, @Param("startDate") Date startDate, @Param("endDate") Date endDate);
+
+    // 获取subtype的列表
+//    @Select("<script>" +
+//            "SELECT " +
+//            "  subtype, " +
+//            "  STRING_AGG(DISTINCT clade::text, ',') AS clades, " +
+//            "  COUNT(1) AS count, " +
+//            "  MAX(collection_date) AS cumulative " +
+//            "FROM isolate " +
+//            "<where> " +
+//            "  1=1 " +
+//            "  <if test=\"inputVO.subtype != null and inputVO.subtype != ''\"> " + // 使用inputVO.subtype
+//            "    AND subtype = #{inputVO.subtype} " +
+//            "  </if> " +
+//            "  <if test=\"inputVO.startDate != null\"> " +
+//            "    AND collection_date >= #{inputVO.startDate} " +
+//            "  </if> " +
+//            "  <if test=\"inputVO.endDate != null\"> " +
+//            "    AND collection_date &lt;= #{inputVO.endDate} " +
+//            "  </if> " +
+//            "</where> " +
+//            "GROUP BY subtype " +
+//            "</script>")
+//    List<GraphInfo> selectGraphInfo(@Param("inputVO") InputVO inputVO);
+
+        @Select("<script>" +
+            "SELECT " +
+            "  subtype, " +
+            "  STRING_AGG(DISTINCT clade::text, ',') AS clade, " +
+            "  COUNT(1) AS count, " +
+            "  MAX(collection_date) AS cumulative " +
+            "FROM isolate " +
+            "<where> " +
+            "  1=1 " +
+            "  <if test=\"inputVO.subtype != null and inputVO.subtype != ''\"> " + // 使用inputVO.subtype
+            "    AND subtype = #{inputVO.subtype} " +
+            "  </if> " +
+            "  <if test=\"inputVO.startDate != null\"> " +
+            "    AND collection_date >= #{inputVO.startDate} " +
+            "  </if> " +
+            "  <if test=\"inputVO.endDate != null\"> " +
+            "    AND collection_date &lt;= #{inputVO.endDate} " +
+            "  </if> " +
+            "</where> " +
+            "GROUP BY subtype " +
             "</script>")
-    int selectCount(@Param("constellation") List<String> constellation, @Param("subtype") String subtype, @Param("isChina") Integer isChina, @Param("startDate") Date startDate, @Param("endDate") Date endDate);
+    public List<GraphInfo> selectGraphInfo(@Param("inputVO")InputVO inputVO);
 }

+ 79 - 0
src/main/java/com/qizhen/healsphere/repository/mapper/entity/Isolate.java

@@ -0,0 +1,79 @@
+package com.qizhen.healsphere.repository.mapper.entity;
+
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("isolate")
+public class Isolate implements Serializable {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "isolate_id")
+    private String isolateId;
+
+    /**
+     * 病毒名称
+     */
+    @TableField("isolate_name")
+    private String isolateName;
+
+    /**
+     * 采集日期
+     */
+    @TableField(value = "collection_date")
+    private Date collectionDate;
+
+    /**
+     * 提交日期
+     */
+    @TableField(value = "submission_date")
+    private Date submissionDate;
+
+    /**
+     * 更新日期
+     */
+    @TableField(value = "update_date")
+    private Date updateDate;
+
+    /**
+     * 亚型
+     */
+    @TableField("subtype")
+    private String subtype;
+
+    /**
+     * 基因型
+     */
+    @TableField("genotype")
+    private String genotype;
+
+    /**
+     * 谱系
+     */
+    @TableField("lineage")
+    private String lineage;
+
+    /**
+     * 进化分支
+     */
+    @TableField("clade")
+    private String clade;
+
+    /**
+     * 地理来源
+     */
+    @TableField("location")
+    private String location;
+
+    /**
+     * 宿主外键
+     */
+    @TableField("host_id")
+    private String hostId;
+}

+ 15 - 0
src/main/java/com/qizhen/healsphere/service/IsolateService.java

@@ -0,0 +1,15 @@
+package com.qizhen.healsphere.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qizhen.healsphere.repository.mapper.entity.Isolate;
+import com.qizhen.healsphere.web.param.GraphInfo;
+import com.qizhen.healsphere.web.vo.InputVO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public interface IsolateService extends IService<Isolate> {
+
+    public List<GraphInfo> selectGraphInfo(InputVO inputVO);
+}

+ 20 - 0
src/main/java/com/qizhen/healsphere/service/impl/IsolateServiceImpl.java

@@ -0,0 +1,20 @@
+package com.qizhen.healsphere.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qizhen.healsphere.repository.mapper.*;
+import com.qizhen.healsphere.repository.mapper.entity.Isolate;
+import com.qizhen.healsphere.service.IsolateService;
+import com.qizhen.healsphere.web.param.*;
+import com.qizhen.healsphere.web.vo.InputVO;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Service
+public class IsolateServiceImpl extends ServiceImpl<IsolateMapper, Isolate> implements IsolateService {
+
+    @Override
+    public List<GraphInfo> selectGraphInfo(InputVO inputVO) {
+        return baseMapper.selectGraphInfo(inputVO);
+    }
+}

+ 6 - 6
src/main/java/com/qizhen/healsphere/service/impl/ZYApiServiceImpl.java

@@ -269,12 +269,12 @@ public Map<String, Object> getInEachContinentMap(String subtype) {
             for (LocalDate date = startLocalDate; !date.isAfter(endLocalDate); date = date.plusDays(1)) {
                 // 遍历 constellation 列表
                 int totalCount = 0;
-                for (String constellation : inputVO.getConstellation()) {
-                    Integer count = epifluMapper.selectDayCount(inputVO.getSubtype(), constellation, date);
-                    if (count != null) {
-                        totalCount += count;
-                    }
-                }
+//                for (String constellation : inputVO.getConstellation()) {
+//                    Integer count = epifluMapper.selectDayCount(inputVO.getSubtype(), constellation, date);
+//                    if (count != null) {
+//                        totalCount += count;
+//                    }
+//                }
                 dailyCounts.put(date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), totalCount);
             }
             // 设置累积数据

+ 34 - 0
src/main/java/com/qizhen/healsphere/web/IsolateController.java

@@ -0,0 +1,34 @@
+package com.qizhen.healsphere.web;
+
+import com.qizhen.healsphere.facade.IsolateFacade;
+import com.qizhen.healsphere.service.IsolateService;
+import com.qizhen.healsphere.web.dto.IsolateDTO;
+import com.qizhen.healsphere.web.vo.InputVO;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@Slf4j
+@Api(value = "突发传染病病原体快速识别与溯源API", tags = { "突发传染病病原体快速识别与溯源API" })
+public class IsolateController {
+
+    @Autowired
+    private IsolateFacade isolateFacade;
+
+    @PostMapping("/getIsolate")
+    @ResponseBody
+    public List<IsolateDTO> getIsolate(@RequestBody(required = false) InputVO inputVO) {
+        return isolateFacade.getIsolate(inputVO);
+    }
+
+    @PostMapping("/getChinaIsolate")
+    @ResponseBody
+    public List<IsolateDTO> getChinaIsolate(@RequestBody(required = false) InputVO inputVO) {
+        return isolateFacade.getChinaIsolate(inputVO);
+    }
+
+}

+ 1 - 1
src/main/java/com/qizhen/healsphere/web/ZYController.java

@@ -143,7 +143,7 @@ public class ZYController {
             for (String subType : subTypeList) {
                 GraphInfo graphInfo = new GraphInfo();
                 graphInfo.setEpifluentName("甲流");
-                graphInfo.setClade(zyApiService.getCladeList(subType));
+//                graphInfo.setClade(zyApiService.getCladeList(subType));
                 graphInfo.setSubtype(subType);
                 graphInfo.setCount(zyApiService.getCount(subType));
                 graphInfo.setCumulative(zyApiService.getCumulativeList(subType));

+ 26 - 0
src/main/java/com/qizhen/healsphere/web/dto/IsolateDTO.java

@@ -0,0 +1,26 @@
+package com.qizhen.healsphere.web.dto;
+
+import com.qizhen.healsphere.web.param.GeoInfo;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description:
+ * @author: dsYun
+ * @time: 2025/4/24 11:02
+ */
+@Getter
+@Setter
+public class IsolateDTO {
+    private String epifluentName;
+    private String clade;
+    private String subtype;
+    private int count;
+    private Map<String, Integer> cumulative;
+    //private Map<String, Object> inEachContinent;
+    private String analyse;
+    private List<GeoInfo> geoInfoList;
+}

+ 3 - 1
src/main/java/com/qizhen/healsphere/web/param/Epiflu.java

@@ -9,5 +9,7 @@ public class Epiflu {
     private String location;
     private String subtype;
     private String clade;
-
+    private String isolateId;
+    private String isChina;
+    private String chinaCity;
 }

+ 1 - 1
src/main/java/com/qizhen/healsphere/web/param/GraphInfo.java

@@ -8,7 +8,7 @@ import java.util.Map;
 @Data
 public class GraphInfo {
     private String epifluentName;
-    private List<String> clade;
+    private String clade;
     private String subtype;
     private int count;
     private Map<String, Integer> cumulative;

+ 15 - 0
src/main/java/com/qizhen/healsphere/web/vo/ChinaInputVO.java

@@ -0,0 +1,15 @@
+package com.qizhen.healsphere.web.vo;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class ChinaInputVO {
+    private List<String> clade; //进化分支
+    private String subtype; //亚系
+    private Date startDate;
+    private Date endDate;
+    private String province; //省份
+}

+ 4 - 2
src/main/java/com/qizhen/healsphere/web/vo/InputVO.java

@@ -7,9 +7,11 @@ import java.util.List;
 
 @Data
 public class InputVO {
-    private List<String> constellation;
+    private String constellation;
+    private List<String> clade; // clade
     private String subtype;
-    private Integer isChina; // 0: 全球 1: 中国
+    private Integer isChina = 0; // 0: 全球 1: 中国
     private Date startDate;
     private Date endDate;
+    private String continent; //大洲
 }

+ 6 - 0
src/main/resources/application-local.yml

@@ -30,6 +30,12 @@ spring:
       additional-paths: src/main/java
       exclude: static/**,public/**
 
+
+#mybatis
+mybatis:
+  type-aliases-package: com.qizhen.healsphere.repository.mapper.entity
+  mapper-locations: classpath:mapper/*.xml
+
 springfox:
   documentation:
     enabled: true

+ 44 - 0
src/main/resources/mapper/IsolateMapper.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.qizhen.healsphere.repository.mapper.IsolateMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qizhen.healsphere.repository.mapper.entity.Isolate">
+        <id column="isolate_id" property="isolateId" />
+        <result column="isolate_name" property="isolateName" />
+        <result column="collection_date" property="collectionDate" />
+        <result column="submission_date" property="submissionDate" />
+        <result column="update_date" property="updateDate" />
+        <result column="subtype" property="subtype" />
+        <result column="genotype" property="genotype" />
+        <result column="lineage" property="lineage" />
+        <result column="clade" property="clade" />
+        <result column="location" property="location" />
+        <result column="host_id" property="hostId" />
+    </resultMap>
+
+    <!-- 动态条件查询 -->
+    <select id="selectGraphInfo" resultType="com.qizhen.healsphere.web.param.GraphInfo">
+        SELECT
+        subtype,
+        STRING_AGG(DISTINCT clade::text, ',') AS clades,
+        COUNT(1) AS count,
+        MAX(collection_date) AS cumulative
+        FROM isolate
+        <where>
+        1=1
+            <if test="inputVO.subtype != null">
+                AND subtype = #{inputVO.subtype}
+            </if>
+            <if test="inputVO.startDate != null">
+                AND collection_date >= #{inputVO.startDate}
+            </if>
+            <if test="inputVO.endDate != null">
+                AND collection_date &lt;= #{inputVO.endDate}
+            </if>
+        </where>
+        GROUP BY subtype
+    </select>
+
+
+</mapper>