Browse Source

* 任务开始和结束要操作DMS(开始时间-创建时间,结束时间-修改时间,文件名,任务状态,备注)。根据token调用接口就可以(待测试)
* 注意传入的文件可能数据量过大,可能导致保存xlsx或者csv数据丢失,还要注意字段对应是否正确。(待开发测试)
* 根据文件名和文件类型,下载对应文件类型的数据文件

DESKTOP-6LTVLN7\Liumouren 4 months ago
parent
commit
d5cf7c0125

+ 1 - 1
pom.xml

@@ -29,7 +29,7 @@
     </scm>
     </scm>
     <properties>
     <properties>
         <geotools.version>25.2</geotools.version>
         <geotools.version>25.2</geotools.version>
-        <java.version>11</java.version>
+        <java.version>8</java.version>
     </properties>
     </properties>
     <dependencies>
     <dependencies>
         <dependency>
         <dependency>

+ 274 - 218
src/main/java/com/skyversation/poiaddr/controller/PoiAddressController.java

@@ -7,11 +7,14 @@ import com.skyversation.poiaddr.util.CsvWriter;
 import com.skyversation.poiaddr.util.ExcelReaderUtils;
 import com.skyversation.poiaddr.util.ExcelReaderUtils;
 import com.skyversation.poiaddr.util.RequestUtils;
 import com.skyversation.poiaddr.util.RequestUtils;
 import com.skyversation.poiaddr.util.SerializationUtils;
 import com.skyversation.poiaddr.util.SerializationUtils;
+import com.skyversation.poiaddr.util.dms.DmsTools;
 import com.skyversation.poiaddr.util.fileTools.ReadFileData;
 import com.skyversation.poiaddr.util.fileTools.ReadFileData;
 import com.skyversation.poiaddr.util.geotools.GeoJsonIntersector;
 import com.skyversation.poiaddr.util.geotools.GeoJsonIntersector;
 import com.skyversation.poiaddr.util.geotools.GeoJsonPointInRegion;
 import com.skyversation.poiaddr.util.geotools.GeoJsonPointInRegion;
 import com.skyversation.poiaddr.util.status.AddressLevel;
 import com.skyversation.poiaddr.util.status.AddressLevel;
+import com.skyversation.poiaddr.util.zipunit.DataExportUtil;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
+import org.json.simple.JSONObject;
 import org.locationtech.jts.io.ParseException;
 import org.locationtech.jts.io.ParseException;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
@@ -50,6 +53,20 @@ public class PoiAddressController {
     }
     }
 
 
     /**
     /**
+     * (开发中)
+     * 定时器:每天都增量更新法人库的地名地址数据
+     * 1、获取数据库连接
+     * 2、查询数据
+     * 3、得到地名地址字段和值(清洗数据,并地名格式调整)
+     * 4、批量多任务处理查询结果并判断网格区划
+     * 5、将结果保存为ser文件
+     */
+    public Object updateDataBaseData() {
+        return null;
+    }
+
+    /**
+     * (测试中)
      * 地名查询任务接口
      * 地名查询任务接口
      * * 参数:
      * * 参数:
      * ** 必填:
      * ** 必填:
@@ -63,17 +80,17 @@ public class PoiAddressController {
      * *** outCoordinate: 输出坐标系(默认为wgs84)
      * *** outCoordinate: 输出坐标系(默认为wgs84)
      * *** matchingLevel: 匹配等级,详情见xmind文件【1、2、3、4、5】
      * *** matchingLevel: 匹配等级,详情见xmind文件【1、2、3、4、5】
      * *** regionalJudgment: 区域判断,geojson、shape文件[前端可以内置一些常用的区域文件,通过下拉框的方式进行选择]
      * *** regionalJudgment: 区域判断,geojson、shape文件[前端可以内置一些常用的区域文件,通过下拉框的方式进行选择]
-     * *** outFileType: 输出文件类型【xlsx、csv、geojson、shape】
-     *
+     * <p>
      * JDK版本要规定为11(已完成)
      * JDK版本要规定为11(已完成)
-     * 需要使用request获取参数,参数校验不能太严格。
-     * 接口调用要使用oauth认证,添加拦截器验证headers里面的token。
-     * 任务开始和结束要操作DMS(开始时间-创建时间,结束时间-修改时间,文件名,任务状态,备注)。
-     * 注意传入的文件可能数据量过大,可能导致保存xlsx或者csv数据丢失,还要注意字段对应是否正确。
+     * 需要使用request获取参数,参数校验不能太严格。(通过拦截器的方式也实现了参数乱斗并对必传参数的非空判断以及错误提示)
+     * 接口调用要使用oauth认证,添加拦截器验证headers里面的token。(已完成,暂时使用的是121的Oauth系统,用户名:sj_test)
+     * 任务开始和结束要操作DMS(开始时间-创建时间,结束时间-修改时间,文件名,任务状态,备注)。根据token调用接口就可以(待测试)
+     * 注意传入的文件可能数据量过大,可能导致保存xlsx或者csv数据丢失,还要注意字段对应是否正确。(待开发测试)
+     *
      * @return Object
      * @return Object
      */
      */
     @RequestMapping(value = "/nameQueryTaskInterface", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
     @RequestMapping(value = "/nameQueryTaskInterface", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
-    public Object GeoCoordinate(HttpServletResponse response, HttpServletRequest request, @RequestParam(name = "file") MultipartFile file,
+    public Object GeoCoordinate(HttpServletResponse response, @RequestHeader(value = "token") String token, @RequestParam(name = "file") MultipartFile file,
                                 @RequestParam(name = "addrColNames") String addrColNames,
                                 @RequestParam(name = "addrColNames") String addrColNames,
                                 @RequestParam(name = "inCoordinate", required = false) String inCoordinate,
                                 @RequestParam(name = "inCoordinate", required = false) String inCoordinate,
                                 @RequestParam(name = "latLonColName", required = false) String latLonColName,
                                 @RequestParam(name = "latLonColName", required = false) String latLonColName,
@@ -82,252 +99,291 @@ public class PoiAddressController {
                                 @RequestParam(name = "outCoordinate", required = false) String outCoordinate,
                                 @RequestParam(name = "outCoordinate", required = false) String outCoordinate,
                                 @RequestParam(name = "matchingLevel", required = false) Integer matchingLevel,
                                 @RequestParam(name = "matchingLevel", required = false) Integer matchingLevel,
                                 @RequestParam(name = "regionalJudgment", required = false) MultipartFile regionalJudgment,
                                 @RequestParam(name = "regionalJudgment", required = false) MultipartFile regionalJudgment,
-                                @RequestParam(name = "outFileType", required = false) String outFileType,
-                                @RequestParam(name = "outputFileName", required = false) String outputFileName) throws IOException, ParseException {
+                                @RequestParam(name = "outputFileName", required = false) String outputFileName) {
 //      参数合法性判断
 //      参数合法性判断
         if ((file != null && !file.isEmpty()) && (addrColNames != null && !addrColNames.isEmpty())) {
         if ((file != null && !file.isEmpty()) && (addrColNames != null && !addrColNames.isEmpty())) {
-            outputFileName += UUID.randomUUID();
-//          搜索地址key
-            String addr1Key = "";
-            String addr2Key = "";
-//          参考经纬度key
-            String latKey = "";
-            String lonKey = "";
-            String latLonKey = "";
-            String SplitStr = "";
-//          多地址的形式
-            if (addrColNames.contains(",")) {
-                String[] addrs = addrColNames.split(",");
-                if (addrs.length > 2) {
-                    return "最多支持两个地址列查询,请检查addrColNames参数!";
-                }
-                addr1Key = addrs[0];
-                addr2Key = addrs[1];
-            } else {
-                addr1Key = addrColNames;
+//          先更新一下token
+            DmsTools.DmsToken = token;
+            if (outputFileName == null) {
+                outputFileName = "";
             }
             }
-
-            if (matchingLevel == null) {
-                matchingLevel = 1;
-            }
-//          匹配距离参数合法性判断
-            if (matchingDistance != null) {
-                if (latLonColName == null || latLonColName.isEmpty()) {
-                    return "当匹配距离参数不为空时,必须传入经纬度参数!";
-                }
+            outputFileName += UUID.randomUUID();
+            org.json.JSONObject params = new org.json.JSONObject();
+            params.put("columnId", "1542");
+            params.put("modelId", "1445");
+            org.json.JSONObject paramsContent = new org.json.JSONObject();
+            paramsContent.put("title", outputFileName);
+            paramsContent.put("content", new Date().toString());
+            paramsContent.put("c_content", "任务初始化");
+//          0:任务初始化;1:开始处理;2:异常中断;3:任务完成
+            paramsContent.put("c_task_status", 0);
+            params.put("content", paramsContent);
+            JSONObject initDmsData = DmsTools.addContent(params);
+            if (initDmsData.get("message") == null || !"200".equals(initDmsData.get("code"))) {
+                return "任务初始化失败!DMS数据插入异常,请联系管理员!";
             }
             }
-//          经纬度合法性判断
-            if (latLonColName != null) {
-//              两个key
-                if (latLonColName.contains(",")) {
-                    String[] latLons = latLonColName.split(",");
-                    latKey = latLons[0];
-                    lonKey = latLons[1];
-                } else if (latLonSplitStr != null && !latLonSplitStr.isEmpty()) {
-//                    单个key分割,必须要分割符参数
-                    latLonKey = latLonColName;
-                    SplitStr = latLonSplitStr;
+            paramsContent.put("id", initDmsData.get("content"));
+            System.out.println("任务初始化:" + initDmsData.get("message"));
+            try {
+//              搜索地址key
+                String addr1Key = "";
+                String addr2Key = "";
+//              参考经纬度key
+                String latKey = "";
+                String lonKey = "";
+                String latLonKey = "";
+                String SplitStr = "";
+                paramsContent.put("c_task_status", 1);
+                paramsContent.put("c_content", "开始处理");
+                params.put("content", paramsContent);
+                DmsTools.updateContent(params);
+//              多地址的形式
+                if (addrColNames.contains(",")) {
+                    String[] addrs = addrColNames.split(",");
+                    if (addrs.length > 2) {
+                        return "最多支持两个地址列查询,请检查addrColNames参数!";
+                    }
+                    addr1Key = addrs[0];
+                    addr2Key = addrs[1];
                 } else {
                 } else {
-                    return "经纬度列名解析失败,请传入经度和纬度的列名以逗号分割或传入经纬度列名和分隔符参数!";
+                    addr1Key = addrColNames;
                 }
                 }
-            }
-//          封装解析文件的参数
-//          TODO 文件数据解析
-            List<FileDataDto> fileDataDtoList = ReadFileData.ReadMultipartFile(file);
-            List<FileDataDto> regionalDataList = new ArrayList<>();
-            if (regionalJudgment != null && !regionalJudgment.isEmpty()) {
-//              解析geojson文件得到区域
-                regionalDataList = ReadFileData.ReadMultipartFile(regionalJudgment);
-            }
-//          TODO 补充FileDataDto中的搜索条件参数
-            for (FileDataDto fileDataDto : fileDataDtoList) {
-//              搜索等级
-                fileDataDto.setMatchingLevel(matchingLevel);
-//              TODO 数据过滤
-//              set地名地址搜索字段
-                Map<String, Object> properties = fileDataDto.getProperties();
-                try {
-                    if (properties != null && properties.get(addr1Key) != null && addr1Key != null && properties.get(addr1Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr1Key).toString())) {
-                        String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr1Key, "").toString());
-                        fileDataDto.setAddr1(address);
+
+                if (matchingLevel == null) {
+                    matchingLevel = 1;
+                }
+//              匹配距离参数合法性判断
+                if (matchingDistance != null) {
+                    if (latLonColName == null || latLonColName.isEmpty()) {
+                        return "当匹配距离参数不为空时,必须传入经纬度参数!";
                     }
                     }
-                    if (properties != null && properties.get(addr1Key) != null && addr2Key != null && properties.get(addr2Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr2Key).toString())) {
-                        String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr2Key, "").toString());
-                        fileDataDto.setAddr2(address);
+                }
+//              经纬度合法性判断
+                if (latLonColName != null) {
+//                  两个key
+                    if (latLonColName.contains(",")) {
+                        String[] latLons = latLonColName.split(",");
+                        latKey = latLons[0];
+                        lonKey = latLons[1];
+                    } else if (latLonSplitStr != null && !latLonSplitStr.isEmpty()) {
+//                      单个key分割,必须要分割符参数
+                        latLonKey = latLonColName;
+                        SplitStr = latLonSplitStr;
+                    } else {
+                        return "经纬度列名解析失败,请传入经度和纬度的列名以逗号分割或传入经纬度列名和分隔符参数!";
                     }
                     }
-                } catch (Exception e) {
-                    System.err.println(e);
                 }
                 }
-//              判断是否有参考经纬度字段
-                if (properties != null && latKey != null && !latKey.isEmpty() && lonKey != null && !lonKey.isEmpty() && properties.get(latKey) != null && properties.get(lonKey) != null) {
-                    String latStr = properties.get(latKey).toString();
-                    String lonStr = properties.get(lonKey).toString();
-                    fileDataDto.setLat(Double.valueOf(latStr));
-                    fileDataDto.setLon(Double.valueOf(lonStr));
-                } else if (!latLonKey.isEmpty()) {
-                    String[] latLonKeys = latLonKey.split(SplitStr);
-                    if (properties != null && properties.get(latLonKeys[0]) != null && properties.get(latLonKeys[1]) != null) {
-                        String latStr = properties.get(latLonKeys[0]).toString();
-                        String lonStr = properties.get(latLonKeys[1]).toString();
+//              封装解析文件的参数
+//              TODO 文件数据解析
+                List<FileDataDto> fileDataDtoList = ReadFileData.ReadMultipartFile(file);
+                List<FileDataDto> regionalDataList = new ArrayList<>();
+                if (regionalJudgment != null && !regionalJudgment.isEmpty()) {
+//                  解析geojson文件得到区域
+                    regionalDataList = ReadFileData.ReadMultipartFile(regionalJudgment);
+                }
+//              TODO 补充FileDataDto中的搜索条件参数
+                for (FileDataDto fileDataDto : fileDataDtoList) {
+//                  搜索等级
+                    fileDataDto.setMatchingLevel(matchingLevel);
+//                  TODO 数据过滤
+//                  set地名地址搜索字段
+                    Map<String, Object> properties = fileDataDto.getProperties();
+                    try {
+                        if (properties != null && addr1Key != null && properties.get(addr1Key) != null && properties.get(addr1Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr1Key).toString())) {
+                            String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr1Key, "").toString());
+                            fileDataDto.setAddr1(address);
+                        }
+                        if (properties != null && addr2Key != null && properties.get(addr2Key) != null && properties.get(addr2Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr2Key).toString())) {
+                            String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr2Key, "").toString());
+                            fileDataDto.setAddr2(address);
+                        }
+                    } catch (Exception e) {
+                        System.err.println(e);
+                    }
+//                  判断是否有参考经纬度字段
+                    if (properties != null && latKey != null && !latKey.isEmpty() && lonKey != null && !lonKey.isEmpty() && properties.get(latKey) != null && properties.get(lonKey) != null) {
+                        String latStr = properties.get(latKey).toString();
+                        String lonStr = properties.get(lonKey).toString();
                         fileDataDto.setLat(Double.valueOf(latStr));
                         fileDataDto.setLat(Double.valueOf(latStr));
                         fileDataDto.setLon(Double.valueOf(lonStr));
                         fileDataDto.setLon(Double.valueOf(lonStr));
+                    } else if (!latLonKey.isEmpty()) {
+                        String[] latLonKeys = latLonKey.split(SplitStr);
+                        if (properties != null && properties.get(latLonKeys[0]) != null && properties.get(latLonKeys[1]) != null) {
+                            String latStr = properties.get(latLonKeys[0]).toString();
+                            String lonStr = properties.get(latLonKeys[1]).toString();
+                            fileDataDto.setLat(Double.valueOf(latStr));
+                            fileDataDto.setLon(Double.valueOf(lonStr));
+                        }
                     }
                     }
                 }
                 }
-            }
-//          TODO 地名查询
-            // 创建线程池
-            int threadCount = Runtime.getRuntime().availableProcessors();
-            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
-            List<Future<?>> futures = new ArrayList<>();
-            // 为每个元素提交任务
-            for (FileDataDto fileDataDto : fileDataDtoList) {
-                futures.add(executorService.submit(() -> {
-                    if (fileDataDto.getAddr1() != null || fileDataDto.getAddr2() != null) {
-                        AddressResult addressResult = AddressQueryEngine.getInstance().commonSearchByName(new String[]{fileDataDto.getAddr1().isEmpty() ? fileDataDto.getAddr2() : fileDataDto.getAddr1()}, AddressLevel.values()[fileDataDto.getMatchingLevel() - 1]);
-                        if (addressResult != null) {
-                            if (addressResult.getData() == null || addressResult.getData().size() < 1) {
-                                fileDataDto.getProperties().put("所属街道", null);
-                                fileDataDto.getProperties().put("所属居委", null);
-                                fileDataDto.getProperties().put("纬度", null);
-                                fileDataDto.getProperties().put("经度", null);
-                                fileDataDto.getProperties().put("搜索结果地址", null);
-                            } else {
-                                try {
-                                    for (AddressResult.ContentBean contentBean : addressResult.getData()) {
-                                        String resultAddrKey = contentBean.getAddress();
-                                        if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null && contentBean.getCjJson() != null) {
-                                            String lng = contentBean.getLat() + "";
-                                            String lat = contentBean.getLon() + "";
-                                            fileDataDto.getProperties().put("所属街道", contentBean.getCjJson().getString("所属街道"));
-                                            fileDataDto.getProperties().put("所属居委", contentBean.getCjJson().getString("所属居委"));
-                                            fileDataDto.getProperties().put("纬度", lng);
-                                            fileDataDto.getProperties().put("经度", lat);
-                                            fileDataDto.getProperties().put("搜索结果地址", resultAddrKey);
-                                            System.out.println("成功:" + lat + "," + lng);
-                                            break;
-                                        } else if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null) {
-                                            fileDataDto.getProperties().put("所属街道", null);
-                                            fileDataDto.getProperties().put("所属居委", null);
-                                            fileDataDto.getProperties().put("纬度", null);
-                                            fileDataDto.getProperties().put("经度", null);
-                                            fileDataDto.getProperties().put("搜索结果地址", null);
-                                        }
-                                    }
-                                } catch (Exception e) {
+//              TODO 地名查询
+                // 创建线程池
+                int threadCount = Runtime.getRuntime().availableProcessors();
+                ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+                List<Future<?>> futures = new ArrayList<>();
+                // 为每个元素提交任务
+                for (FileDataDto fileDataDto : fileDataDtoList) {
+                    futures.add(executorService.submit(() -> {
+                        if (fileDataDto.getAddr1() != null || fileDataDto.getAddr2() != null) {
+                            AddressResult addressResult = AddressQueryEngine.getInstance().commonSearchByName(new String[]{fileDataDto.getAddr1().isEmpty() ? fileDataDto.getAddr2() : fileDataDto.getAddr1()}, AddressLevel.values()[fileDataDto.getMatchingLevel() - 1]);
+                            if (addressResult != null) {
+                                if (addressResult.getData() == null || addressResult.getData().size() < 1) {
                                     fileDataDto.getProperties().put("所属街道", null);
                                     fileDataDto.getProperties().put("所属街道", null);
                                     fileDataDto.getProperties().put("所属居委", null);
                                     fileDataDto.getProperties().put("所属居委", null);
                                     fileDataDto.getProperties().put("纬度", null);
                                     fileDataDto.getProperties().put("纬度", null);
                                     fileDataDto.getProperties().put("经度", null);
                                     fileDataDto.getProperties().put("经度", null);
                                     fileDataDto.getProperties().put("搜索结果地址", null);
                                     fileDataDto.getProperties().put("搜索结果地址", null);
-                                    System.err.println(e);
+                                } else {
+                                    try {
+                                        for (AddressResult.ContentBean contentBean : addressResult.getData()) {
+                                            String resultAddrKey = contentBean.getAddress();
+                                            if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null && contentBean.getCjJson() != null) {
+                                                String lng = contentBean.getLat() + "";
+                                                String lat = contentBean.getLon() + "";
+                                                fileDataDto.getProperties().put("所属街道", contentBean.getCjJson().getString("所属街道"));
+                                                fileDataDto.getProperties().put("所属居委", contentBean.getCjJson().getString("所属居委"));
+                                                fileDataDto.getProperties().put("纬度", lng);
+                                                fileDataDto.getProperties().put("经度", lat);
+                                                fileDataDto.getProperties().put("搜索结果地址", resultAddrKey);
+                                                System.out.println("成功:" + lat + "," + lng);
+                                                break;
+                                            } else if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null) {
+                                                fileDataDto.getProperties().put("所属街道", null);
+                                                fileDataDto.getProperties().put("所属居委", null);
+                                                fileDataDto.getProperties().put("纬度", null);
+                                                fileDataDto.getProperties().put("经度", null);
+                                                fileDataDto.getProperties().put("搜索结果地址", null);
+                                            }
+                                        }
+                                    } catch (Exception e) {
+                                        fileDataDto.getProperties().put("所属街道", null);
+                                        fileDataDto.getProperties().put("所属居委", null);
+                                        fileDataDto.getProperties().put("纬度", null);
+                                        fileDataDto.getProperties().put("经度", null);
+                                        fileDataDto.getProperties().put("搜索结果地址", null);
+                                        System.err.println(e);
+                                    }
                                 }
                                 }
                             }
                             }
+                        } else {
+                            fileDataDto.getProperties().put("所属街道", null);
+                            fileDataDto.getProperties().put("所属居委", null);
+                            fileDataDto.getProperties().put("纬度", null);
+                            fileDataDto.getProperties().put("经度", null);
+                            fileDataDto.getProperties().put("搜索结果地址", null);
                         }
                         }
-                    } else {
-                        fileDataDto.getProperties().put("所属街道", null);
-                        fileDataDto.getProperties().put("所属居委", null);
-                        fileDataDto.getProperties().put("纬度", null);
-                        fileDataDto.getProperties().put("经度", null);
-                        fileDataDto.getProperties().put("搜索结果地址", null);
-                    }
-                }));
-            }
-            // 等待所有任务完成
-            for (Future<?> future : futures) {
-                try {
-                    future.get();
-                } catch (InterruptedException | ExecutionException e) {
-                    e.printStackTrace();
+                    }));
                 }
                 }
-            }
-            // 关闭线程池
-            executorService.shutdown();
-//          TODO 转换坐标(根据输入坐标系将参考坐标统一转换成wgs84)
-            ArrayList<String> selectOptions = new ArrayList<>();
-//          {"WGS84(国际通用)", "GCJ02(高德、QQ地图)", "BD09(百度地图)", "上海2000坐标系"}
-            selectOptions.add("WGS84");
-            selectOptions.add("GCJ02");
-            selectOptions.add("BD09");
-            selectOptions.add("SH2000");
-            if (inCoordinate != null && !inCoordinate.isEmpty()) {
-                if (!selectOptions.contains(inCoordinate)) {
-                    return "请传入正确的坐标系名称!可选坐标系名称:WGS84,GCJ02,BD09,SH2000";
+                // 等待所有任务完成
+                for (Future<?> future : futures) {
+                    try {
+                        future.get();
+                    } catch (InterruptedException | ExecutionException e) {
+                        e.printStackTrace();
+                    }
                 }
                 }
-            } else {
-                inCoordinate = "WGS84";
-            }
-            List<Map<String, Object>> dataList = new ArrayList<>();
-//          TODO 距离计算和区域判断和转换坐标
-            for (FileDataDto fileDataDto : fileDataDtoList) {
-                if (fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
-                    double[] lonLat = com.skyversation.poiaddr.util.Coordinate.transformationCoordinateByCoordinate(fileDataDto.getLat(), fileDataDto.getLon(), inCoordinate, outCoordinate);
-                    fileDataDto.setLat(lonLat[1]);
-                    fileDataDto.setLon(lonLat[0]);
+                // 关闭线程池
+                executorService.shutdown();
+//              TODO 转换坐标(根据输入坐标系将参考坐标统一转换成wgs84)
+                ArrayList<String> selectOptions = new ArrayList<>();
+//              {"WGS84(国际通用)", "GCJ02(高德、QQ地图)", "BD09(百度地图)", "上海2000坐标系"}
+                selectOptions.add("WGS84");
+                selectOptions.add("GCJ02");
+                selectOptions.add("BD09");
+                selectOptions.add("SH2000");
+                if (inCoordinate != null && !inCoordinate.isEmpty()) {
+                    if (!selectOptions.contains(inCoordinate)) {
+                        return "请传入正确的坐标系名称!可选坐标系名称:WGS84,GCJ02,BD09,SH2000";
+                    }
+                } else {
+                    inCoordinate = "WGS84";
                 }
                 }
-//              TODO 距离计算
-                if (matchingDistance != null) {
-                    if (fileDataDto.getResultLat() != null && fileDataDto.getResultLon() != null && fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
-                        double distance = com.skyversation.poiaddr.util.Coordinate.calculateDistance(fileDataDto.getResultLat(), fileDataDto.getResultLon(), fileDataDto.getLat(), fileDataDto.getLon());
-                        fileDataDto.setMatchingDistance(distance);
-                        double matchingDistanceDb = Double.parseDouble(matchingDistance.toString());
-                        if (distance < matchingDistanceDb) {
-                            fileDataDto.setLtMatchingDistance("是");
-                        } else {
-                            fileDataDto.setLtMatchingDistance("否");
+                List<Map<String, Object>> dataList = new ArrayList<>();
+//              TODO 距离计算和区域判断和转换坐标
+                for (FileDataDto fileDataDto : fileDataDtoList) {
+                    if (fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
+                        double[] lonLat = com.skyversation.poiaddr.util.Coordinate.transformationCoordinateByCoordinate(fileDataDto.getLat(), fileDataDto.getLon(), inCoordinate, outCoordinate);
+                        fileDataDto.setLat(lonLat[1]);
+                        fileDataDto.setLon(lonLat[0]);
+                    }
+//                  TODO 距离计算
+                    if (matchingDistance != null) {
+                        if (fileDataDto.getResultLat() != null && fileDataDto.getResultLon() != null && fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
+                            double distance = com.skyversation.poiaddr.util.Coordinate.calculateDistance(fileDataDto.getResultLat(), fileDataDto.getResultLon(), fileDataDto.getLat(), fileDataDto.getLon());
+                            fileDataDto.setMatchingDistance(distance);
+                            double matchingDistanceDb = Double.parseDouble(matchingDistance.toString());
+                            if (distance < matchingDistanceDb) {
+                                fileDataDto.setLtMatchingDistance("是");
+                            } else {
+                                fileDataDto.setLtMatchingDistance("否");
+                            }
                         }
                         }
                     }
                     }
-                }
-//              TODO 区域判断(得到经纬度后,要判断得到的点是否在传入的geojson面内)
-                if (regionalDataList != null && regionalDataList.size() > 0) {
+//                  TODO 区域判断(得到经纬度后,要判断得到的点是否在传入的geojson面内)
+                    if (regionalDataList != null && regionalDataList.size() > 0) {
 //                  解析geojson文件得到区域
 //                  解析geojson文件得到区域
-                    boolean isArea= false;
-                    for(FileDataDto regionlItem: regionalDataList){
-                        if(GeoJsonPointInRegion.isPointInGeoJsonRegion(regionlItem.getGeometry().toString(),fileDataDto.getLat(),fileDataDto.getLon())){
-                            isArea = true;
-                            break;
-                        };
+                        boolean isArea = false;
+                        for (FileDataDto regionlItem : regionalDataList) {
+                            if (GeoJsonPointInRegion.isPointInGeoJsonRegion(regionlItem.getGeometry().toString(), fileDataDto.getLat(), fileDataDto.getLon())) {
+                                isArea = true;
+                                break;
+                            }
+                        }
+                        fileDataDto.getProperties().put("是否在区域内", isArea ? "是" : "否");
                     }
                     }
-                    fileDataDto.getProperties().put("是否在区域内",isArea ? "是" : "否");
+                    dataList.add(fileDataDto.getProperties());
                 }
                 }
-                dataList.add(fileDataDto.getProperties());
-            }
-//          TODO 结果输出 result[_匹配等级][_匹配距离][_区域名称][_坐标系].[文件类型]
-            if (outFileType != null) {
-                if (outFileType.contains("csv")) {
-                    outFileType = "csv";
-                }
-                if (outFileType.contains("xlsx")) {
-                    outFileType = "xlsx";
-                }
-
-                switch (outFileType) {
-                    case "xlsx":
-                        ExcelReaderUtils.writeToExcel(dataList, "output/output" + outputFileName + ".xlsx");
-                        ReadFileData.outputFile("output/output" + outputFileName + ".xlsx", response);
-                        break;
-                    case "csv":
-                        CsvWriter.writeToCsv(dataList, "output/output" + outputFileName + ".csv");
-                        ReadFileData.outputFile("output/output" + outputFileName + ".csv", response);
-                        break;
-                    case "geojson":
-                        break;
-                }
-            } else {
+//              TODO 结果输出 result[_匹配等级][_匹配距离][_区域名称][_坐标系].[文件类型]
 //              保存实体类到序列化文件
 //              保存实体类到序列化文件
                 SerializationUtils.serialize(fileDataDtoList, "output/output" + outputFileName + ".ser");
                 SerializationUtils.serialize(fileDataDtoList, "output/output" + outputFileName + ".ser");
+                System.out.println("处理完成!");
+                paramsContent.put("c_task_status", 3);
+                paramsContent.put("c_content", "处理完成!");
+                paramsContent.put("c_file_name", "output/output" + outputFileName + ".ser");
+                params.put("content", paramsContent);
+                DmsTools.updateContent(params);
+                return "文件解析完成,共" + fileDataDtoList.size() + "条数据!";
+            } catch (Exception e) {
+                paramsContent.put("c_task_status", 2);
+                paramsContent.put("c_content", "异常中断:" + e);
+                params.put("content", paramsContent);
+                DmsTools.updateContent(params);
+                return "异常中断:" + e;
             }
             }
-            // 从文件中反序列化读取数据
-            /*List<FileDataDto> deserializedList = SerializationUtils.deserialize("output/output" + outputFileName + ".ser");
-            if (deserializedList != null) {
-                for (FileDataDto person : deserializedList) {
-                    System.out.println(person);
-                }
-            }*/
-            System.out.println("处理完成!");
-            return "文件解析完成,共" + fileDataDtoList.size() + "条数据!";
         } else {
         } else {
             return "file或addrColName不能为空!";
             return "file或addrColName不能为空!";
         }
         }
     }
     }
 
 
+    /**
+     * (开发中)
+     * 根据文件名和文件类型,下载对应文件类型的数据文件
+     *
+     * @param response
+     * @param outFileType: 输出文件类型【xlsx、csv、geojson(暂不支持)、shape(暂不支持)】
+     * @param fileName
+     * @throws IOException
+     */
+    @RequestMapping(value = "/getFileByParams", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public void getFileByParams(HttpServletResponse response, @RequestParam(name = "outFileType", required = false) String outFileType, @RequestParam(name = "fileName", required = false) String fileName) throws IOException {
+        // 从文件中反序列化读取数据
+        List<FileDataDto> deserializedList = SerializationUtils.deserialize(fileName);
+        List<Map<String, Object>> dataList = new ArrayList<>();
+        if (deserializedList != null) {
+            for (FileDataDto person : deserializedList) {
+                dataList.add(person.getProperties());
+            }
+        }
+        if (outFileType != null) {
+            if (outFileType.contains("csv")) {
+                outFileType = "csv";
+            }
+            if (outFileType.contains("xlsx")) {
+                outFileType = "xlsx";
+            }
+        } else {
+            outFileType = "csv";
+        }
+        DataExportUtil.exportDataToZip(dataList, fileName.replace(".ser", ""), response, outFileType);
+    }
 }
 }

+ 7 - 6
src/main/java/com/skyversation/poiaddr/util/dms/login.java → src/main/java/com/skyversation/poiaddr/util/dms/DmsTools.java

@@ -12,9 +12,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
-public class login {
+public class DmsTools {
     public static String DmsToken;
     public static String DmsToken;
 
 
+
     //  登录操作,请求token
     //  登录操作,请求token
     public static String getDmsToken() {
     public static String getDmsToken() {
         JSONObject resultObject = RequestUtils.requestPost("http://121.43.55.7:2101/proxy_oauth/user/login?userName=user001&password=1234567890&clientId=2", null, null);
         JSONObject resultObject = RequestUtils.requestPost("http://121.43.55.7:2101/proxy_oauth/user/login?userName=user001&password=1234567890&clientId=2", null, null);
@@ -39,14 +40,14 @@ public class login {
         return RequestUtils.requestPost("http://121.43.55.7:2101/proxy_dms/content/selectContentList", headers, formDatas);
         return RequestUtils.requestPost("http://121.43.55.7:2101/proxy_dms/content/selectContentList", headers, formDatas);
     }
     }
 
 
-    public static JSONObject addContent(Map<String, String> formDatas) {
+    public static JSONObject addContent(org.json.JSONObject formDatas) {
         Map<String, String> headers = new HashMap<>();
         Map<String, String> headers = new HashMap<>();
         if (DmsToken != null && DmsToken.length() > 0) {
         if (DmsToken != null && DmsToken.length() > 0) {
             headers.put("Token", DmsToken);
             headers.put("Token", DmsToken);
         } else {
         } else {
             headers.put("Token", getDmsToken());
             headers.put("Token", getDmsToken());
         }
         }
-        return RequestUtils.requestPost("http://121.43.55.7:2101/proxy_dms/content/addContent", headers, formDatas);
+        return RequestUtils.requestPost2("http://121.43.55.7:2101/proxy_dms/content/addContent", headers, formDatas);
     }
     }
 
 
     public static JSONObject updateContent(org.json.JSONObject formDatas) {
     public static JSONObject updateContent(org.json.JSONObject formDatas) {
@@ -124,10 +125,10 @@ public class login {
             }
             }
         }else{
         }else{
 //      准备更新数据
 //      准备更新数据
-//      首先得到关键数据(id\columnId\modelId)
+//      首先得到关键数据(id\columnId\modelId) 1542\1445
             org.json.JSONObject params = new org.json.JSONObject();
             org.json.JSONObject params = new org.json.JSONObject();
-            params.put("columnId", "1477");
-            params.put("modelId", "1358");
+            params.put("columnId", "1542");
+            params.put("modelId", "1445");
             for (String schoolNameAndId : newSchoolGeojson.keySet()) {
             for (String schoolNameAndId : newSchoolGeojson.keySet()) {
                 String schoolName = schoolNameAndId.split("_")[0];
                 String schoolName = schoolNameAndId.split("_")[0];
                 String id = schoolNameAndId.split("_")[1];
                 String id = schoolNameAndId.split("_")[1];

+ 3 - 3
src/main/java/com/skyversation/poiaddr/util/fileTools/ReadFileData.java

@@ -48,7 +48,7 @@ public class ReadFileData {
     public static List<FileDataDto> readGeojson(File file) throws IOException {
     public static List<FileDataDto> readGeojson(File file) throws IOException {
         List<FileDataDto> resultList = new ArrayList<>();
         List<FileDataDto> resultList = new ArrayList<>();
         ObjectMapper mapper = new ObjectMapper();
         ObjectMapper mapper = new ObjectMapper();
-        List<FeatureDiy> features = mapper.readValue(file, new TypeReference<>() {
+        List<FeatureDiy> features = mapper.readValue(file, new TypeReference<List<FeatureDiy>>() {
         });
         });
         for (FeatureDiy featureDiy : features) {
         for (FeatureDiy featureDiy : features) {
             FileDataDto fileDataDto = new FileDataDto();
             FileDataDto fileDataDto = new FileDataDto();
@@ -114,10 +114,10 @@ public class ReadFileData {
             for (int cellIndex = 0; cellIndex < headerSize; cellIndex++) {
             for (int cellIndex = 0; cellIndex < headerSize; cellIndex++) {
                 Cell headerCell = headerRow.getCell(cellIndex);
                 Cell headerCell = headerRow.getCell(cellIndex);
                 Cell currentCell = currentRow.getCell(cellIndex);
                 Cell currentCell = currentRow.getCell(cellIndex);
-                if(currentCell != null){
+                if (currentCell != null) {
                     currentCell.setCellType(CellType.STRING);
                     currentCell.setCellType(CellType.STRING);
                     rowMap.put(getCellValue(headerCell).toString(), getCellValue(currentCell).toString());
                     rowMap.put(getCellValue(headerCell).toString(), getCellValue(currentCell).toString());
-                }else{
+                } else {
                     rowMap.put(getCellValue(headerCell).toString(), null);
                     rowMap.put(getCellValue(headerCell).toString(), null);
                 }
                 }
             }
             }

+ 125 - 124
src/main/java/com/skyversation/poiaddr/util/net/MultiThreadedAsyncHttpClient.java

@@ -1,14 +1,15 @@
 package com.skyversation.poiaddr.util.net;
 package com.skyversation.poiaddr.util.net;
 import java.net.URI;
 import java.net.URI;
 import java.net.URLEncoder;
 import java.net.URLEncoder;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
+//import java.net.http.HttpClient;
+//import java.net.http.HttpRequest;
+//import java.net.http.HttpResponse;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.*;
 
 
 /**
 /**
+ * java 11支持的版本
  * executeAsyncRequests 方法:
  * executeAsyncRequests 方法:
  * 接收请求的 URL 列表、请求头和请求参数作为输入。
  * 接收请求的 URL 列表、请求头和请求参数作为输入。
  * 创建一个固定大小的线程池,线程池的大小与 URL 列表的大小相同。
  * 创建一个固定大小的线程池,线程池的大小与 URL 列表的大小相同。
@@ -30,125 +31,125 @@ import java.util.concurrent.*;
  * 通过这种方式,可以实现多线程异步请求,并在所有请求完成后将结果存储到数据库。
  * 通过这种方式,可以实现多线程异步请求,并在所有请求完成后将结果存储到数据库。
  */
  */
 public class MultiThreadedAsyncHttpClient {
 public class MultiThreadedAsyncHttpClient {
-
-    public static void main(String[] args) {
-        // 模拟请求的 URL 列表
-        List<String> urls = Arrays.asList(
-                "https://www.example.com/api/endpoint1",
-                "https://www.example.com/api/endpoint2"
-        );
-
-        // 配置请求头
-        Map<String, String> headers = new HashMap<>();
-        headers.put("Authorization", "Bearer your_token");
-        headers.put("Content-Type", "application/json");
-
-        // 配置请求参数
-        Map<String, String> params = new HashMap<>();
-        params.put("param1", "value1");
-        params.put("param2", "value2");
-
-        // 执行多线程异步请求
-        List<String> results = executeAsyncRequests(urls, headers, params);
-
-        // 模拟将结果存储到数据库
-        saveResultsToDatabase(results);
-    }
-
-    public static List<String> executeAsyncRequests(List<String> urls, Map<String, String> headers, Map<String, String> params) {
-        // 创建一个固定大小的线程池
-        int threadPoolSize = urls.size();
-        ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
-
-        // 创建 CountDownLatch 用于等待所有任务完成
-        CountDownLatch latch = new CountDownLatch(threadPoolSize);
-
-        // 用于存储所有请求的结果
-        List<String> results = new CopyOnWriteArrayList<>();
-
-        // 创建并提交任务到线程池
-        for (String url : urls) {
-            executorService.submit(new RequestTask(url, headers, params, latch, results));
-        }
-
-        try {
-            // 等待所有任务完成
-            latch.await();
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            e.printStackTrace();
-        }
-
-        // 关闭线程池
-        executorService.shutdown();
-
-        return results;
-    }
-
-    static class RequestTask implements Runnable {
-        private final String url;
-        private final Map<String, String> headers;
-        private final Map<String, String> params;
-        private final CountDownLatch latch;
-        private final List<String> results;
-
-        public RequestTask(String url, Map<String, String> headers, Map<String, String> params, CountDownLatch latch, List<String> results) {
-            this.url = url;
-            this.headers = headers;
-            this.params = params;
-            this.latch = latch;
-            this.results = results;
-        }
-
-        @Override
-        public void run() {
-            try {
-                // 创建 HttpClient 实例
-                HttpClient client = HttpClient.newHttpClient();
-
-                // 构建请求 URL,添加参数
-                StringBuilder query = new StringBuilder();
-                for (Map.Entry<String, String> entry : params.entrySet()) {
-                    if (query.length() > 0) {
-                        query.append("&");
-                    }
-                    query.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
-                    query.append("=");
-                    query.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
-                }
-                String fullUrl = url + "?" + query.toString();
-
-                // 创建 HttpRequest 实例
-                HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
-                        .uri(URI.create(fullUrl));
-
-                // 添加请求头
-                for (Map.Entry<String, String> entry : headers.entrySet()) {
-                    requestBuilder.header(entry.getKey(), entry.getValue());
-                }
-
-                HttpRequest request = requestBuilder.build();
-
-                // 发送异步请求
-                CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
-                HttpResponse<String> response = future.get();
-
-                // 处理响应
-                results.add(response.body());
-            } catch (Exception e) {
-                e.printStackTrace();
-            } finally {
-                // 任务完成,减少计数器
-                latch.countDown();
-            }
-        }
-    }
-
-    public static void saveResultsToDatabase(List<String> results) {
-        // 模拟将结果存储到数据库
-        System.out.println("将以下结果存储到数据库:");
-        for (String result : results) {
-            System.out.println(result);
-        }
-    }
+//
+//    public static void main(String[] args) {
+//        // 模拟请求的 URL 列表
+//        List<String> urls = Arrays.asList(
+//                "https://www.example.com/api/endpoint1",
+//                "https://www.example.com/api/endpoint2"
+//        );
+//
+//        // 配置请求头
+//        Map<String, String> headers = new HashMap<>();
+//        headers.put("Authorization", "Bearer your_token");
+//        headers.put("Content-Type", "application/json");
+//
+//        // 配置请求参数
+//        Map<String, String> params = new HashMap<>();
+//        params.put("param1", "value1");
+//        params.put("param2", "value2");
+//
+//        // 执行多线程异步请求
+//        List<String> results = executeAsyncRequests(urls, headers, params);
+//
+//        // 模拟将结果存储到数据库
+//        saveResultsToDatabase(results);
+//    }
+//
+//    public static List<String> executeAsyncRequests(List<String> urls, Map<String, String> headers, Map<String, String> params) {
+//        // 创建一个固定大小的线程池
+//        int threadPoolSize = urls.size();
+//        ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
+//
+//        // 创建 CountDownLatch 用于等待所有任务完成
+//        CountDownLatch latch = new CountDownLatch(threadPoolSize);
+//
+//        // 用于存储所有请求的结果
+//        List<String> results = new CopyOnWriteArrayList<>();
+//
+//        // 创建并提交任务到线程池
+//        for (String url : urls) {
+//            executorService.submit(new RequestTask(url, headers, params, latch, results));
+//        }
+//
+//        try {
+//            // 等待所有任务完成
+//            latch.await();
+//        } catch (InterruptedException e) {
+//            Thread.currentThread().interrupt();
+//            e.printStackTrace();
+//        }
+//
+//        // 关闭线程池
+//        executorService.shutdown();
+//
+//        return results;
+//    }
+//
+//    static class RequestTask implements Runnable {
+//        private final String url;
+//        private final Map<String, String> headers;
+//        private final Map<String, String> params;
+//        private final CountDownLatch latch;
+//        private final List<String> results;
+//
+//        public RequestTask(String url, Map<String, String> headers, Map<String, String> params, CountDownLatch latch, List<String> results) {
+//            this.url = url;
+//            this.headers = headers;
+//            this.params = params;
+//            this.latch = latch;
+//            this.results = results;
+//        }
+//
+//        @Override
+//        public void run() {
+//            try {
+//                // 创建 HttpClient 实例
+//                HttpClient client = HttpClient.newHttpClient();
+//
+//                // 构建请求 URL,添加参数
+//                StringBuilder query = new StringBuilder();
+//                for (Map.Entry<String, String> entry : params.entrySet()) {
+//                    if (query.length() > 0) {
+//                        query.append("&");
+//                    }
+//                    query.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
+//                    query.append("=");
+//                    query.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
+//                }
+//                String fullUrl = url + "?" + query.toString();
+//
+//                // 创建 HttpRequest 实例
+//                HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+//                        .uri(URI.create(fullUrl));
+//
+//                // 添加请求头
+//                for (Map.Entry<String, String> entry : headers.entrySet()) {
+//                    requestBuilder.header(entry.getKey(), entry.getValue());
+//                }
+//
+//                HttpRequest request = requestBuilder.build();
+//
+//                // 发送异步请求
+//                CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
+//                HttpResponse<String> response = future.get();
+//
+//                // 处理响应
+//                results.add(response.body());
+//            } catch (Exception e) {
+//                e.printStackTrace();
+//            } finally {
+//                // 任务完成,减少计数器
+//                latch.countDown();
+//            }
+//        }
+//    }
+//
+//    public static void saveResultsToDatabase(List<String> results) {
+//        // 模拟将结果存储到数据库
+//        System.out.println("将以下结果存储到数据库:");
+//        for (String result : results) {
+//            System.out.println(result);
+//        }
+//    }
 }
 }

+ 188 - 0
src/main/java/com/skyversation/poiaddr/util/zipunit/DataExportUtil.java

@@ -0,0 +1,188 @@
+package com.skyversation.poiaddr.util.zipunit;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * exportDataToZip 方法:
+ * 首先创建保存 XLSX 文件的目录。
+ * 将数据按每 10000 条进行分割。
+ * 调用 createXlsxFile 方法将每组数据生成 XLSX 文件。
+ * 调用 zipDirectory 方法将生成的 XLSX 文件目录打包成 ZIP 文件。
+ * 设置响应头,将 ZIP 文件流输出给客户端。
+ * 最后删除临时文件和目录。
+ * createXlsxFile 方法:
+ * 使用 Apache POI 库创建 XLSX 文件。
+ * 生成表头,将数据填充到表格中。
+ * 自动调整列宽。
+ * zipDirectory 方法:
+ * 使用 ZipOutputStream 将指定目录下的文件打包成 ZIP 文件。
+ * deleteDirectory 方法:
+ * 递归删除指定目录及其所有子文件和子目录。
+ */
+public class DataExportUtil {
+
+    public static void exportDataToZip(List<Map<String, Object>> dataList, String filePath, HttpServletResponse response,String outFileType) throws IOException {
+        // 创建保存 XLSX 文件的目录
+        File directory = new File(filePath);
+        if (!directory.exists()) {
+            directory.mkdirs();
+        }
+
+        // 分割数据,每 10000 条数据一组
+        List<List<Map<String, Object>>> dataChunks = new ArrayList<>();
+        for (int i = 0; i < dataList.size(); i += 10000) {
+            dataChunks.add(dataList.subList(i, Math.min(i + 10000, dataList.size())));
+        }
+        switch (outFileType) {
+            case "xlsx":
+                // 生成 XLSX 文件
+                for (int i = 0; i < dataChunks.size(); i++) {
+                    List<Map<String, Object>> chunk = dataChunks.get(i);
+                    String fileName = filePath + File.separator + "data_" + (i + 1) + ".xlsx";
+                    createXlsxFile(chunk, fileName);
+                }
+                break;
+            case "csv":
+                // 生成 CSV 文件
+                for (int i = 0; i < dataChunks.size(); i++) {
+                    List<Map<String, Object>> chunk = dataChunks.get(i);
+                    String fileName = filePath + File.separator + "data_" + (i + 1) + ".csv";
+                    createCsvFile(chunk, fileName);
+                }
+                break;
+            case "geojson":
+                break;
+        }
+
+        // 创建 ZIP 文件
+        String zipFilePath = filePath + File.separator + "data.zip";
+        zipDirectory(directory, zipFilePath);
+
+        // 设置响应头
+        response.setContentType("application/zip");
+        response.setHeader("Content-Disposition", "attachment; filename=data.zip");
+
+        // 输出 ZIP 文件流
+        try (ServletOutputStream outputStream = response.getOutputStream();
+             FileInputStream fileInputStream = new FileInputStream(zipFilePath)) {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+            outputStream.flush();
+        }
+
+        // 删除临时文件和目录
+        deleteDirectory(directory);
+    }
+
+    private static void createXlsxFile(List<Map<String, Object>> data, String filePath) throws IOException {
+        try (Workbook workbook = new XSSFWorkbook();
+             FileOutputStream fileOut = new FileOutputStream(filePath)) {
+            Sheet sheet = workbook.createSheet("Data");
+
+            // 创建表头
+            Row headerRow = sheet.createRow(0);
+            Set<String> headers = new LinkedHashSet<>();
+            for (Map<String, Object> rowData : data) {
+                headers.addAll(rowData.keySet());
+            }
+            int columnIndex = 0;
+            for (String header : headers) {
+                Cell cell = headerRow.createCell(columnIndex++);
+                cell.setCellValue(header);
+            }
+
+            // 填充数据
+            for (int i = 0; i < data.size(); i++) {
+                Row row = sheet.createRow(i + 1);
+                columnIndex = 0;
+                for (String header : headers) {
+                    Cell cell = row.createCell(columnIndex++);
+                    Object value = data.get(i).get(header);
+                    if (value != null) {
+                        cell.setCellValue(value.toString());
+                    }
+                }
+            }
+
+            // 自动调整列宽
+            for (int i = 0; i < headers.size(); i++) {
+                sheet.autoSizeColumn(i);
+            }
+
+            workbook.write(fileOut);
+        }
+    }
+
+    private static void createCsvFile(List<Map<String, Object>> data, String filePath) throws IOException {
+        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
+            // 获取表头
+            Set<String> headers = new LinkedHashSet<>();
+            for (Map<String, Object> rowData : data) {
+                headers.addAll(rowData.keySet());
+            }
+
+            // 写入表头
+            writer.write(String.join(",", headers));
+            writer.newLine();
+
+            // 写入数据
+            for (Map<String, Object> rowData : data) {
+                List<String> values = new ArrayList<>();
+                for (String header : headers) {
+                    Object value = rowData.get(header);
+                    values.add(value != null ? value.toString() : "");
+                }
+                writer.write(String.join(",", values));
+                writer.newLine();
+            }
+        }
+    }
+
+    private static void zipDirectory(File directory, String zipFilePath) throws IOException {
+        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
+            File[] files = directory.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    if (!file.isDirectory()) {
+                        try (FileInputStream fileIn = new FileInputStream(file)) {
+                            ZipEntry zipEntry = new ZipEntry(file.getName());
+                            zipOut.putNextEntry(zipEntry);
+
+                            byte[] bytes = new byte[1024];
+                            int length;
+                            while ((length = fileIn.read(bytes)) >= 0) {
+                                zipOut.write(bytes, 0, length);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean deleteDirectory(File directory) {
+        if (directory.exists()) {
+            File[] files = directory.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    if (file.isDirectory()) {
+                        deleteDirectory(file);
+                    } else {
+                        file.delete();
+                    }
+                }
+            }
+        }
+        return directory.delete();
+    }
+}