Browse Source

中国病毒信息接口上传

wangsy 2 months ago
parent
commit
05f81efde5

+ 78 - 0
src/main/java/com/qizhen/healsphere/common/util/ProvinceConverter.java

@@ -0,0 +1,78 @@
+package com.qizhen.healsphere.common.util;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Description: 对象转换工具类
+ * @author: gaodm
+ * @time: 2018/12/14 14:21
+ */
+public class ProvinceConverter {
+
+    // 使用静态代码块初始化映射表(兼容 Java 8)
+    private static final Map<String, String> PROVINCE_MAP;
+    static {
+        Map<String, String> map = new HashMap<>();
+        map.put("anhui", "安徽");
+        map.put("beijing", "北京");
+        map.put("chongqing", "重庆");
+        map.put("fujian", "福建");
+        map.put("gansu", "甘肃");
+        map.put("guangdong", "广东");
+        map.put("guangxi", "广西");
+        map.put("guizhou", "贵州");
+        map.put("hainan", "海南");
+        map.put("hebei", "河北");
+        map.put("heilongjiang", "黑龙江");
+        map.put("henan", "河南");
+        map.put("hubei", "湖北");
+        map.put("hunan", "湖南");
+        map.put("neimenggu", "内蒙古");
+        map.put("jiangsu", "江苏");
+        map.put("jiangxi", "江西");
+        map.put("jilin", "吉林");
+        map.put("liaoning", "辽宁");
+        map.put("ningxia", "宁夏");
+        map.put("qinghai", "青海");
+        map.put("shaanxi", "陕西");
+        map.put("shandong", "山东");
+        map.put("shanghai", "上海");
+        map.put("shanxi", "山西");
+        map.put("sichuan", "四川");
+        map.put("taiwan", "台湾");
+        map.put("tianjin", "天津");
+        map.put("tibet", "西藏");
+        map.put("xinjiang", "新疆");
+        map.put("yunnan", "云南");
+        map.put("zhejiang", "浙江");
+        // 特别行政区
+        map.put("hong_kong", "香港");
+        map.put("macao", "澳门");
+        PROVINCE_MAP = Collections.unmodifiableMap(map); // 不可变 Map
+    }
+
+    /**
+    * 将英文省份转换为中文(支持多个地区逗号分隔)
+    * @param englishNames 如 "Yunnan, Guangdong"
+    * @return 如 "云南, 广东"
+    */
+    public static String toChinese(String englishNames) {
+        if (englishNames == null || englishNames.isEmpty()) {
+            return "未知地区";
+        }
+        String[] parts = englishNames.split(",\\s*");
+        StringBuilder result = new StringBuilder();
+        for (String part : parts) {
+            String key = part.trim().toLowerCase();
+            String chinese = PROVINCE_MAP.getOrDefault(key, part + "(未知)");
+            result.append(chinese).append(", ");
+        }
+        if (result.length() > 0) {
+            result.setLength(result.length() - 2); // 移除末尾的 ", "
+        }
+        return result.toString();
+    }
+}

+ 197 - 22
src/main/java/com/qizhen/healsphere/facade/IsolateFacade.java

@@ -2,12 +2,14 @@ package com.qizhen.healsphere.facade;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.qizhen.healsphere.common.util.BeanUtil;
+import com.qizhen.healsphere.common.util.ProvinceConverter;
 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.ChinaInputVO;
 import com.qizhen.healsphere.web.vo.InputVO;
 import org.apache.poi.util.StringUtil;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -54,8 +56,8 @@ public class IsolateFacade extends IsolateServiceImpl {
                     .map(Epiflu::getSubtype)
                     .noneMatch(st -> Objects.equals(st, inputVO.getSubtype()));
 
-            if(flag){
-               return getIsolateDTOList(inputVO);
+            if (flag) {
+                return getIsolateDTOList(inputVO);
             }
             resultList.add(isolateDTO);
         } else {
@@ -132,7 +134,7 @@ public class IsolateFacade extends IsolateServiceImpl {
             isolateDTO.setSubtype(subtype);
             isolateDTO.setCumulative(getCumulativeList(subtype));
             isolateDTO.setAnalyse(analyse);
-            isolateDTO.setGeoInfoList(getGeoInfoList(inputVO,subtype));
+            isolateDTO.setGeoInfoList(getGeoInfoList(inputVO, subtype));
             resultList.add(isolateDTO);
         }
         return resultList;
@@ -162,7 +164,7 @@ public class IsolateFacade extends IsolateServiceImpl {
         return cumulative;
     }
 
-    public List<GeoInfo> getGeoInfoList(InputVO inputVO,String input) {
+    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")
@@ -209,40 +211,213 @@ public class IsolateFacade extends IsolateServiceImpl {
     }
 
 
-
     /**
      * 获取中国的病毒分布情况
      *
-     * @param inputVO
+     * @param chinaInputVO
      * @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()));
+    public List<ChinaIsolateDTO> getChinaIsolate(ChinaInputVO chinaInputVO) {
+        List<ChinaIsolateDTO> resultList = new ArrayList<>();
+        List<ChinaEpifluDTO> chinaEpiflu = epifluMapper.findChinaEpiflu(chinaInputVO);
 
-            if(flag){
-                return getIsolateDTOList(inputVO);
+        if (chinaEpiflu == null) {
+            return resultList;
+        }
+
+        for (ChinaEpifluDTO chinaEpifluDTO : chinaEpiflu) {
+            ChinaIsolateDTO chinaIsolateDTO = new ChinaIsolateDTO();
+            BeanUtil.copyProperties(chinaEpifluDTO, chinaIsolateDTO);
+            chinaIsolateDTO.setEpifluentName("甲流");
+
+
+            // 定义日期格式
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+            String subtype = chinaEpifluDTO.getSubtype();
+            QueryWrapper<Epiflu> wrapper = new QueryWrapper<>();
+            wrapper.eq("subtype", subtype).eq("is_china", 1)
+                    .orderByAsc("subtype", "collection_date");
+            List<Epiflu> epiflus = epifluMapper.selectList(wrapper);
+
+            if (epiflus.isEmpty()) {
+                continue; // 跳过无数据的subtype
             }
-            resultList.add(isolateDTO);
-        } else {
-            return getIsolateDTOList(inputVO);
+
+            // 处理首个隔离信息
+            Epiflu firstEpiflu = epiflus.get(0);
+//            String firstLocation = firstEpiflu.getChinaCity() != null ? firstEpiflu.getChinaCity() : "未知地区";
+            String firstLocation = ProvinceConverter.toChinese(firstEpiflu.getChinaCity());
+            Date firstDate = firstEpiflu.getCollectionDate();
+            // 格式化日期(处理空值)
+            String formattedDate = (firstDate != null) ? dateFormat.format(firstDate) : "未知日期";
+
+            // 收集传播地点(排除首个)
+            Set<String> propagationLocations = new LinkedHashSet<>();
+            for (int i = 1; i < epiflus.size(); i++) {
+//                String chinaCity = epiflus.get(i).getChinaCity();
+                String chinaCity = ProvinceConverter.toChinese(epiflus.get(i).getChinaCity());
+                if (chinaCity != null) {
+//                    String lastSegment = getLastSegment(location);
+                    propagationLocations.add(chinaCity);
+                }
+            }
+            String propagation = propagationLocations.isEmpty() ? "无" : String.join(",", propagationLocations);
+            // 构建分析文本
+            String analyse = String.format("%s首次爆发在%s的%s,之后在%s传播。",
+                    subtype, formattedDate, firstLocation, propagation);
+
+            chinaIsolateDTO.setAnalyse(analyse); //插入说明
+
+            chinaIsolateDTO.setCumulativeMap(getChinaCumulativeList(chinaInputVO, chinaEpifluDTO));//获取各个时间段的数量
+
+            chinaIsolateDTO.setGeoInfoList(chinaGeoInfoList(chinaInputVO, chinaEpifluDTO));//获取中国省份坐标及相关进化分支参数
+
+            resultList.add(chinaIsolateDTO);
         }
 
         return resultList;
     }
 
+
+    public List<GeoInfo> chinaGeoInfoList(ChinaInputVO chinaInputVO, ChinaEpifluDTO chinaEpifluDTO) {
+        // 处理 clade 参数
+        String cladeStr = chinaEpifluDTO.getClade();
+        Set<String> cladeSet = Optional.ofNullable(cladeStr)
+                .filter(s -> !s.trim().isEmpty())
+                .map(s -> Arrays.stream(s.split(","))
+                        .map(String::trim)
+                        .filter(part -> !part.isEmpty())
+                        .collect(Collectors.toSet()))
+                .orElse(Collections.emptySet());
+
+        // 构建查询条件
+        QueryWrapper<Epiflu> epifluWrapper = new QueryWrapper<Epiflu>()
+                .eq("is_china", 1)
+                .eq("subtype", chinaEpifluDTO.getSubtype());
+        if (!cladeSet.isEmpty()) {
+            epifluWrapper.in("clade", cladeSet);
+        }
+
+        // 查询并提取结果
+        List<String> provinceList = epifluMapper.selectList(epifluWrapper).stream()
+                .map(Epiflu::getChinaCity)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        List<String> continentList = StringUtil.isBlank(chinaInputVO.getProvince())
+                ? provinceList
+                : Collections.singletonList(chinaInputVO.getProvince().trim());
+
+        List<GeoInfo> geoInfoList = new ArrayList<>();
+
+        for (String continent : continentList) {
+            GeoInfo geoInfo = new GeoInfo();
+
+            // 1. 获取clade统计数据
+            List<CladeCountResult> cladeCountResult = epifluMapper.selectCladeCountByProvince(continent, chinaEpifluDTO.getSubtype());
+
+            // 2. 转换数据格式
+            List<GeoDataItem> dataItems = cladeCountResult.stream()
+                    .map(result -> new GeoDataItem(result.getClade(), result.getCount()))
+                    .collect(Collectors.toList());
+
+            // 3. 设置中心坐标(注意这里传入的是Double数组)
+            ChinaProvinceCoordinates ChinaCoordinates = ChinaProvinceCoordinates.fromName(continent.toUpperCase().replace(" ", "_"));
+            geoInfo.setContinentName(continent);
+            geoInfo.setCenter(new Double[]{
+                    Double.parseDouble(ChinaCoordinates.getLongitude()),
+                    Double.parseDouble(ChinaCoordinates.getLatitude())
+            });
+
+            // 4. 设置数据
+            geoInfo.setData(dataItems);
+
+            geoInfoList.add(geoInfo);
+        }
+        return geoInfoList;
+    }
+
+
+    public Map<String, CumulativeDTO> getChinaCumulativeList(ChinaInputVO chinaInputVO, ChinaEpifluDTO chinaEpifluDTO) {
+        String cladeStr = chinaEpifluDTO.getClade();
+        List<String> cladeList = Optional.ofNullable(cladeStr)
+                .filter(s -> !s.trim().isEmpty())
+                .map(s -> Arrays.stream(s.split(","))
+                        .map(String::trim)          // 去除前后空格
+                        .filter(part -> !part.isEmpty()) // 过滤空字符串
+                        .collect(Collectors.toList())    // 改为收集为 List
+                )
+                .orElse(Collections.emptyList());        // 默认返回空 List
+        Map<String, CumulativeDTO> cumulativeMap = new HashMap<>();
+
+        ChinaCollectionDateDTO chinaCollectionDateDTO = new ChinaCollectionDateDTO();
+        if (StringUtil.isNotBlank(chinaInputVO.getStartDate()) && StringUtil.isNotBlank(chinaInputVO.getEndDate())) {
+            chinaCollectionDateDTO.setMINCollectionDate(chinaInputVO.getStartDate());
+            chinaCollectionDateDTO.setMAXCollectionDate(chinaInputVO.getEndDate());
+            chinaCollectionDateDTO.setCount(chinaEpifluDTO.getCount());
+        } else {
+            String subtype = chinaEpifluDTO.getSubtype();
+            chinaCollectionDateDTO = epifluMapper.findChinaCollectionDate(subtype);
+        }
+
+        // 定义起始和结束日期
+        LocalDate startDate = LocalDate.parse(chinaCollectionDateDTO.getMINCollectionDate());
+        LocalDate endDate = LocalDate.parse(chinaCollectionDateDTO.getMAXCollectionDate());
+        // 生成日期列表
+        List<LocalDate> dateList = generateDateIntervals(startDate, endDate, 30);
+
+        for (LocalDate localDate : dateList) {
+            CumulativeDTO cumulativeDTO = new CumulativeDTO();
+            chinaInputVO.setStartDate(localDate.toString());
+            chinaInputVO.setSubtype(chinaEpifluDTO.getSubtype());
+            chinaInputVO.setClade(cladeList);
+            Integer count = epifluMapper.selectCountByChinaInput(chinaInputVO);
+            double percentage = (chinaCollectionDateDTO.getCount() == 0) ? 0.0 : (count * 100.0) / chinaCollectionDateDTO.getCount();
+            cumulativeDTO.setCount(count);
+            cumulativeDTO.setPercentage(percentage);
+            cumulativeMap.put(localDate.toString(), cumulativeDTO);
+        }
+        return cumulativeMap;
+    }
+
+
     public static String getLastSegment(String input) {
         Pattern pattern = Pattern.compile(".*/([^/]+)/?");
         Matcher matcher = pattern.matcher(input);
         return matcher.find() ? matcher.group(1) : "";
     }
 
+
+    /**
+     * 生成固定间隔的日期列表(包含起始和结束日期)
+     *
+     * @param start 起始日期
+     * @param end   结束日期
+     * @param days  间隔天数
+     * @return 日期列表
+     */
+    public static List<LocalDate> generateDateIntervals(LocalDate start, LocalDate end, int days) {
+        List<LocalDate> dates = new ArrayList<>();
+
+        // 添加起始日期
+        dates.add(start);
+
+        // 计算并添加后续日期
+        LocalDate current = start;
+        while (true) {
+            current = current.plusDays(days);
+            if (current.isAfter(end)) {
+                break;
+            }
+            dates.add(current);
+        }
+
+        // 确保结束日期被包含(如果未被添加)
+        if (!dates.get(dates.size() - 1).equals(end)) {
+            dates.add(end);
+        }
+
+        return dates;
+    }
 }

+ 115 - 0
src/main/java/com/qizhen/healsphere/repository/mapper/EpifluMapper.java

@@ -1,8 +1,13 @@
 package com.qizhen.healsphere.repository.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qizhen.healsphere.web.dto.ChinaCollectionDateDTO;
+import com.qizhen.healsphere.web.dto.ChinaEpifluDTO;
+import com.qizhen.healsphere.web.dto.ChinaIsolateDTO;
+import com.qizhen.healsphere.web.dto.IsolateDTO;
 import com.qizhen.healsphere.web.param.CladeCountResult;
 import com.qizhen.healsphere.web.param.Epiflu;
+import com.qizhen.healsphere.web.vo.ChinaInputVO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -72,4 +77,114 @@ public interface EpifluMapper extends BaseMapper<Epiflu> {
     @Select("SELECT COUNT(*) FROM epiflu WHERE subtype = #{subtype} AND is_china = #{is_china} AND collection_date = #{startDate}")
     Integer selectCount(@Param("subtype") String subtype, @Param("is_china") Integer is_china, @Param("startDate") LocalDate startDate);
 
+
+//    @Select("<script>" +
+//            "SELECT " +
+//            "subtype, " +
+//            "china_city, " +
+//            "STRING_AGG(DISTINCT clade::text, ',') AS clade, " +
+//            "COUNT(1) AS count " +
+//            "FROM epiflu " +
+//            "<where> " +
+//            "1=1 " +
+//            "AND is_china = 1 " +
+//            "<if test=\"chinaInputVO.chinaCity != null and chinaInputVO.chinaCity != ''\"> " +
+//            "AND china_city = #{chinaInputVO.chinaCity} " +
+//            "</if> " +
+//            "<if test=\"chinaInputVO.clade != null and chinaInputVO.hasValidClade()\"> " +
+//            "AND clade in " +
+//            "<foreach collection=\"chinaInputVO.clade\" item=\"item\" open=\"(\" close=\")\" separator=\",\"> " +
+//            "#{item}" +
+//            "</foreach>" +
+//            "</if> " +
+//            "<if test=\"chinaInputVO.subtype != null and chinaInputVO.subtype != ''\"> " +
+//            "AND subtype = #{chinaInputVO.subtype} " +
+//            "</if> " +
+//            "<if test=\"chinaInputVO.startDate != null and chinaInputVO.startDate != ''\"> " +
+//            "AND collection_date >= #{chinaInputVO.startDate}::date " +
+//            "</if> " +
+//            "<if test=\"chinaInputVO.endDate != null and chinaInputVO.startDate != ''\"> " +
+//            "AND collection_date &lt;= #{chinaInputVO.endDate}::date " +
+//            "</if> " +
+//            "</where> " +
+//            "GROUP BY subtype, china_city;" +
+//            "</script>")
+//    List<ChinaIsolateDTO> findChinaIsolate(@Param("chinaInputVO") ChinaInputVO chinaInputVO);
+
+    @Select("<script>" +
+            "SELECT " +
+            "subtype, " +
+            "STRING_AGG(DISTINCT clade::text, ',') AS clade, " +
+            "COUNT(1) AS count " +
+            "FROM epiflu " +
+            "<where> " +
+            "1=1 " +
+            "AND is_china = 1 " +
+            "<if test=\"chinaInputVO.province != null and chinaInputVO.province != ''\"> " +
+            "AND china_city = #{chinaInputVO.province} " +
+            "</if> " +
+            "<if test=\"chinaInputVO.clade != null and chinaInputVO.hasValidClade()\"> " +
+            "AND clade in " +
+            "<foreach collection=\"chinaInputVO.clade\" item=\"item\" open=\"(\" close=\")\" separator=\",\"> " +
+            "#{item}" +
+            "</foreach>" +
+            "</if> " +
+            "<if test=\"chinaInputVO.subtype != null and chinaInputVO.subtype != ''\"> " +
+            "AND subtype = #{chinaInputVO.subtype} " +
+            "</if> " +
+            "<if test=\"chinaInputVO.startDate != null and chinaInputVO.startDate != ''\"> " +
+            "AND collection_date >= #{chinaInputVO.startDate}::date " +
+            "</if> " +
+            "<if test=\"chinaInputVO.endDate != null and chinaInputVO.startDate != ''\"> " +
+            "AND collection_date &lt;= #{chinaInputVO.endDate}::date " +
+            "</if> " +
+            "</where> " +
+            "GROUP BY subtype;" +
+            "</script>")
+    List<ChinaEpifluDTO> findChinaEpiflu(@Param("chinaInputVO") ChinaInputVO chinaInputVO);
+
+
+    /**
+     * 查询指定洲的clade分布
+     * @param continent 洲名称
+     * @return clade统计列表
+     */
+    @Select("SELECT clade, COUNT(*) AS count " +
+            "FROM public.epiflu " +
+            "WHERE china_city = #{continent} " +
+            "AND subtype = #{subtype} " +
+            "GROUP BY clade " +
+            "ORDER BY count DESC")
+    List<CladeCountResult> selectCladeCountByProvince(@Param("continent") String continent , @Param("subtype") String subType);
+
+
+    @Select("SELECT COUNT(1), MAX(collection_date) AS MAXCollectionDate,MIN(collection_date) AS MINCollectionDate " +
+            "FROM epiflu WHERE subtype = #{subtype} AND is_china = 1")
+    ChinaCollectionDateDTO findChinaCollectionDate(@Param("subtype") String subtype);
+
+    @Select("<script>" +
+            "SELECT " +
+            "COUNT(1) AS count " +
+            "FROM epiflu " +
+            "<where> " +
+            "AND is_china = 1 " +
+            "<if test=\"chinaInputVO.province != null and chinaInputVO.province != ''\"> " +
+            "AND china_city = #{chinaInputVO.province} " +
+            "</if> " +
+            "<if test=\"chinaInputVO.clade != null and chinaInputVO.hasValidClade()\"> " +
+            "AND clade in " +
+            "<foreach collection=\"chinaInputVO.clade\" item=\"item\" open=\"(\" close=\")\" separator=\",\"> " +
+            "#{item}" +
+            "</foreach>" +
+            "</if> " +
+            "<if test=\"chinaInputVO.subtype != null and chinaInputVO.subtype != ''\"> " +
+            "AND subtype = #{chinaInputVO.subtype} " +
+            "</if> " +
+            "<if test=\"chinaInputVO.startDate != null and chinaInputVO.startDate != ''\"> " +
+            "AND collection_date >= #{chinaInputVO.startDate}::date " +
+            "</if> " +
+            "</where>;"+
+            "</script>")
+    Integer selectCountByChinaInput(@Param("chinaInputVO") ChinaInputVO chinaInputVO);
+
 }

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

@@ -2,7 +2,9 @@ package com.qizhen.healsphere.web;
 
 import com.qizhen.healsphere.facade.IsolateFacade;
 import com.qizhen.healsphere.service.IsolateService;
+import com.qizhen.healsphere.web.dto.ChinaIsolateDTO;
 import com.qizhen.healsphere.web.dto.IsolateDTO;
+import com.qizhen.healsphere.web.vo.ChinaInputVO;
 import com.qizhen.healsphere.web.vo.InputVO;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
@@ -27,8 +29,8 @@ public class IsolateController {
 
     @PostMapping("/getChinaIsolate")
     @ResponseBody
-    public List<IsolateDTO> getChinaIsolate(@RequestBody(required = false) InputVO inputVO) {
-        return isolateFacade.getChinaIsolate(inputVO);
+    public List<ChinaIsolateDTO> getChinaIsolate(@RequestBody ChinaInputVO chinaInputVO) {
+        return isolateFacade.getChinaIsolate(chinaInputVO);
     }
 
 }

+ 21 - 0
src/main/java/com/qizhen/healsphere/web/dto/ChinaCollectionDateDTO.java

@@ -0,0 +1,21 @@
+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 ChinaCollectionDateDTO {
+    private Integer count;
+    private String MAXCollectionDate;
+    private String MINCollectionDate;
+}

+ 17 - 0
src/main/java/com/qizhen/healsphere/web/dto/ChinaEpifluDTO.java

@@ -0,0 +1,17 @@
+package com.qizhen.healsphere.web.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Description:
+ * @author: dsYun
+ * @time: 2025/4/24 11:02
+ */
+@Getter
+@Setter
+public class ChinaEpifluDTO {
+    private String clade;
+    private String subtype;
+    private int count;
+}

+ 26 - 0
src/main/java/com/qizhen/healsphere/web/dto/ChinaIsolateDTO.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 ChinaIsolateDTO {
+    private String epifluentName;
+    private String clade;
+    private String subtype;
+    private int count;
+    private Map<String, CumulativeDTO> cumulativeMap;
+    //private Map<String, Object> inEachContinent;
+    private String analyse;
+    private List<GeoInfo> geoInfoList;
+}

+ 16 - 0
src/main/java/com/qizhen/healsphere/web/dto/CumulativeDTO.java

@@ -0,0 +1,16 @@
+package com.qizhen.healsphere.web.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Description:
+ * @author: dsYun
+ * @time: 2025/4/24 11:02
+ */
+@Getter
+@Setter
+public class CumulativeDTO {
+    private double percentage;//百分比
+    private int count;
+}

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

@@ -34,7 +34,8 @@ public enum ChinaProvinceCoordinates {
     XINJIANG("+43.8258", "+87.6178"),
     HONG_KONG("+22.3193", "+114.1694"),
     MACAO("+22.1987", "+113.5439"),
-    TAIWAN("+23.6978", "+120.9605");
+    TAIWAN("+23.6978", "+120.9605"),
+    SHAANXI("34.2655", "108.9487");
 
     private final String latitude;
     private final String longitude;

+ 14 - 2
src/main/java/com/qizhen/healsphere/web/vo/ChinaInputVO.java

@@ -9,7 +9,19 @@ import java.util.List;
 public class ChinaInputVO {
     private List<String> clade; //进化分支
     private String subtype; //亚系
-    private Date startDate;
-    private Date endDate;
+    private String startDate; //开始时间
+    private String endDate; //结束时间
     private String province; //省份
+
+    public boolean hasValidClade() {
+        if (clade == null || clade.isEmpty()) {
+            return false;
+        }
+        for (String item : clade) {
+            if (item != null && !item.trim().isEmpty()) {
+                return true; // 存在有效元素
+            }
+        }
+        return false; // 所有元素为空或空字符串
+    }
 }