浏览代码

1、添加查询单个项目所有阶段信息的接口
2、添加查询单个阶段中所有项目的接口

ywf 1 月之前
父节点
当前提交
c38237cad2

+ 41 - 0
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/controller/ProjectController.java

@@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.FileInputStream;
 import java.nio.file.Path;
@@ -107,6 +108,21 @@ public class ProjectController extends BaseController {
         }
     }
 
+    /**
+     * 根据当前生命周期阶段查询项目
+     */
+    @PostMapping("/cycle/projects")
+    public R<Map<String, Object>> GetCurrentCycleProjects(@RequestBody ProjectCycleProjectVo projectCycleProjectVo) {
+        try {
+            Map<String, Object> projects = projectService.getCurrentCycleProjects(projectCycleProjectVo);
+            return R.ok(projects);
+        } catch (IllegalArgumentException e) {
+            return R.fail(501, e.getMessage());
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
+
 
     /**
      * 获取项目概览
@@ -130,6 +146,31 @@ public class ProjectController extends BaseController {
         return R.ok(projectCycleRes);
     }
 
+    /**
+     * 导出项目全生命周期SHP
+     */
+    @GetMapping("/cycle/{projectId}/shp")
+    public void ExportCycleShp(@PathVariable String projectId, HttpServletResponse response) {
+        projectService.exportCycleShp(projectId, response);
+    }
+
+    /**
+     * 批量导出项目全生命周期SHP
+     */
+    @PostMapping("/cycle/shp")
+    public void ExportCycleShp(@RequestBody IdsVo idsVo, HttpServletResponse response) {
+        projectService.exportCycleShp(idsVo.getIds(), response);
+    }
+
+    /**
+     * 批量导出当前生命周期阶段SHP
+     */
+    @PostMapping("/cycle/current/shp")
+    public void ExportCurrentCycleShp(@RequestBody ProjectCycleCurrentShpVo projectCycleCurrentShpVo,
+                                      HttpServletResponse response) {
+        projectService.exportCurrentCycleShp(projectCycleCurrentShpVo, response);
+    }
+
     /**
      * 更新项目
      *

+ 29 - 0
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/domain/vo/ProjectCycleCurrentShpVo.java

@@ -0,0 +1,29 @@
+package com.siwei.apply.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 当前生命周期阶段SHP导出参数
+ */
+@Data
+public class ProjectCycleCurrentShpVo {
+    /**
+     * 阶段业务表名,如 t_ydysyxz、t_tdgy
+     */
+    private String nodeTableName;
+
+    /**
+     * 勾选的当前阶段地块行
+     */
+    private List<Item> rows;
+
+    @Data
+    public static class Item {
+        private String projectId;
+        private String nodeId;
+        private String geomNodeId;
+        private String geomId;
+    }
+}

+ 21 - 0
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/domain/vo/ProjectCycleProjectVo.java

@@ -0,0 +1,21 @@
+package com.siwei.apply.domain.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 按生命周期阶段查询项目参数
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectCycleProjectVo extends ProjectFilterVo {
+    /**
+     * 阶段业务表名,如 t_ydysyxz、t_tdgy
+     */
+    private String nodeTableName;
+
+    /**
+     * 前端阶段类型,如 ydysyxz、tdgy
+     */
+    private String worktype;
+}

+ 2 - 0
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/mapper/NodeLandMapper.java

@@ -21,6 +21,8 @@ public interface NodeLandMapper {
      */
     Map<String, String> selectGeomByNodeId(String nodeId);
 
+    Map<String, String> selectUnionGeomByNodeId(@Param("nodeId") String nodeId);
+
 
     Integer selectCountGeomByNodeId(@Param("nodeIdList") List<String> nodeIdList);
 

+ 6 - 1
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/mapper/ProjectMapper.java

@@ -30,6 +30,11 @@ public interface ProjectMapper {
      */
     List<Project> getList(ProjectFilterVo projectFilterVo);
 
+    /**
+     * 获取不分页项目列表
+     */
+    List<Project> getListWithoutPage(ProjectFilterVo projectFilterVo);
+
     /**
      * 获取项目总数
      *
@@ -84,4 +89,4 @@ public interface ProjectMapper {
 
 
 
-}
+}

+ 1 - 0
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/mapper/ProjectWorkflowMapper.java

@@ -75,6 +75,7 @@ public interface ProjectWorkflowMapper {
 
     Map<String,Object> getCurrentNode(@Param("id") String id, @Param("projectId") String projectId, @Param("tableName") String tableName);
 
+    List<Map<String,String>> selectTableColumnComments(@Param("tableName") String tableName);
 
 
 }

+ 11 - 1
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/service/ProjectService.java

@@ -6,11 +6,14 @@ import com.siwei.apply.domain.ProjectWorkflow;
 import com.siwei.apply.domain.res.ProjectCycleRes;
 import com.siwei.apply.domain.res.ProjectOverviewRes;
 import com.siwei.apply.domain.vo.NodeVo;
+import com.siwei.apply.domain.vo.ProjectCycleCurrentShpVo;
+import com.siwei.apply.domain.vo.ProjectCycleProjectVo;
 import com.siwei.apply.domain.vo.ProjectFilterVo;
 import com.siwei.apply.domain.vo.ProjectUpdateVo;
 import com.siwei.apply.domain.vo.ProjectVo;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
@@ -45,6 +48,8 @@ public interface ProjectService {
 
     Map<String, Object> getListAndGeom(ProjectFilterVo projectFilterVo);
 
+    Map<String, Object> getCurrentCycleProjects(ProjectCycleProjectVo projectCycleProjectVo);
+
     /**
      * 更新项目
      *
@@ -70,6 +75,12 @@ public interface ProjectService {
      */
     List<ProjectCycleRes> getCycle(String projectId);
 
+    void exportCycleShp(String projectId, HttpServletResponse response);
+
+    void exportCycleShp(List<String> projectIds, HttpServletResponse response);
+
+    void exportCurrentCycleShp(ProjectCycleCurrentShpVo projectCycleCurrentShpVo, HttpServletResponse response);
+
     void countOnChinaNum(String projectId);
 
     Map<String, Object> getListSearch(ProjectFilterVo projectFilterVo);
@@ -86,4 +97,3 @@ public interface ProjectService {
     void testData() ;
 
 }
-

+ 835 - 2
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/service/impl/ProjectImpl.java

@@ -6,6 +6,8 @@ import com.siwei.apply.common.Constant;
 import com.siwei.apply.domain.*;
 import com.siwei.apply.domain.res.*;
 import com.siwei.apply.domain.vo.NodeVo;
+import com.siwei.apply.domain.vo.ProjectCycleCurrentShpVo;
+import com.siwei.apply.domain.vo.ProjectCycleProjectVo;
 import com.siwei.apply.domain.vo.ProjectFilterVo;
 import com.siwei.apply.domain.vo.ProjectUpdateVo;
 import com.siwei.apply.domain.vo.ProjectVo;
@@ -14,7 +16,22 @@ import com.siwei.apply.mapper.*;
 import com.siwei.apply.service.NodeAttachmentService;
 import com.siwei.apply.service.NodeLandService;
 import com.siwei.apply.service.ProjectService;
+import com.siwei.apply.utils.ServiceFileUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.ShapefileDataStoreFactory;
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
+import org.geotools.referencing.CRS;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.io.WKTReader;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.ArrayUtils;
@@ -23,8 +40,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -43,6 +66,36 @@ import static com.siwei.apply.common.Common.UserId;
 @Slf4j
 @Service
 public class ProjectImpl implements ProjectService {
+    private static final Map<String, String> CYCLE_TABLE_WORKTYPE_MAP;
+    private static final Map<String, String> CYCLE_RESOURCE_TABLE_MAP;
+
+    static {
+        Map<String, String> tableWorktypeMap = new HashMap<>();
+        tableWorktypeMap.put("t_ydysyxz", "ydysyxz");
+        tableWorktypeMap.put("t_ydbp", "ydbp");
+        tableWorktypeMap.put("t_ydbp_data", "ydbpData");
+        tableWorktypeMap.put("t_tjyydhx", "tjyydhx");
+        tableWorktypeMap.put("t_tdgy", "tdgy");
+        tableWorktypeMap.put("t_gyjsydscdj", "gyjsydscdj");
+        tableWorktypeMap.put("t_jsydghxk", "jsydghxk");
+        tableWorktypeMap.put("t_jsgcghxk", "jsgcghxk");
+        tableWorktypeMap.put("t_tdhyhs", "tdhyhs");
+        tableWorktypeMap.put("t_gyjsydjfwscdj", "gyjsydjfwscdj");
+        CYCLE_TABLE_WORKTYPE_MAP = Collections.unmodifiableMap(tableWorktypeMap);
+
+        Map<String, String> resourceTableMap = new HashMap<>();
+        resourceTableMap.put("1", "t_ydysyxz");
+        resourceTableMap.put("2", "t_ydbp");
+        resourceTableMap.put("3", "t_tjyydhx");
+        resourceTableMap.put("4", "t_tdgy");
+        resourceTableMap.put("5", "t_gyjsydscdj");
+        resourceTableMap.put("6", "t_jsydghxk");
+        resourceTableMap.put("7", "t_jsgcghxk");
+        resourceTableMap.put("8", "t_tdhyhs");
+        resourceTableMap.put("9", "t_gyjsydjfwscdj");
+        CYCLE_RESOURCE_TABLE_MAP = Collections.unmodifiableMap(resourceTableMap);
+    }
+
     @Autowired
     private ProjectMapper projectMapper;
     @Autowired
@@ -135,6 +188,51 @@ public class ProjectImpl implements ProjectService {
         return map;
     }
 
+    @Override
+    public Map<String, Object> getCurrentCycleProjects(ProjectCycleProjectVo projectCycleProjectVo) {
+        if (projectCycleProjectVo == null || StringUtils.isBlank(projectCycleProjectVo.getNodeTableName())) {
+            throw new IllegalArgumentException("阶段表名不能为空");
+        }
+
+        projectCycleProjectVo.validatePageParams();
+        String nodeTableName = projectCycleProjectVo.getNodeTableName().trim();
+        String worktype = CYCLE_TABLE_WORKTYPE_MAP.get(nodeTableName);
+        if (StringUtils.isBlank(worktype)) {
+            throw new IllegalArgumentException("不支持的阶段表名:" + nodeTableName);
+        }
+        projectCycleProjectVo.setNodeTableName(nodeTableName);
+        projectCycleProjectVo.setWorktype(worktype);
+        projectCycleProjectVo.setIsOnchain(true);
+
+        List<Project> candidates = projectMapper.getListWithoutPage(projectCycleProjectVo);
+        List<Map<String, Object>> rows = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(candidates)) {
+            for (Project project : candidates) {
+                ProjectWorkflow currentProjectWorkflow = getCurrentNode(project.getId());
+                if (currentProjectWorkflow == null
+                        || !nodeTableName.equals(currentProjectWorkflow.getNodeTableName())) {
+                    continue;
+                }
+                Map<String, Object> nodeData = projectWorkflowMapper.getCurrentNode(
+                        currentProjectWorkflow.getNodeId(),
+                        project.getId(),
+                        currentProjectWorkflow.getNodeTableName());
+                rows.addAll(buildCycleProjectRows(project, currentProjectWorkflow, nodeData, worktype));
+            }
+        }
+
+        int count = rows.size();
+        int fromIndex = Math.min(projectCycleProjectVo.getOffset(), count);
+        int toIndex = Math.min(fromIndex + projectCycleProjectVo.getPageSize(), count);
+
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("projects", rows.subList(fromIndex, toIndex));
+        map.put("count", count);
+        map.put("nodeTableName", nodeTableName);
+        map.put("worktype", worktype);
+        return map;
+    }
+
     @Override
     public void update(ProjectUpdateVo projectUpdateVo) {
         projectMapper.update(projectUpdateVo);
@@ -322,6 +420,716 @@ public class ProjectImpl implements ProjectService {
         return workflows;
     }
 
+    @Override
+    public void exportCycleShp(String projectId, HttpServletResponse response) {
+        exportCycleShp(Collections.singletonList(projectId), response);
+    }
+
+    @Override
+    public void exportCycleShp(List<String> projectIds, HttpServletResponse response) {
+        File tempDir = null;
+        try {
+            if (CollectionUtils.isEmpty(projectIds)) {
+                throw new RuntimeException("项目ID列表不能为空");
+            }
+
+            List<CycleShpRow> rows = new ArrayList<>();
+            List<String> projectNames = new ArrayList<>();
+            for (String projectId : projectIds) {
+                if (StringUtils.isBlank(projectId)) {
+                    continue;
+                }
+                Project project = projectMapper.get(projectId);
+                if (Objects.isNull(project)) {
+                    log.warn("批量导出项目全生命周期SHP时项目不存在,projectId={}", projectId);
+                    continue;
+                }
+
+                List<ProjectCycleExportNode> exportNodes = buildCycleExportNodes(project);
+                if (CollectionUtils.isEmpty(exportNodes)) {
+                    log.warn("批量导出项目全生命周期SHP时项目无已上链生命周期数据,projectId={}", projectId);
+                    continue;
+                }
+
+                List<CycleShpRow> projectRows = buildCycleShpRows(project, exportNodes);
+                if (CollectionUtils.isNotEmpty(projectRows)) {
+                    rows.addAll(projectRows);
+                    projectNames.add(project.getName());
+                }
+            }
+            if (CollectionUtils.isEmpty(rows)) {
+                throw new RuntimeException("所选项目无可导出的地块图形");
+            }
+
+            String shpName = "project_cycle_" + System.currentTimeMillis();
+            tempDir = Files.createTempDirectory("project-cycle-shp-").toFile();
+            File shpFile = new File(tempDir, shpName + ".shp");
+            writeCycleShapefile(shpFile, rows);
+
+            File cpgFile = new File(tempDir, shpName + ".cpg");
+            try (PrintWriter writer = new PrintWriter(cpgFile, "UTF-8")) {
+                writer.print("UTF-8");
+            }
+
+            File zipFile = new File(tempDir, shpName + ".zip");
+            ServiceFileUtil.zipFiles(Arrays.asList(
+                    new File(tempDir, shpName + ".shp"),
+                    new File(tempDir, shpName + ".shx"),
+                    new File(tempDir, shpName + ".dbf"),
+                    new File(tempDir, shpName + ".prj"),
+                    cpgFile
+            ), zipFile);
+
+            String downloadName = projectNames.size() == 1
+                    ? safeFileName(projectNames.get(0)) + "_全生命周期.zip"
+                    : "项目全生命周期_" + projectNames.size() + "个项目.zip";
+            String encodedName = URLEncoder.encode(downloadName, StandardCharsets.UTF_8.name())
+                    .replaceAll("\\+", "%20");
+            response.reset();
+            response.setContentType("application/zip");
+            response.setCharacterEncoding("utf-8");
+            response.setHeader("Content-Disposition", "attachment;filename=" + encodedName
+                    + ";filename*=UTF-8''" + encodedName);
+            response.setContentLengthLong(zipFile.length());
+
+            try (FileInputStream fis = new FileInputStream(zipFile)) {
+                byte[] buffer = new byte[4096];
+                int len;
+                while ((len = fis.read(buffer)) > 0) {
+                    response.getOutputStream().write(buffer, 0, len);
+                }
+            }
+            response.getOutputStream().flush();
+        } catch (Exception e) {
+            log.error("导出项目全生命周期SHP失败,projectIds={}", projectIds, e);
+            throw new RuntimeException("导出项目全生命周期SHP失败: " + e.getMessage());
+        } finally {
+            if (tempDir != null && tempDir.exists()) {
+                try {
+                    FileUtils.deleteDirectory(tempDir);
+                } catch (IOException e) {
+                    log.warn("清理生命周期SHP临时目录失败: {}", tempDir.getAbsolutePath(), e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void exportCurrentCycleShp(ProjectCycleCurrentShpVo projectCycleCurrentShpVo, HttpServletResponse response) {
+        File tempDir = null;
+        try {
+            if (projectCycleCurrentShpVo == null || StringUtils.isBlank(projectCycleCurrentShpVo.getNodeTableName())) {
+                throw new IllegalArgumentException("阶段表名不能为空");
+            }
+            String nodeTableName = projectCycleCurrentShpVo.getNodeTableName().trim();
+            if (!CYCLE_TABLE_WORKTYPE_MAP.containsKey(nodeTableName)) {
+                throw new IllegalArgumentException("不支持的阶段表名:" + nodeTableName);
+            }
+            if (CollectionUtils.isEmpty(projectCycleCurrentShpVo.getRows())) {
+                throw new IllegalArgumentException("请选择要导出的当前阶段数据");
+            }
+
+            Map<String, String> currentFieldMap = buildCurrentCycleFieldMap(nodeTableName);
+            List<CycleShpRow> rows = new ArrayList<>();
+            Set<String> projectNames = new LinkedHashSet<>();
+            for (ProjectCycleCurrentShpVo.Item item : projectCycleCurrentShpVo.getRows()) {
+                if (item == null || StringUtils.isBlank(item.getProjectId())) {
+                    continue;
+                }
+                Project project = projectMapper.get(item.getProjectId());
+                if (project == null) {
+                    continue;
+                }
+                ProjectWorkflow workflow = findExportWorkflow(item, nodeTableName);
+                if (workflow == null) {
+                    continue;
+                }
+                Map<String, Object> nodeData = projectWorkflowMapper.getCurrentNode(
+                        workflow.getNodeId(), project.getId(), nodeTableName);
+                if (nodeData == null) {
+                    continue;
+                }
+                List<Map<String, String>> geomDetails = getSelectedGeomDetails(project.getId(), nodeTableName, workflow, item);
+                for (Map<String, String> geomDetail : geomDetails) {
+                    String geom = geomDetail.get("geom");
+                    if (StringUtils.isBlank(geom)) {
+                        continue;
+                    }
+                    CycleShpRow row = new CycleShpRow();
+                    row.geom = geom;
+                    row.attributes.put("id", project.getId());
+                    row.attributes.put("xmmc", project.getName());
+                    row.attributes.put("xmbm", project.getCode());
+                    row.attributes.put("jsdw", project.getCompany());
+                    row.attributes.put("dkid", geomDetail.get("id"));
+                    for (Map.Entry<String, Object> entry : nodeData.entrySet()) {
+                        String columnName = entry.getKey();
+                        if (shouldSkipExportColumn(columnName) || entry.getValue() == null) {
+                            continue;
+                        }
+                        String fieldName = currentFieldMap.get(columnName);
+                        if (StringUtils.isNotBlank(fieldName)) {
+                            row.attributes.put(fieldName, entry.getValue());
+                        }
+                    }
+                    rows.add(row);
+                    projectNames.add(project.getName());
+                }
+            }
+
+            if (CollectionUtils.isEmpty(rows)) {
+                throw new RuntimeException("所选当前阶段数据无可导出的地块图形");
+            }
+
+            String shpName = "project_cycle_current_" + System.currentTimeMillis();
+            tempDir = Files.createTempDirectory("project-cycle-current-shp-").toFile();
+            File shpFile = new File(tempDir, shpName + ".shp");
+            writeCycleShapefile(shpFile, rows);
+
+            File cpgFile = new File(tempDir, shpName + ".cpg");
+            try (PrintWriter writer = new PrintWriter(cpgFile, "UTF-8")) {
+                writer.print("UTF-8");
+            }
+
+            File zipFile = new File(tempDir, shpName + ".zip");
+            ServiceFileUtil.zipFiles(Arrays.asList(
+                    new File(tempDir, shpName + ".shp"),
+                    new File(tempDir, shpName + ".shx"),
+                    new File(tempDir, shpName + ".dbf"),
+                    new File(tempDir, shpName + ".prj"),
+                    cpgFile
+            ), zipFile);
+
+            String downloadName = projectNames.size() == 1
+                    ? safeFileName(projectNames.iterator().next()) + "_当前阶段.zip"
+                    : "当前阶段_" + projectNames.size() + "个项目.zip";
+            writeZipToResponse(zipFile, downloadName, response);
+        } catch (Exception e) {
+            log.error("导出当前生命周期阶段SHP失败,param={}", projectCycleCurrentShpVo, e);
+            throw new RuntimeException("导出当前生命周期阶段SHP失败: " + e.getMessage());
+        } finally {
+            if (tempDir != null && tempDir.exists()) {
+                try {
+                    FileUtils.deleteDirectory(tempDir);
+                } catch (IOException e) {
+                    log.warn("清理当前阶段SHP临时目录失败: {}", tempDir.getAbsolutePath(), e);
+                }
+            }
+        }
+    }
+
+    private List<ProjectCycleExportNode> buildCycleExportNodes(Project project) {
+        Integer projectType = project.getProjectType();
+        List<ProjectCycleRes> workflows = workflowMapper.selectByProjectTypeOrderByIndex(projectType);
+        List<String> allowTables = workflows.stream().map(ProjectCycleRes::getTableName).collect(Collectors.toList());
+        Map<String, Integer> levelMap = new HashMap<>();
+        for (int i = 0; i < workflows.size(); i++) {
+            levelMap.put(workflows.get(i).getId(), i + 1);
+        }
+
+        List<ProjectWorkflowRes> projectWorkflows = projectWorkflowMapper.selectCycleByProjectId(project.getId());
+        List<ProjectCycleExportNode> exportNodes = new ArrayList<>();
+        for (ProjectWorkflowRes workflowRes : projectWorkflows) {
+            String tableName = workflowRes.getNodeTableName();
+            if (!allowTables.contains(tableName)) {
+                continue;
+            }
+            Boolean hasOnchain = projectWorkflowMapper.isOnchain(workflowRes.getNodeId(), project.getId(), tableName);
+            if (!Boolean.TRUE.equals(hasOnchain)) {
+                continue;
+            }
+
+            Map<String, Object> currentNode = projectWorkflowMapper.getCurrentNode(workflowRes.getNodeId(),
+                    project.getId(), tableName);
+            if (Objects.isNull(currentNode)) {
+                continue;
+            }
+
+            Map<String, String> geomInfo = nodeLandMapper.selectUnionGeomByNodeId(workflowRes.getNodeId());
+            if ((geomInfo == null || StringUtils.isBlank(geomInfo.get("geom")))
+                    && shouldUseGyjsydscdjGeom(tableName)) {
+                geomInfo = nodeLandMapper.selectUnionGeomByNodeId(findProjectNodeId(project.getId(), "t_gyjsydscdj"));
+            }
+            if (geomInfo == null || StringUtils.isBlank(geomInfo.get("geom"))) {
+                continue;
+            }
+
+            int level = levelMap.getOrDefault(workflowRes.getWorkflowId(), exportNodes.size() + 1);
+            ProjectCycleExportNode node = new ProjectCycleExportNode();
+            node.level = level;
+            node.tableName = tableName;
+            node.node = currentNode;
+            node.geom = geomInfo.get("geom");
+            node.geomKey = StringUtils.defaultIfBlank(geomInfo.get("geomDbId"), normalizeGeomKey(node.geom));
+            exportNodes.add(node);
+        }
+        exportNodes.sort(Comparator.comparingInt(o -> o.level));
+        return exportNodes;
+    }
+
+    private ProjectWorkflow findExportWorkflow(ProjectCycleCurrentShpVo.Item item, String nodeTableName) {
+        if (StringUtils.isNotBlank(item.getNodeId())) {
+            List<ProjectWorkflow> workflows = projectWorkflowMapper.selectByNodeId(item.getNodeId(), item.getProjectId());
+            if (CollectionUtils.isNotEmpty(workflows)) {
+                for (ProjectWorkflow workflow : workflows) {
+                    if (nodeTableName.equals(workflow.getNodeTableName())) {
+                        return workflow;
+                    }
+                }
+            }
+        }
+        List<ProjectWorkflow> workflows = projectWorkflowMapper.selectByProjectIdAndNodeTableName(
+                item.getProjectId(), nodeTableName);
+        return CollectionUtils.isEmpty(workflows) ? null : workflows.get(0);
+    }
+
+    private List<Map<String, String>> getSelectedGeomDetails(String projectId, String nodeTableName,
+                                                             ProjectWorkflow workflow,
+                                                             ProjectCycleCurrentShpVo.Item item) {
+        String geomNodeId = StringUtils.defaultIfBlank(item.getGeomNodeId(), workflow.getNodeId());
+        List<Map<String, String>> details = copyGeomDetails(geomNodeId,
+                nodeLandMapper.selectGeomDbDetailsByNodeId(geomNodeId, item.getGeomId()));
+        if (CollectionUtils.isEmpty(details) && shouldUseGyjsydscdjGeom(nodeTableName)) {
+            String fallbackNodeId = findProjectNodeId(projectId, "t_gyjsydscdj");
+            if (StringUtils.isNotBlank(fallbackNodeId)) {
+                details = copyGeomDetails(fallbackNodeId,
+                        nodeLandMapper.selectGeomDbDetailsByNodeId(fallbackNodeId, item.getGeomId()));
+            }
+        }
+        return details;
+    }
+
+    private List<Map<String, String>> getCycleProjectGeomDetails(String projectId, String nodeTableName,
+                                                                 ProjectWorkflow workflow) {
+        List<Map<String, String>> details = copyGeomDetails(workflow.getNodeId(),
+                nodeLandMapper.selectGeomDbDetailsByNodeId(workflow.getNodeId(), null));
+        if (CollectionUtils.isEmpty(details) && shouldUseGyjsydscdjGeom(nodeTableName)) {
+            String fallbackNodeId = findProjectNodeId(projectId, "t_gyjsydscdj");
+            if (StringUtils.isNotBlank(fallbackNodeId)) {
+                details = copyGeomDetails(fallbackNodeId,
+                        nodeLandMapper.selectGeomDbDetailsByNodeId(fallbackNodeId, null));
+            }
+        }
+        return details;
+    }
+
+    private List<Map<String, String>> copyGeomDetails(String geomNodeId, List<Map<String, String>> details) {
+        if (CollectionUtils.isEmpty(details)) {
+            return Collections.emptyList();
+        }
+        List<Map<String, String>> result = new ArrayList<>();
+        for (Map<String, String> detail : details) {
+            Map<String, String> copy = new LinkedHashMap<>(detail);
+            copy.put("geomNodeId", geomNodeId);
+            result.add(copy);
+        }
+        return result;
+    }
+
+    private List<CycleShpRow> buildCycleShpRows(Project project, List<ProjectCycleExportNode> exportNodes) {
+        List<CycleShpRow> rows = new ArrayList<>();
+        Map<String, CycleShpRow> rowMap = new LinkedHashMap<>();
+        Map<String, Map<String, String>> tableColumnMap = new HashMap<>();
+        Map<String, Map<String, String>> cycleFieldMap = buildCycleFieldMap(project.getProjectType(), tableColumnMap);
+
+        for (ProjectCycleExportNode node : exportNodes) {
+            CycleShpRow row = rowMap.computeIfAbsent(node.geomKey, key -> {
+                CycleShpRow newRow = new CycleShpRow();
+                newRow.geom = node.geom;
+                newRow.attributes.put("id", project.getId());
+                newRow.attributes.put("xmmc", project.getName());
+                newRow.attributes.put("xmbm", project.getCode());
+                newRow.attributes.put("jsdw", project.getCompany());
+                for (Map<String, String> fieldMap : cycleFieldMap.values()) {
+                    for (String fieldName : fieldMap.values()) {
+                        newRow.attributes.put(fieldName, "");
+                    }
+                }
+                rows.add(newRow);
+                return newRow;
+            });
+
+            Map<String, String> fieldMap = cycleFieldMap.getOrDefault(node.tableName, new LinkedHashMap<>());
+            for (Map.Entry<String, Object> entry : node.node.entrySet()) {
+                String columnName = entry.getKey();
+                if (shouldSkipExportColumn(columnName) || entry.getValue() == null) {
+                    continue;
+                }
+                String fieldName = fieldMap.get(columnName);
+                if (StringUtils.isNotBlank(fieldName)) {
+                    row.attributes.put(fieldName, entry.getValue());
+                }
+            }
+        }
+        return rows;
+    }
+
+    private Map<String, String> buildCurrentCycleFieldMap(String tableName) {
+        Map<String, String> columnCommentMap = getColumnCommentMap(tableName);
+        Map<String, String> fieldMap = new LinkedHashMap<>();
+        Set<String> usedFieldNames = new HashSet<>(Arrays.asList("id", "xmmc", "xmbm", "jsdw", "dkid"));
+        for (Map.Entry<String, String> entry : columnCommentMap.entrySet()) {
+            String columnName = entry.getKey();
+            if (shouldSkipExportColumn(columnName)) {
+                continue;
+            }
+            fieldMap.put(columnName, buildShpFieldName("D", entry.getValue(), columnName, usedFieldNames));
+        }
+        return fieldMap;
+    }
+
+    private Map<String, Map<String, String>> buildCycleFieldMap(Integer projectType, Map<String, Map<String, String>> tableColumnMap) {
+        Map<String, Map<String, String>> result = new LinkedHashMap<>();
+        Set<String> usedFieldNames = new HashSet<>();
+        List<ProjectCycleRes> workflows = workflowMapper.selectByProjectTypeOrderByIndex(projectType);
+        for (int i = 0; i < workflows.size(); i++) {
+            ProjectCycleRes workflow = workflows.get(i);
+            String tableName = workflow.getTableName();
+            Map<String, String> columnCommentMap = tableColumnMap.computeIfAbsent(tableName, this::getColumnCommentMap);
+            Map<String, String> fieldMap = new LinkedHashMap<>();
+            for (Map.Entry<String, String> entry : columnCommentMap.entrySet()) {
+                String columnName = entry.getKey();
+                if (shouldSkipExportColumn(columnName)) {
+                    continue;
+                }
+                String fieldName = buildShpFieldName("L" + (i + 1), entry.getValue(), columnName, usedFieldNames);
+                fieldMap.put(columnName, fieldName);
+            }
+            result.put(tableName, fieldMap);
+        }
+        return result;
+    }
+
+    private Map<String, String> getColumnCommentMap(String tableName) {
+        List<Map<String, String>> columnComments = projectWorkflowMapper.selectTableColumnComments(tableName);
+        Map<String, String> result = new HashMap<>();
+        if (CollectionUtils.isNotEmpty(columnComments)) {
+            for (Map<String, String> columnComment : columnComments) {
+                result.put(columnComment.get("columnName"), columnComment.get("columnComment"));
+            }
+        }
+        return result;
+    }
+
+    private void writeCycleShapefile(File shpFile, List<CycleShpRow> rows) throws Exception {
+        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
+        typeBuilder.setName("project_cycle");
+        CoordinateReferenceSystem crs = CRS.decode("EPSG:4490");
+        typeBuilder.setCRS(crs);
+        typeBuilder.add("the_geom", MultiPolygon.class);
+
+        List<String> fields = rows.stream()
+                .flatMap(row -> row.attributes.keySet().stream())
+                .distinct()
+                .sorted(this::compareCycleField)
+                .collect(Collectors.toList());
+        for (String field : fields) {
+            typeBuilder.length(254).add(field, String.class);
+        }
+        SimpleFeatureType featureType = typeBuilder.buildFeatureType();
+
+        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
+        Map<String, Serializable> params = new HashMap<>();
+        params.put("url", shpFile.toURI().toURL());
+        params.put("create spatial index", Boolean.TRUE);
+        ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
+        dataStore.setCharset(Charset.forName("UTF-8"));
+        dataStore.createSchema(featureType);
+
+        WKTReader wktReader = new WKTReader();
+        Transaction transaction = new DefaultTransaction("create");
+        try (FeatureWriter<SimpleFeatureType, SimpleFeature> writer = dataStore.getFeatureWriterAppend(transaction)) {
+            for (CycleShpRow row : rows) {
+                Geometry geometry = parseEwkt(row.geom, wktReader);
+                if (geometry == null) {
+                    continue;
+                }
+                if (geometry instanceof Polygon) {
+                    geometry = geometry.getFactory().createMultiPolygon(new Polygon[]{(Polygon) geometry});
+                }
+
+                SimpleFeature feature = writer.next();
+                feature.setAttribute("the_geom", geometry);
+                for (String field : fields) {
+                    Object value = row.attributes.get(field);
+                    feature.setAttribute(field, value == null ? "" : trimDbfValue(String.valueOf(value)));
+                }
+                writer.write();
+            }
+            transaction.commit();
+        } catch (Exception e) {
+            transaction.rollback();
+            throw e;
+        } finally {
+            transaction.close();
+            dataStore.dispose();
+        }
+    }
+
+    private Geometry parseEwkt(String ewkt, WKTReader reader) {
+        if (StringUtils.isBlank(ewkt)) {
+            return null;
+        }
+        try {
+            String wkt = ewkt.contains(";") ? ewkt.substring(ewkt.indexOf(";") + 1) : ewkt;
+            return reader.read(wkt);
+        } catch (Exception e) {
+            log.warn("生命周期SHP几何解析失败: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    private boolean shouldUseGyjsydscdjGeom(String tableName) {
+        return AloneWorkFlowEnum.NODE_4.getTableName().equalsIgnoreCase(tableName)
+                || AloneWorkFlowEnum.NODE_5.getTableName().equalsIgnoreCase(tableName)
+                || AloneWorkFlowEnum.NODE_6.getTableName().equalsIgnoreCase(tableName)
+                || AloneWorkFlowEnum.NODE_7.getTableName().equalsIgnoreCase(tableName);
+    }
+
+    private String findProjectNodeId(String projectId, String tableName) {
+        List<ProjectWorkflow> workflows = projectWorkflowMapper.selectByProjectIdAndNodeTableName(projectId, tableName);
+        if (CollectionUtils.isEmpty(workflows)) {
+            return null;
+        }
+        return workflows.get(0).getNodeId();
+    }
+
+    private boolean shouldSkipExportColumn(String columnName) {
+        if (StringUtils.isBlank(columnName)) {
+            return true;
+        }
+        String column = columnName.toLowerCase();
+        return Arrays.asList("id", "project_id", "has_onchain", "created_at", "updated_at", "creator_id",
+                "geom", "shape", "shp", "shppath", "file_path", "file_name").contains(column);
+    }
+
+    private String buildShpFieldName(String level, String comment, String columnName, Set<String> usedFieldNames) {
+        String initials = toInitials(StringUtils.defaultIfBlank(comment, columnName));
+        if (StringUtils.isBlank(initials)) {
+            initials = columnName.replaceAll("[^A-Za-z0-9]", "");
+        }
+        if (StringUtils.isBlank(initials)) {
+            initials = "field";
+        }
+        String prefix = level + "_";
+        String base = prefix.toUpperCase(Locale.ROOT) + initials.toLowerCase(Locale.ROOT);
+        if (base.length() > 10) {
+            base = base.substring(0, 10);
+        }
+        String fieldName = base;
+        int index = 1;
+        while (usedFieldNames.contains(fieldName)) {
+            String suffix = String.valueOf(index++);
+            int maxBaseLength = Math.max(1, 10 - suffix.length());
+            fieldName = base.substring(0, Math.min(base.length(), maxBaseLength)) + suffix;
+        }
+        usedFieldNames.add(fieldName);
+        return fieldName;
+    }
+
+    private int compareCycleField(String field1, String field2) {
+        List<String> baseFields = Arrays.asList("id", "xmmc", "xmbm", "jsdw");
+        int baseIndex1 = baseFields.indexOf(field1);
+        int baseIndex2 = baseFields.indexOf(field2);
+        if (baseIndex1 >= 0 || baseIndex2 >= 0) {
+            if (baseIndex1 < 0) {
+                return 1;
+            }
+            if (baseIndex2 < 0) {
+                return -1;
+            }
+            return Integer.compare(baseIndex1, baseIndex2);
+        }
+
+        int level1 = getCycleFieldLevel(field1);
+        int level2 = getCycleFieldLevel(field2);
+        if (level1 != level2) {
+            return Integer.compare(level1, level2);
+        }
+        return field1.compareToIgnoreCase(field2);
+    }
+
+    private int getCycleFieldLevel(String field) {
+        if (StringUtils.isBlank(field) || !field.startsWith("L")) {
+            return Integer.MAX_VALUE;
+        }
+        int end = field.indexOf('_');
+        if (end <= 1) {
+            return Integer.MAX_VALUE;
+        }
+        try {
+            return Integer.parseInt(field.substring(1, end));
+        } catch (NumberFormatException e) {
+            return Integer.MAX_VALUE;
+        }
+    }
+
+    private String toInitials(String text) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < text.length(); i++) {
+            char ch = text.charAt(i);
+            if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
+                sb.append(Character.toLowerCase(ch));
+            } else if (isChinese(ch)) {
+                sb.append(getChineseFirstLetter(ch));
+            }
+        }
+        return sb.toString();
+    }
+
+    private boolean isChinese(char ch) {
+        return Character.UnicodeScript.of(ch) == Character.UnicodeScript.HAN;
+    }
+
+    private char getChineseFirstLetter(char ch) {
+        String letters = "ABCDEFGHJKLMNOPQRSTWXYZ";
+        int[] areas = {45217, 45253, 45761, 46318, 46826, 47010, 47297, 47614, 48119, 49062, 49324, 49896,
+                50371, 50614, 50622, 50906, 51387, 51446, 52218, 52698, 52980, 53689, 54481};
+        try {
+            byte[] bytes = String.valueOf(ch).getBytes("GBK");
+            if (bytes.length < 2) {
+                return Character.toLowerCase(ch);
+            }
+            int code = (bytes[0] & 0xff) * 256 + (bytes[1] & 0xff);
+            for (int i = areas.length - 1; i >= 0; i--) {
+                if (code >= areas[i]) {
+                    return Character.toLowerCase(letters.charAt(i));
+                }
+            }
+        } catch (Exception ignored) {
+        }
+        return 'x';
+    }
+
+    private String normalizeGeomKey(String ewkt) {
+        if (StringUtils.isBlank(ewkt)) {
+            return "";
+        }
+        return ewkt.replaceAll("\\s+", "").toUpperCase(Locale.ROOT);
+    }
+
+    private String trimDbfValue(String value) {
+        if (value == null) {
+            return "";
+        }
+        return value.length() > 80 ? value.substring(0, 80) : value;
+    }
+
+    private String safeFileName(String name) {
+        if (StringUtils.isBlank(name)) {
+            return "项目";
+        }
+        return name.replaceAll("[\\\\/:*?\"<>|]", "_");
+    }
+
+    private void writeZipToResponse(File zipFile, String downloadName, HttpServletResponse response) throws IOException {
+        String encodedName = URLEncoder.encode(downloadName, StandardCharsets.UTF_8.name())
+                .replaceAll("\\+", "%20");
+        response.reset();
+        response.setContentType("application/zip");
+        response.setCharacterEncoding("utf-8");
+        response.setHeader("Content-Disposition", "attachment;filename=" + encodedName
+                + ";filename*=UTF-8''" + encodedName);
+        response.setContentLengthLong(zipFile.length());
+
+        try (FileInputStream fis = new FileInputStream(zipFile)) {
+            byte[] buffer = new byte[4096];
+            int len;
+            while ((len = fis.read(buffer)) > 0) {
+                response.getOutputStream().write(buffer, 0, len);
+            }
+        }
+        response.getOutputStream().flush();
+    }
+
+    private static class ProjectCycleExportNode {
+        private int level;
+        private String tableName;
+        private Map<String, Object> node;
+        private String geom;
+        private String geomKey;
+    }
+
+    private static class CycleShpRow {
+        private String geom;
+        private final Map<String, Object> attributes = new LinkedHashMap<>();
+    }
+
+    private List<Map<String, Object>> buildCycleProjectRows(Project project, ProjectWorkflow workflow,
+                                                            Map<String, Object> nodeData, String worktype) {
+        List<Map<String, String>> geomDetails = getCycleProjectGeomDetails(
+                project.getId(), workflow.getNodeTableName(), workflow);
+        if (CollectionUtils.isEmpty(geomDetails)) {
+            return Collections.singletonList(buildCycleProjectRow(project, workflow, nodeData, worktype, null));
+        }
+        List<Map<String, Object>> rows = new ArrayList<>();
+        for (Map<String, String> geomDetail : geomDetails) {
+            rows.add(buildCycleProjectRow(project, workflow, nodeData, worktype, geomDetail));
+        }
+        return rows;
+    }
+
+    private Map<String, Object> buildCycleProjectRow(Project project, ProjectWorkflow workflow,
+                                                     Map<String, Object> nodeData, String worktype,
+                                                     Map<String, String> geomDetail) {
+        Map<String, Object> row = new LinkedHashMap<>();
+        if (nodeData != null) {
+            for (Map.Entry<String, Object> entry : nodeData.entrySet()) {
+                String key = toCamelCase(entry.getKey());
+                if ("id".equals(key)) {
+                    row.put("businessId", entry.getValue());
+                } else {
+                    row.put(key, entry.getValue());
+                }
+            }
+        }
+
+        row.put("id", project.getId());
+        row.put("projectId", project.getId());
+        row.put("projectName", project.getName());
+        row.put("projectCode", project.getCode());
+        row.put("projectCompany", project.getCompany());
+        row.put("name", project.getName());
+        row.put("code", project.getCode());
+        row.put("company", project.getCompany());
+        row.put("projectType", project.getProjectType());
+        row.put("onChainNum", project.getOnChainNum());
+        row.put("nodeId", workflow.getNodeId());
+        row.put("workflowId", workflow.getWorkflowId());
+        row.put("workflowName", workflow.getWorkflowName());
+        row.put("nodeTableName", workflow.getNodeTableName());
+        row.put("tableName", workflow.getNodeTableName());
+        row.put("worktype", worktype);
+        row.put("sjly", workflow.getNodeTableName());
+        if (geomDetail != null) {
+            row.put("geomId", geomDetail.get("id"));
+            row.put("geomNodeId", geomDetail.get("geomNodeId"));
+            row.put("geom", geomDetail.get("geom"));
+            row.put("geomArea", geomDetail.get("geom_area"));
+            row.put("landRowKey", project.getId() + "_" + workflow.getNodeId() + "_" + geomDetail.get("id"));
+        }
+        return row;
+    }
+
+    private String toCamelCase(String value) {
+        if (StringUtils.isBlank(value) || !value.contains("_")) {
+            return value;
+        }
+        StringBuilder builder = new StringBuilder();
+        boolean upperNext = false;
+        for (char ch : value.toCharArray()) {
+            if (ch == '_') {
+                upperNext = true;
+            } else if (upperNext) {
+                builder.append(Character.toUpperCase(ch));
+                upperNext = false;
+            } else {
+                builder.append(ch);
+            }
+        }
+        return builder.toString();
+    }
+
     /**
      * 1.查询出所有的节点。
      * 2.根据节点表名和项目id,查询出该节点表中该项目的上链数量。
@@ -485,11 +1293,36 @@ public class ProjectImpl implements ProjectService {
         String fileName = fileURL.getPath();
         String configStr = FileUtils.readFileToString(new File(fileName), StandardCharsets.UTF_8);
         JSONArray aa = JSON.parseArray(configStr);
-        configStr = aa.toJSONString();
-        log.info("------------" + configStr);
+        enrichCycleResourceData(aa);
+        log.info("------------" + aa.toJSONString());
         return aa;
     }
 
+    private void enrichCycleResourceData(JSONArray nodes) {
+        if (nodes == null) {
+            return;
+        }
+        for (Object item : nodes) {
+            if (!(item instanceof JSONObject)) {
+                continue;
+            }
+            JSONObject node = (JSONObject) item;
+            String tableName = CYCLE_RESOURCE_TABLE_MAP.get(node.getString("id"));
+            if (StringUtils.isNotBlank(tableName)) {
+                String worktype = CYCLE_TABLE_WORKTYPE_MAP.get(tableName);
+                node.put("source", tableName);
+                node.put("nodeTableName", tableName);
+                node.put("tableName", tableName);
+                node.put("sjly", tableName);
+                node.put("worktype", worktype);
+            }
+            Object children = node.get("children");
+            if (children instanceof JSONArray) {
+                enrichCycleResourceData((JSONArray) children);
+            }
+        }
+    }
+
     @Override
     public void testData() {
         // String nodeId = null;

+ 12 - 0
siwei-modules/siwei-apply/src/main/resources/mapper/NodeLandMapper.xml

@@ -45,6 +45,18 @@
         GROUP BY nl.geom_db_id, tgd.shppath
     </select>
 
+    <select id="selectUnionGeomByNodeId" resultType="map" parameterType="String">
+        SELECT
+            nl.geom_db_id as "geomDbId",
+            public.ST_AsEWKT(public.ST_Union(gd.geom)) as "geom"
+        FROM t_node_land nl
+        LEFT JOIN t_geom_db_details gd ON nl.geom_db_id = gd.upload_id
+        WHERE nl.node_id = #{nodeId}
+          AND gd.geom IS NOT NULL
+        GROUP BY nl.geom_db_id
+        LIMIT 1
+    </select>
+
     <!-- 根据node_id查询地块数量信息 -->
     <select id="selectCountGeomByNodeId" >
         SELECT

+ 36 - 0
siwei-modules/siwei-apply/src/main/resources/mapper/ProjectMapper.xml

@@ -80,6 +80,42 @@
         ORDER BY updated_at DESC
         LIMIT #{pageSize} offset #{offset}
     </select>
+    <select id="getListWithoutPage" parameterType="com.siwei.apply.domain.vo.ProjectFilterVo" resultMap="projectMap">
+        SELECT *
+        FROM t_project
+        <where>
+            <if test="name != null and name != ''">
+                AND name LIKE CONCAT('%', #{name}, '%')
+            </if>
+            <if test="code != null and code != ''">
+                AND code LIKE CONCAT('%', #{code}, '%')
+            </if>
+            <if test="keyWord != null and keyWord != ''">
+                AND (
+                    name    LIKE CONCAT('%', #{keyWord}, '%')
+                    OR company LIKE CONCAT('%', #{keyWord}, '%')
+                )
+            </if>
+            <if test="projectType != null and projectType != 0">
+                AND project_type = #{projectType}
+            </if>
+            <if test="isOnchain != null and isOnchain==true">
+                AND on_chain_num > 0
+            </if>
+            <if test="shape != null and shape != ''">
+                AND EXISTS (
+                    SELECT 1
+                    FROM t_project_workflow pw
+                    JOIN t_node_land nl ON nl.node_id = pw.node_id
+                    JOIN t_geom_db_details gd ON gd.upload_id = nl.geom_db_id
+                    WHERE pw.project_id = t_project.id
+                      AND public.ST_Intersects(gd.geom, public.ST_GeomFromEWKT(#{shape}))
+                )
+            </if>
+
+        </where>
+        ORDER BY updated_at DESC
+    </select>
     <select id="getCount" parameterType="com.siwei.apply.domain.vo.ProjectFilterVo" resultType="int">
         SELECT COUNT(*)
         FROM t_project

+ 18 - 0
siwei-modules/siwei-apply/src/main/resources/mapper/ProjectWorkflowMapper.xml

@@ -96,6 +96,24 @@
         LIMIT 1
     </select>
 
+    <select id="selectTableColumnComments" parameterType="string" resultType="map">
+        SELECT
+            c.column_name AS "columnName",
+            COALESCE(d.description, c.column_name) AS "columnComment"
+        FROM information_schema.columns c
+        LEFT JOIN pg_catalog.pg_class pc
+               ON pc.relname = c.table_name
+        LEFT JOIN pg_catalog.pg_namespace pn
+               ON pn.oid = pc.relnamespace
+              AND pn.nspname = c.table_schema
+        LEFT JOIN pg_catalog.pg_description d
+               ON d.objoid = pc.oid
+              AND d.objsubid = c.ordinal_position
+        WHERE c.table_schema = 'public'
+          AND c.table_name = #{tableName}
+        ORDER BY c.ordinal_position
+    </select>
+
 
 
     <insert id="add" parameterType="com.siwei.apply.domain.ProjectWorkflow">

+ 40 - 4
siwei-modules/siwei-apply/src/main/resources/one_code_index_v1.json

@@ -18,7 +18,11 @@
         "title": "用地预审与选址意见书许可范围",
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_ydysyxz&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
-        "source": "",
+        "source": "t_ydysyxz",
+        "nodeTableName": "t_ydysyxz",
+        "tableName": "t_ydysyxz",
+        "sjly": "t_ydysyxz",
+        "worktype": "ydysyxz",
         "legend": "",
         "favorite": null,
         "disabled": false,
@@ -32,7 +36,11 @@
         "title": "用地报批",
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_ydbp&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
-        "source": null,
+        "source": "t_ydbp",
+        "nodeTableName": "t_ydbp",
+        "tableName": "t_ydbp",
+        "sjly": "t_ydbp",
+        "worktype": "ydbp",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -46,7 +54,11 @@
         "title": "规划条件与用地红线",
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_tjyydhx&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
-        "source": null,
+        "source": "t_tjyydhx",
+        "nodeTableName": "t_tjyydhx",
+        "tableName": "t_tjyydhx",
+        "sjly": "t_tjyydhx",
+        "worktype": "tjyydhx",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -61,6 +73,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_tdgy&bbox=115.84467869332723%2C28.816888197870995%2C115.94519076899752%2C28.919477034878152&width=752&height=768&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_tdgy",
+        "nodeTableName": "t_tdgy",
+        "tableName": "t_tdgy",
+        "sjly": "t_tdgy",
+        "worktype": "tdgy",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -75,6 +91,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_gyjsydscdj&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_gyjsydscdj",
+        "nodeTableName": "t_gyjsydscdj",
+        "tableName": "t_gyjsydscdj",
+        "sjly": "t_gyjsydscdj",
+        "worktype": "gyjsydscdj",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -89,6 +109,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_jsgcghxk&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_jsydghxk",
+        "nodeTableName": "t_jsydghxk",
+        "tableName": "t_jsydghxk",
+        "sjly": "t_jsydghxk",
+        "worktype": "jsydghxk",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -103,6 +127,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_jsydghxk&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_jsgcghxk",
+        "nodeTableName": "t_jsgcghxk",
+        "tableName": "t_jsgcghxk",
+        "sjly": "t_jsgcghxk",
+        "worktype": "jsgcghxk",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -117,6 +145,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_tdhyhs&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_tdhyhs",
+        "nodeTableName": "t_tdhyhs",
+        "tableName": "t_tdhyhs",
+        "sjly": "t_tdhyhs",
+        "worktype": "tdhyhs",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -131,6 +163,10 @@
         "type": "wms",
         "url": "http://192.168.60.220:8090/geoserver/c_one_code/wms?service=WMS&version=1.1.0&request=GetMap&layers=c_one_code%3Ac_one_code_gyjsydjfwscdj&bbox=115.81535941350508%2C28.80663797957737%2C115.95003761461436%2C28.93561402541765&width=768&height=735&srs=EPSG%3A4326&styles=&format=application/openlayers",
         "source": "t_gyjsydjfwscdj",
+        "nodeTableName": "t_gyjsydjfwscdj",
+        "tableName": "t_gyjsydjfwscdj",
+        "sjly": "t_gyjsydjfwscdj",
+        "worktype": "gyjsydjfwscdj",
         "legend": null,
         "favorite": null,
         "disabled": false,
@@ -169,4 +205,4 @@
       }
     ]
   }
-]
+]