Przeglądaj źródła

项目列表管理中添加 文件批量下载。项目详情中的文档管理页面添加文件下载

徐滕 1 miesiąc temu
rodzic
commit
cccb2e01f9

+ 8 - 0
src/main/java/com/jeeplus/modules/ruralprojectrecords/dao/RuralProjectRecordsDao.java

@@ -514,4 +514,12 @@ public interface RuralProjectRecordsDao extends CrudDao<RuralProjectRecords> {
      * @return
      * @return
      */
      */
     List<RuralProjectRecords> getInfoByReportList(@Param("reportNumberList") List<ExportProjectFileInfoInfo> reportNumberList);
     List<RuralProjectRecords> getInfoByReportList(@Param("reportNumberList") List<ExportProjectFileInfoInfo> reportNumberList);
+
+
+    /**
+     * 根据项目id查询项目信息(批量)
+     * @param projectIdList
+     * @return
+     */
+    List<RuralProjectRecords> getInfoByProjectIdList(@Param("projectIdList") List<String> projectIdList);
 }
 }

+ 208 - 20
src/main/java/com/jeeplus/modules/ruralprojectrecords/service/RuralProjectRecordsService.java

@@ -10,7 +10,10 @@ import com.jeeplus.common.config.Global;
 import com.jeeplus.common.oss.OSSClientUtil;
 import com.jeeplus.common.oss.OSSClientUtil;
 import com.jeeplus.common.persistence.Page;
 import com.jeeplus.common.persistence.Page;
 import com.jeeplus.common.service.CrudService;
 import com.jeeplus.common.service.CrudService;
-import com.jeeplus.common.utils.*;
+import com.jeeplus.common.utils.Collections3;
+import com.jeeplus.common.utils.DateUtils;
+import com.jeeplus.common.utils.IdGen;
+import com.jeeplus.common.utils.MenuStatusEnum;
 import com.jeeplus.modules.act.entity.Act;
 import com.jeeplus.modules.act.entity.Act;
 import com.jeeplus.modules.act.service.ActTaskService;
 import com.jeeplus.modules.act.service.ActTaskService;
 import com.jeeplus.modules.act.utils.ProcessDefCache;
 import com.jeeplus.modules.act.utils.ProcessDefCache;
@@ -24,7 +27,6 @@ import com.jeeplus.modules.projectFilingBatch.entity.ProjectFilingBatchImportInf
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectReportDataDao;
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectReportDataDao;
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectReportDataTwoDao;
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectReportDataTwoDao;
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectcontentinfoDao;
 import com.jeeplus.modules.projectcontentinfo.dao.ProjectcontentinfoDao;
-import com.jeeplus.modules.projectcontentinfo.entity.ProjectMaterialDefectRecord;
 import com.jeeplus.modules.projectcontentinfo.entity.ProjectReportData;
 import com.jeeplus.modules.projectcontentinfo.entity.ProjectReportData;
 import com.jeeplus.modules.projectcontentinfo.entity.ProjectReportDataTwo;
 import com.jeeplus.modules.projectcontentinfo.entity.ProjectReportDataTwo;
 import com.jeeplus.modules.projectcontentinfo.entity.Projectcontentinfo;
 import com.jeeplus.modules.projectcontentinfo.entity.Projectcontentinfo;
@@ -99,7 +101,6 @@ import java.net.URLEncoder;
 import java.nio.charset.Charset;
 import java.nio.charset.Charset;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
-import java.time.*;
 import java.util.*;
 import java.util.*;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipFile;
@@ -2686,13 +2687,11 @@ public class RuralProjectRecordsService extends CrudService<RuralProjectRecordsD
 		File resultFile = null;
 		File resultFile = null;
 		File file = null;
 		File file = null;
 		try {
 		try {
-			//遍历项目id并查询对应数据信息
-			for (String projectId: idList) {
-				//获取项目信息
-				RuralProjectRecords records = this.getQueryProjectUsers(projectId);
-				//根据项目id查询项目报告信息
-				//ProjectReportData projectReportData = projectReportDataService.getReportDataByProjectId(records.getId());
-				//projectReportData = projectReportDataService.get(projectReportData.getId());
+			//一次性将被选择到的所有项目信息都查出来,然后进行批量处理,降低处理时间
+			List<RuralProjectRecords> projectList = ruralProjectRecordsDao.getInfoByProjectIdList(idList);
+
+			for (RuralProjectRecords records : projectList) {
+
 				ProjectAccessoryRelationInfo relateInfo = new ProjectAccessoryRelationInfo();
 				ProjectAccessoryRelationInfo relateInfo = new ProjectAccessoryRelationInfo();
 
 
 				//添加项目类型
 				//添加项目类型
@@ -2728,29 +2727,37 @@ public class RuralProjectRecordsService extends CrudService<RuralProjectRecordsD
 				relateInfo.setId(records.getId());
 				relateInfo.setId(records.getId());
 				//查询报告文件、依据性文件、其他文件必填列表以及数据
 				//查询报告文件、依据性文件、其他文件必填列表以及数据
 				List<MainDictDetail> mainDictDetails = this.attachmentTemplateList();
 				List<MainDictDetail> mainDictDetails = this.attachmentTemplateList();
+
 				List<ProjectTemplateInfo> projectTemplateList = null;
 				List<ProjectTemplateInfo> projectTemplateList = null;
-				String downloadPath = path + "/" + records.getProjectName();
+				// 原始拼接逻辑
+				String downloadPath = path + "/"+ records.getProjectReportNumber() + "-" + records.getProjectName();
+				// 最大长度限制
+				int maxLength = 100;
+				// 省略号
+				String ellipsis = "...";
+				// 处理超长字符串
+				if (downloadPath.length() > maxLength) {
+					// 截取前 97 个字符 + 省略号(总长度刚好100)
+					downloadPath = downloadPath.substring(0, maxLength - ellipsis.length()) + ellipsis;
+				}
 				for (MainDictDetail mainDict : mainDictDetails) {
 				for (MainDictDetail mainDict : mainDictDetails) {
 					relateInfo.setAttachType(mainDict.getValue());
 					relateInfo.setAttachType(mainDict.getValue());
 					File dirFile = null;
 					File dirFile = null;
 					switch (mainDict.getValue()) {
 					switch (mainDict.getValue()) {
 						case "11":
 						case "11":
 							dirFile=new File(downloadPath+"/成果文件");
 							dirFile=new File(downloadPath+"/成果文件");
-							projectTemplateList = Lists.newArrayList();
-							projectTemplateList = this.getProjectTemplateList(relateInfo);
-							this.disposeFileList(projectTemplateList,dirFile.getPath());
+							List<WorkClientAttachment> exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+							this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
 							break;
 							break;
 						case "12":
 						case "12":
 							dirFile=new File(downloadPath+"/依据性资料");
 							dirFile=new File(downloadPath+"/依据性资料");
-							projectTemplateList = Lists.newArrayList();
-							projectTemplateList = this.getProjectTemplateList(relateInfo);
-							this.disposeFileList(projectTemplateList,dirFile.getPath());
+							exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+							this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
 							break;
 							break;
 						case "13":
 						case "13":
 							dirFile=new File(downloadPath+"/其他文件");
 							dirFile=new File(downloadPath+"/其他文件");
-							projectTemplateList = Lists.newArrayList();
-							projectTemplateList = this.getProjectTemplateList(relateInfo);
-							this.disposeFileList(projectTemplateList,dirFile.getPath());
+							exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+							this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
 							break;
 							break;
 					}
 					}
 
 
@@ -2775,6 +2782,128 @@ public class RuralProjectRecordsService extends CrudService<RuralProjectRecordsD
 		return file.getPath();
 		return file.getPath();
 	}
 	}
 
 
+
+
+	/**
+	 * 项目相应文件批量下载并压缩
+	 * @param id
+	 */
+	public String exportProjectRecordsFiles(String id) throws Exception {
+		File file = null;
+		if(StringUtils.isNotBlank(id)){
+			RuralProjectRecords records = ruralProjectRecordsDao.get(id);
+
+			File resultFile = null;
+			String path = null;
+			try {
+				if (null != records) {
+					//设置下载的压缩包名(固定字符+时间戳)
+					long timeMillis = System.currentTimeMillis();
+
+					// 最大长度限制
+					int maxLength = 100;
+					// 省略号
+					String ellipsis = "...";
+
+					String projectName = records.getProjectName();
+					// 处理超长字符串
+					if (records.getProjectName().length() > maxLength) {
+						// 截取前 97 个字符 + 省略号(总长度刚好100)
+						projectName = records.getProjectName().substring(0, maxLength - ellipsis.length()) + ellipsis;
+					}
+
+					String fileLocality = records.getProjectReportNumber() + "-" + projectName + "-项目附件_" + timeMillis;
+
+					//判定当前系统
+					if(System.getProperty("os.name").toLowerCase().contains("win")){
+						path = Global.getConfig("remoteServer.winDirectory");
+					}else{
+						path = Global.getConfig("remoteServer.directory");
+					}
+					path = path + "/" + fileLocality;
+					//检查该路径对应的目录是否存在. 如果不存在则创建目录
+					File dir=new File(path);
+					if (!dir.exists()) {
+						dir.mkdirs();
+					}
+
+
+					ProjectAccessoryRelationInfo relateInfo = new ProjectAccessoryRelationInfo();
+
+					//添加项目类型
+					relateInfo.setAttachmentProjectType(records.getProjectType());
+					relateInfo.setAttachmentProjectSort(records.getAttachmentProjectSort());
+
+					String money=records.getSubmitMoney();
+					Integer approvalMoney=null;
+					if(com.jeeplus.common.utils.StringUtils.isBlank(money)){
+						approvalMoney=1;
+					}else{
+						approvalMoney=Integer.parseInt(money);
+					}
+					switch (approvalMoney){
+						case 0:
+							//金额为0
+							relateInfo.setAttachmentProjectApprovalMoney(null);
+							break;
+						case 1:
+							//500w以下金额状态
+							relateInfo.setAttachmentProjectApprovalMoney("1");
+							break;
+						case 2:
+							//500w以上金额状态
+							relateInfo.setAttachmentProjectApprovalMoney("2");
+							break;
+					}
+					//添加报告类型
+					relateInfo.setRequiredStage(1);
+					relateInfo.setId(records.getId());
+					//查询报告文件、依据性文件、其他文件必填列表以及数据
+					List<MainDictDetail> mainDictDetails = this.attachmentTemplateList();
+					for (MainDictDetail mainDict : mainDictDetails) {
+						relateInfo.setAttachType(mainDict.getValue());
+						File dirFile = null;
+						switch (mainDict.getValue()) {
+							case "11":
+								dirFile=new File(path+"/成果文件");
+								List<WorkClientAttachment> exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+								this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
+								break;
+							case "12":
+								dirFile=new File(path+"/依据性资料");
+								exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+								this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
+								break;
+							case "13":
+								dirFile=new File(path+"/其他文件");
+								exportProjectTemplateList = this.getExportProjectTemplateList(relateInfo);
+								this.disposeExportFileList(exportProjectTemplateList,dirFile.getPath());
+								break;
+						}
+
+					}
+				}
+
+				//处理完之后进行打包压缩并删除之前的文件
+				ZipCompressUtil zipUtil = new ZipCompressUtil();
+				resultFile = new File(path);
+				file = zipUtil.zipCompress(resultFile, true);
+
+
+
+			}catch (IOException e) {
+				logger.error("Exception e:"+e);
+			}
+			finally {
+				//路径是个文件且不为空时删除文件
+				if(resultFile.isFile()&&resultFile.exists()){
+					resultFile.delete();
+				}
+			}
+		}
+		return file.getPath();
+	}
+
 	/**
 	/**
 	 * 下载压缩文件
 	 * 下载压缩文件
 	 * @param filePath
 	 * @param filePath
@@ -2873,6 +3002,65 @@ public class RuralProjectRecordsService extends CrudService<RuralProjectRecordsD
 		return projectTemplateList;
 		return projectTemplateList;
 	}
 	}
 
 
+
+	/**
+	 * 项目相关文件信息批量导出
+	 * @param projectTemplateInfo
+	 * @return
+	 */
+	public List<WorkClientAttachment> getExportProjectTemplateList(ProjectAccessoryRelationInfo projectTemplateInfo){
+		//查询所有被选择为必填项展示列,并查询附件信息
+		List<ProjectTemplateInfo> projectTemplateList = projectTemplateDao.getProjectTemplateList(projectTemplateInfo);
+		//查询没有被选择必填项的数据并查询是否已经上传附件,若有则添加到展示列中
+		List<ProjectTemplateInfo> otherProjectTemplateList = projectTemplateDao.projectAccessoryListByParentId(projectTemplateInfo);
+		WorkClientAttachment attchment = new WorkClientAttachment();
+		attchment.setProjectId(projectTemplateInfo.getId());
+		List<String> attachmentIdList = Lists.newArrayList();
+		for (ProjectTemplateInfo info: projectTemplateList) {
+			attachmentIdList.add(info.getId());
+		}
+		for (ProjectTemplateInfo info: otherProjectTemplateList) {
+			attachmentIdList.add(info.getId());
+		}
+		attchment.setAttachmentIdList(attachmentIdList);
+		List<WorkClientAttachment> attachments = workClientAttachmentDao.getInfoList(attchment);
+
+		return attachments;
+	}
+
+	/**
+	 * 处理文件信息
+	 * @param workAttachments
+	 * @param downloadPath
+	 */
+	public void disposeExportFileList(List<WorkClientAttachment> workAttachments,String downloadPath) throws Exception {
+		if(null != workAttachments && !workAttachments.isEmpty()){
+			SftpClientUtil sftpClientUtil = new SftpClientUtil();
+			try {
+				// 批量连接一次
+				sftpClientUtil.connectBatch();
+
+				for (WorkClientAttachment attachment : workAttachments) {
+					String url = attachment.getUrl();
+					int lastSlash = url.lastIndexOf("/");
+					String dir = url.substring(0, lastSlash);
+					String fileName = url.substring(lastSlash + 1);
+
+					// 调用【批量下载方法】
+					sftpClientUtil.downloadRuralProjectBatch(
+							dir,
+							fileName,
+							downloadPath,
+							attachment.getAttachmentName()
+					);
+				}
+			} finally {
+				// 统一关闭
+				sftpClientUtil.closeBatch();
+			}
+		}
+	}
+
 	/**
 	/**
 	 * 项目列表新增文件信息获取
 	 * 项目列表新增文件信息获取
 	 * @param projectRecords  项目信息
 	 * @param projectRecords  项目信息

+ 53 - 1
src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralProjectMessageAllController.java

@@ -19,7 +19,6 @@ import com.jeeplus.modules.projectcontentinfo.entity.Projectcontentinfo;
 import com.jeeplus.modules.projectcontentinfo.service.ProjectReportDataService;
 import com.jeeplus.modules.projectcontentinfo.service.ProjectReportDataService;
 import com.jeeplus.modules.projectcontentinfo.service.ProjectcontentinfoService;
 import com.jeeplus.modules.projectcontentinfo.service.ProjectcontentinfoService;
 import com.jeeplus.modules.projectrecord.entity.ProjectRecords;
 import com.jeeplus.modules.projectrecord.entity.ProjectRecords;
-import com.jeeplus.modules.projectrecord.enums.ProjectStatusEnum;
 import com.jeeplus.modules.ruralprojectrecords.entity.*;
 import com.jeeplus.modules.ruralprojectrecords.entity.*;
 import com.jeeplus.modules.ruralprojectrecords.service.RuralProjectMessageAllService;
 import com.jeeplus.modules.ruralprojectrecords.service.RuralProjectMessageAllService;
 import com.jeeplus.modules.ruralprojectrecords.service.RuralProjectMessageService;
 import com.jeeplus.modules.ruralprojectrecords.service.RuralProjectMessageService;
@@ -47,6 +46,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
+import java.io.File;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.*;
@@ -856,4 +856,56 @@ public class RuralProjectMessageAllController extends BaseController {
         return map;
         return map;
     }
     }
 
 
+    /**
+     * 项目相应文件批量下载并压缩
+     */
+    @RequiresPermissions("ruralProject:ruralProjectRecords:del")
+    @RequestMapping(value = "exportAll")
+    public String exportAll(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) {
+        String listIds = request.getParameter("listId");
+        List<String> idList = Arrays.asList(listIds.split(","));
+        String filePath = null;
+        try {
+            filePath = projectRecordsService.exportAll(idList);
+            projectRecordsService.downloadZipFile(filePath,response);
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "批量下载项目文件失败!");
+            logger.error("Exception e:"+e);
+        }finally {
+            File file = new File(filePath);
+            //路径是个文件且不为空时删除文件
+            if(file.isFile()&&file.exists()){
+                file.delete();
+            }
+        }
+        return "redirect:"+Global.getAdminPath()+"/ruralProject/ruralProjectRecords/?repage";
+    }
+
+    /**
+     * 根据项目信息下载项目相关的档案信息
+     * @param response
+     * @param id
+     */
+    @RequestMapping(value="exportProjectRecordsFiles")
+    @ResponseBody
+    public void exportProjectRecordsFiles(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes, String id)  {
+        String filePath = null;
+        try {
+            filePath = projectRecordsService.exportProjectRecordsFiles(id);
+            projectRecordsService.downloadZipFile(filePath,response);
+            return ;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "批量下载项目文件失败!");
+            logger.error("Exception e:"+e);
+        }finally {
+            File file = new File(filePath);
+            //路径是个文件且不为空时删除文件
+            if(file.isFile()&&file.exists()){
+                file.delete();
+            }
+        }
+
+    }
+
 }
 }

+ 52 - 6
src/main/java/com/jeeplus/modules/utils/SftpClientUtil.java

@@ -1,10 +1,5 @@
 package com.jeeplus.modules.utils;
 package com.jeeplus.modules.utils;
 
 
-import java.io.*;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-import java.util.*;
-
 import com.jcraft.jsch.*;
 import com.jcraft.jsch.*;
 import com.jeeplus.common.bos.BOSClientUtil;
 import com.jeeplus.common.bos.BOSClientUtil;
 import com.jeeplus.common.config.Global;
 import com.jeeplus.common.config.Global;
@@ -18,7 +13,10 @@ import org.springframework.web.multipart.MultipartFile;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.io.FileUtils;
+import java.io.*;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Properties;
 
 
 /**
 /**
  * @author: 徐滕
  * @author: 徐滕
@@ -44,6 +42,9 @@ public class SftpClientUtil {
     @Autowired
     @Autowired
     private HttpServletResponse response;
     private HttpServletResponse response;
 
 
+    private OSSClientUtil batchOssClient;
+    private ChannelSftp batchSftp;
+
 
 
     /**
     /**
      * 上传单个文件
      * 上传单个文件
@@ -592,5 +593,50 @@ public class SftpClientUtil {
         return swapStream;
         return swapStream;
     }
     }
 
 
+    // ===================== 【新增】批量连接(只连1次) =====================
+    public void connectBatch() {
+        if ("2".equals(uploadMode)) {
+            // 只初始化1次OSS客户端
+            this.batchOssClient = new OSSClientUtil();
+        } else {
+            try {
+                this.batchSftp = SftpClientUtil.connect();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // ===================== 【新增】批量关闭 =====================
+    public void closeBatch() {
+        if (this.batchSftp != null) {
+            SftpClientUtil.disconnect(this.batchSftp);
+        }
+    }
+
+    // ===================== 【新增】批量下载(复制你的原方法修改,只改连接复用) =====================
+    public byte[] downloadRuralProjectBatch(String directory, String downloadFile,String downLoadFilePath,String finalFileName) throws IOException {
+        File dirFile = new File(downLoadFilePath);
+        if (!dirFile.exists()) {
+            dirFile.mkdirs();
+        }
+        if("2".equals(uploadMode)){
+            String fileNamePath = directory +"/" + downloadFile;
+            try {
+                fileNamePath = URLDecoder.decode(fileNamePath,"UTF-8");
+                String key = fileNamePath.substring(1,fileNamePath.length());
+                if (!downLoadFilePath.endsWith("/")) {
+                    downLoadFilePath += "/";
+                }
+                String downFileStr = downLoadFilePath + finalFileName;
+                // 【优化】使用批量连接,不新建!
+                this.batchOssClient.downByStreamSaveLocal(key,finalFileName,downFileStr);
+            }catch (IOException e){
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
 }
 }
 
 

+ 2 - 1
src/main/java/com/jeeplus/modules/workclientinfo/dao/WorkClientAttachmentDao.java

@@ -9,7 +9,6 @@ import com.jeeplus.modules.externalUnit.entity.ExternalUnitWorkClientAttachment;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientAttachment;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientAttachment;
 
 
 import java.util.List;
 import java.util.List;
-import java.util.Map;
 
 
 /**
 /**
  * 附件信息DAO接口
  * 附件信息DAO接口
@@ -23,6 +22,8 @@ public interface WorkClientAttachmentDao extends CrudDao<WorkClientAttachment> {
     List<WorkClientAttachment> getList(WorkClientAttachment attchment);
     List<WorkClientAttachment> getList(WorkClientAttachment attchment);
 
 
 
 
+
+
     List<WorkClientAttachment> getExistProjectAttachmentList(WorkClientAttachment attchment);
     List<WorkClientAttachment> getExistProjectAttachmentList(WorkClientAttachment attchment);
 
 
     /**
     /**

+ 25 - 0
src/main/resources/mappings/modules/ruralprojectrecords/RuralProjectRecordsDao.xml

@@ -7171,4 +7171,29 @@ GROUP BY
 	</select>
 	</select>
 
 
 
 
+	<select id="getInfoByProjectIdList" resultType="RuralProjectRecords">
+		select
+		<include refid="projectRecordsColumns"/>
+		,prd.number AS "projectReportNumber"
+		,a.submit_money as "submitMoney"
+		,a.engineering_type as "engineeringType"
+		,a.start_date as "startDate"
+		,a.ending_date as "endDate"
+		FROM rural_project_records a
+		left join project_report_data prd on prd.project_id = a.id
+		<include refid="projectRecordsJoins"/>
+		<where>
+			<if test="projectIdList!=null and projectIdList.size!=0">
+				and a.id in
+				<foreach collection="projectIdList" item="id" separator="," open="(" close=")">
+					#{id}
+				</foreach>
+			</if>
+		and prd.del_flag = 0
+		and a.del_flag = 0
+		</where>
+
+	</select>
+
+
 </mapper>
 </mapper>

+ 58 - 0
src/main/webapp/webpage/modules/ruralprojectrecords/ruralporjectmessage/all/ruralProjectMessageAllList.jsp

@@ -1119,6 +1119,10 @@
 							<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-blue" id="reportBatchReject"> 批量驳回</button>
 							<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-blue" id="reportBatchReject"> 批量驳回</button>
 						</shiro:hasPermission>
 						</shiro:hasPermission>
 
 
+						<shiro:hasPermission name="ruralProject:ruralCostProjectRecords:exportAll">
+							<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-blue" id="delUser"> 下载项目档案信息</button>
+						</shiro:hasPermission>
+
 
 
 						<button class="layui-btn layui-btn-sm" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
 						<button class="layui-btn layui-btn-sm" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
 						<shiro:hasPermission name="ruralProject:ruralCostProjectMessage:leader">
 						<shiro:hasPermission name="ruralProject:ruralCostProjectMessage:leader">
@@ -1581,6 +1585,60 @@
 			});
 			});
 			return false;
 			return false;
 		}
 		}
+
+		$("#delUser").off('click').on('click', function () {
+			var _this = $(this);
+
+			// 1. 获取表格选中ID 【你的原代码,一字未改】
+			var checkList = layui.table.checkStatus('checkboxTable').data;
+			var listId = [];
+			$.each(checkList, function (i, data) {
+				listId.push(data.id);
+			});
+			if (listId.length <= 0) {
+				layer.msg("请选择需要下载的项目信息", {icon: 2});
+				return;
+			}
+
+			// ✅ ✅ ✅ 【你指定的提示框样式,100%原封不动保留,一行没改】✅ ✅ ✅
+			var loadingIndex = layer.msg("文件生成中,请不要离开该页面,下载完成自动弹窗~", {
+				icon:16,
+				time:0,
+				shade:0.1,
+				btn: ['我知道了']
+			});
+
+			// ✅ 核心:随机唯一ID创建form+iframe,无DOM残留、无限次下载无冲突,同时下载多个也可以
+			var randomId = "down_" + new Date().getTime();
+			var downForm = $('<form>', {
+				'action': "${ctx}/ruralProject/ruralProjectRecords/exportAll",
+				'method': 'GET',
+				'style': 'display:none;'
+			}).appendTo('body');
+			$('<input>', {'type':'hidden','name':'listId','value':listId.join(",")}).appendTo(downForm);
+			var downIframe = $('<iframe>', {'name':randomId,'style':'display:none;'}).appendTo('body');
+			downForm.attr('target', randomId);
+			downForm.submit();
+
+			// ✅ 点击「我知道了」关闭提示+清理资源,无事件残留
+			layer.on('btn(我知道了)', function(index){
+				layer.close(index);
+				cleanResource();
+			});
+
+			// ✅ 30分钟兜底超时自动清理资源(适配最长的文件生成时间,无内存泄漏)
+			var timer = setTimeout(cleanResource, 1800000);
+
+			// ✅ 统一清理函数:只清理本次下载的资源,不影响其他操作
+			function cleanResource(){
+				clearTimeout(timer);
+				downForm.remove();
+				downIframe.remove();
+				layer.close(loadingIndex);
+				layer.off('btn(我知道了)'); // 解绑全局事件,彻底杜绝冲突
+			}
+		});
+
     })
     })
 
 
     resizeListTable();
     resizeListTable();

+ 38 - 0
src/main/webapp/webpage/modules/ruralprojectrecords/view/projectAccessoryView.jsp

@@ -6,6 +6,8 @@
 	<meta name="decorator" content="default"/>
 	<meta name="decorator" content="default"/>
 	<%--<link href="${ctxStatic}/layer-v2.3/layui/tableTree/treetable.css" rel="stylesheet" />--%>
 	<%--<link href="${ctxStatic}/layer-v2.3/layui/tableTree/treetable.css" rel="stylesheet" />--%>
 	<%@include file="/webpage/include/treetable.jsp" %>
 	<%@include file="/webpage/include/treetable.jsp" %>
+	<script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+	<link rel='stylesheet' type="text/css" href="${ctxStatic}/layui/css/layui.css"/>
 	<style>
 	<style>
 		label.error{
 		label.error{
 			top:40px;
 			top:40px;
@@ -56,6 +58,12 @@
 					tips: 3
 					tips: 3
 				});
 				});
 			})
 			})
+
+			layui.use(['form', 'layer'], function () {
+				var form = layui.form;
+
+			})
+
 		});
 		});
 
 
 		function readOpenInfo(value) {
 		function readOpenInfo(value) {
@@ -65,6 +73,25 @@
 				content: value
 				content: value
 			});
 			});
 		}
 		}
+
+		function doDownload(btn) {
+
+			// 用 LayUI 确认框的回调写法
+			confirmx('是否需要下载该项目档案信息?', function() {
+				const downloadUrl = btn.getAttribute('data-url');
+				// 先关闭 confirmx 弹窗,再弹出提示(关键!)
+				layui.layer.closeAll('dialog');
+				// 弹出 2 秒提示
+				layui.layer.msg('下载中请勿进行其他操作~', {
+					time: 2000,          // 2秒自动关闭
+					icon: 6,             // 成功图标
+					shade: [0.1, '#000'] // 背景灰色 0.2 透明度(你要的效果)
+				}, function() {
+					// 提示消失后,再开始下载(避免提示被打断)
+					document.getElementById('downloadFrame').src = downloadUrl;
+				});
+			});
+		}
 	</script>
 	</script>
 </head>
 </head>
 <body>
 <body>
@@ -106,6 +133,17 @@
 			<input type="hidden" id="reviewFee" value="${projectcontentinfo.projectReportData.reviewFee}">
 			<input type="hidden" id="reviewFee" value="${projectcontentinfo.projectReportData.reviewFee}">
 		<sys:message content="${message}"/>
 		<sys:message content="${message}"/>
 
 
+			<iframe id="downloadFrame" style="display:none;"></iframe>
+
+			<!-- 下载按钮,把完整路径存在 data-url 里 -->
+			<div style="float: right;">
+				<a href="javascript:;"
+				   class="layui-btn layui-btn-sm"
+				   data-url="${ctx}/ruralProject/ruralProjectMessageAll/exportProjectRecordsFiles?id=${projectRecords.id}"
+				   onclick="doDownload(this)">
+					<i class="fa fa-file-excel-o"></i> 下载项目档案信息
+				</a>
+			</div>
 
 
 			<!-- 成果文件模块引用 -->
 			<!-- 成果文件模块引用 -->
 			<table:otherFileDisplay
 			<table:otherFileDisplay