Bladeren bron

业务招待制度生成zip文件

dingsixi 2 weken geleden
bovenliggende
commit
21305dca6f

+ 526 - 0
base/nckd-base-common/src/main/java/nckd/base/common/utils/AttachmentUtils.java

@@ -0,0 +1,526 @@
+package nckd.base.common.utils;
+
+import cn.hutool.core.util.ZipUtil;
+import kd.bos.cache.CacheFactory;
+import kd.bos.cache.TempFileCache;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.MainEntityType;
+import kd.bos.exception.KDBizException;
+import kd.bos.fileservice.BatchDownloadRequest;
+import kd.bos.fileservice.FileItem;
+import kd.bos.fileservice.FileService;
+import kd.bos.fileservice.FileServiceFactory;
+import kd.bos.id.ID;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.AttachmentServiceHelper;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.ExceptionUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+/**
+ * @description:附件工具
+ * @author: dingsixi
+ * @create: 2025/12/24 12:09
+ */
+public class AttachmentUtils {/**
+ * 附件详情单据标识
+ */
+private static final String BOS_ATTACHMENT_ENTITY = "bos_attachment";
+
+    /**
+     * 复制附件
+     *
+     * @param srcBill             源单据
+     * @param srcAttachmentPanel  源单据附件面板
+     * @param destBill            目标单据
+     * @param destAttachmentPanel 目标单据附件面板
+     */
+    public static void copy(DynamicObject srcBill, String srcAttachmentPanel, DynamicObject destBill,
+                            String destAttachmentPanel) {
+        String srcEntityName = srcBill.getDynamicObjectType().getName();
+        String destEntityName = destBill.getDynamicObjectType().getName();
+        Object srcBillId = srcBill.getPkValue();
+        Object destBillId = destBill.getPkValue();
+        List<Map<String, Object>> srcAttachments = AttachmentServiceHelper.getAttachments(srcEntityName, srcBillId,
+                srcAttachmentPanel);
+        List<Map<String, Object>> destAttachments = AttachmentServiceHelper.getAttachments(destEntityName, destBillId,
+                destAttachmentPanel);
+        List<Map<String, Object>> srcAttachmentDatas = decodeAttachmentUrls(srcAttachments);
+        List<Map<String, Object>> destAttachmentDatas = decodeAttachmentUrls(destAttachments);
+        if (!destAttachmentDatas.isEmpty()) {
+            srcAttachmentDatas.removeIf(srcAttachmentData -> {
+                for (Map<String, Object> destAttachmentData : destAttachmentDatas) {
+                    if (destAttachmentData.get("url").equals(srcAttachmentData.get("url"))) {
+                        return true;
+                    }
+                }
+                return false;
+            });
+        }
+        for (Map<String, Object> attachmentData : srcAttachmentDatas) {
+            String fileName = String.valueOf(attachmentData.get("name"));
+            String attachmentUrl = String.valueOf(attachmentData.get("url"));
+            Integer byteSize = Integer.parseInt(String.valueOf(attachmentData.get("size")));
+            DynamicObject destAttachmentData = createAttachmentData(fileName, attachmentUrl, byteSize, destBill, destAttachmentPanel);
+            SaveServiceHelper.save(new DynamicObject[]{destAttachmentData});
+        }
+
+    }
+
+    /**
+     * 上传文件
+     *
+     * @param fileName 文件名
+     * @param is       文件流
+     * @return 文件url
+     */
+    public static String upload(String fileName, InputStream is) {
+        return upload(fileName, is, null);
+    }
+
+    /**
+     * 上传文件
+     *
+     * @param fileName 文件名
+     * @param path     文件路径
+     * @param is       文件流
+     * @return 文件url
+     */
+    public static String upload(String fileName, InputStream is, String path) {
+        if (path == null) {
+            path = getAttachmentFilePath() + "/" + fileName;
+        } else if (!path.endsWith(fileName)) {
+            path = path + "/" + fileName;
+        }
+        FileItem fileItem = new FileItem(fileName, path, is);
+        fileItem.setCreateNewFileWhenExists(true);
+        FileService attachmentFileService = FileServiceFactory.getAttachmentFileService();
+        String attachmentUrl = attachmentFileService.upload(fileItem);
+        return attachmentUrl;
+    }
+
+    /**
+     * 上传附件 通过字节数组
+     *
+     * @param fileName        附件名称
+     * @param bytes           文件字节数组
+     * @param bill            单据
+     * @param attachmentPanel 附件面板
+     */
+    public static void upload(String fileName, byte[] bytes, DynamicObject bill, String attachmentPanel) {
+        DynamicObject attachmentData = createAttachmentData(fileName, bytes, bill, attachmentPanel);
+        SaveServiceHelper.save(new DynamicObject[]{attachmentData});
+    }
+
+    /**
+     * 上传附件 通过字文件流
+     *
+     * @param fileName        附件名称
+     * @param is              文件流
+     * @param bill            单据
+     * @param attachmentPanel 附件面板
+     */
+    public static void upload(String fileName, InputStream is, DynamicObject bill, String attachmentPanel) {
+        DynamicObject attachmentData = createAttachmentData(fileName, is, bill, attachmentPanel);
+        SaveServiceHelper.save(new DynamicObject[]{attachmentData});
+    }
+
+    /**
+     * 构建待上传的附件 通过字节数组
+     *
+     * @param fileName        附件名称
+     * @param bytes           文件字节数组
+     * @param bill            单据
+     * @param attachmentPanel 附件面板
+     * @return 附件对象
+     */
+    public static DynamicObject createAttachmentData(String fileName, byte[] bytes, DynamicObject bill,
+                                                     String attachmentPanel) {
+        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+        return createAttachmentData(fileName, is, bill, attachmentPanel);
+    }
+
+    /**
+     * 构建待上传的附件 通过字文件流
+     *
+     * @param fileName        附件名称
+     * @param is              文件流
+     * @param bill            单据
+     * @param attachmentPanel 附件面板
+     * @return 附件对象
+     */
+    public static DynamicObject createAttachmentData(String fileName, InputStream is, DynamicObject bill,
+                                                     String attachmentPanel) {
+        int count = 0;
+        try {
+            count = is.available();
+        } catch (IOException e1) {
+            e1.printStackTrace();
+        }
+        String path = getAttachmentFilePath(bill);
+        String attachmentUrl = upload(fileName, is, path);
+        return createAttachmentData(fileName, attachmentUrl, count, bill, attachmentPanel);
+    }
+
+    /**
+     * 构建待上传的附件 通过文件url
+     *
+     * @param fileName        文件名
+     * @param attachmentUrl   文件url
+     * @param sizeInByte      文件字节数
+     * @param bill            单据
+     * @param attachmentPanel 附件面板
+     * @return
+     */
+    private static DynamicObject createAttachmentData(String fileName, String attachmentUrl, int sizeInByte, DynamicObject bill,
+                                                      String attachmentPanel) {
+        DynamicObject attachment = BusinessDataServiceHelper.newDynamicObject(BOS_ATTACHMENT_ENTITY);
+        // attachment.set("id", Long.valueOf(orm.genLongId(bos_attachment)));
+        attachment.set("fnumber", ID.genStringId());// 编码
+        attachment.set("fbilltype", bill.getDataEntityType().getName());// 单据类型
+        attachment.set("fbillno", bill.getString("number"));// 单据编号
+        attachment.set("finterid", String.valueOf(bill.getPkValue()));// 单据内码
+        Date today = new Date();
+        attachment.set("fcreatetime", today);// 创建时间
+        attachment.set("fmodifytime", today);// 修改时间
+        attachment.set("faudittime", today);// 审核时间
+        attachment.set("faliasfilename", fileName);// 别名
+        attachment.set("fattachmentname", fileName);// 文件名
+        attachment.set("fextname", fileName.substring(fileName.lastIndexOf(".") + 1));// 文件类型
+        attachment.set("fattachmentsize", sizeInByte);// 大小 kb
+        attachment.set("ffileid", attachmentUrl);// 文件 url
+        Object creator = bill.get("creator");
+        attachment.set("fcreatemen", creator);// 创建人
+        attachment.set("fmodifymen", creator);// 修改人
+        attachment.set("fauditmen", creator);// 审核人
+        attachment.set("fattachmentpanel", attachmentPanel);// 附件面板key
+        return attachment;
+    }
+
+
+    /**
+     * 打包下载附件
+     *
+     * @param fileName        文件名
+     * @param pkValues        单据内码数组
+     * @param entityName      单据标识
+     * @param attachmentPanel 附件面板
+     * @return
+     */
+    public static String download(String fileName, Object[] pkValues, String entityName, String attachmentPanel) {
+        List<QFilter> qFilterList = new ArrayList<>();
+        if (pkValues != null) {
+            qFilterList.add(new QFilter("finterid", QCP.in, pkValues));
+        }
+        if (entityName != null) {
+            qFilterList.add(new QFilter("fbilltype", QCP.equals, entityName));
+        }
+        if (pkValues != null) {
+            qFilterList.add(new QFilter("fattachmentpanel", QCP.equals, attachmentPanel));
+        }
+        DynamicObjectCollection fileUrlCollection = QueryServiceHelper.query(BOS_ATTACHMENT_ENTITY, "ffileid", qFilterList.toArray(new QFilter[0]));
+        String[] fileUrls = fileUrlCollection.stream().map(o -> o.getString("ffileid")).toArray(String[]::new);
+        return downloadByAttachmentUrls(fileName, fileUrls);
+    }
+
+    /**
+     * 打包下载附件
+     *
+     * @param fileName      文件名
+     * @param attachmentIds 附件id数组
+     * @return 打包下载的URL地址
+     */
+    public static String downloadByAttachmentIds(String fileName, String... attachmentIds) {
+        QFilter qFilter = new QFilter("id", QCP.in, attachmentIds);
+        DynamicObject[] attachments = BusinessDataServiceHelper.load("bd_attachment", "id,url",
+                new QFilter[]{qFilter});
+        String[] filePaths = Arrays.stream(attachments).map(attachment -> attachment.getString("url"))
+                .toArray(String[]::new);
+        return downloadByAttachmentUrls(fileName, filePaths);
+    }
+
+    /**
+     * 打包下载附件 linux 创建中文文件名会报错 Malformed input or input contains unmappable
+     * characters, 所以用字节流 大文件如何处理呢,字节数组放不下
+     *
+     * @param fileName       文件名
+     * @param attachmentUrls 附件路径数组
+     * @return 打包下载的URL地址
+     */
+    public static String downloadByAttachmentUrls(String fileName, String... attachmentUrls) {
+        if (attachmentUrls == null || attachmentUrls.length == 0) {
+            return null;
+        }
+        if (attachmentUrls.length > 1) {
+            fileName = fileName == null ? "attachments.zip"
+                    : !fileName.toLowerCase().endsWith(".zip") ? fileName + ".zip" : fileName;
+        } else if (fileName == null) {
+            String attachmentUrl = attachmentUrls[0];
+            fileName = attachmentUrl.substring(attachmentUrl.lastIndexOf("/") + 1);
+        }
+        FileService fs = FileServiceFactory.getAttachmentFileService();
+        // 模拟浏览器userAgent访问
+        String userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
+        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            if (attachmentUrls.length > 1) {
+                // 构造BatchDownloadRequest对象
+                BatchDownloadRequest bdr = getBatchDownloadRequest(attachmentUrls);
+                fs.batchDownload(bdr, out, userAgent);
+            } else {
+                String attachmentUrl = attachmentUrls[0];
+                fs.download(attachmentUrl, out, userAgent);
+            }
+            out.flush();
+            return download(fileName, out.toByteArray());
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new KDBizException("下载附件失败:"+e);
+        }
+    }
+
+    /**
+     * 下载文件
+     *
+     * @param fileName 要下载的文件名
+     * @param in       输入流
+     * @return 文件下载的URL地址
+     */
+    public static String download(String fileName, InputStream in) {
+        TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+        return cache.saveAsUrl(fileName, in, 2 * 60);
+    }
+
+    /**
+     * 下载文件
+     *
+     * @param fileName 要下载的文件名
+     * @param file     文件
+     * @return 文件下载的URL地址
+     */
+    public static String download(String fileName, File file) {
+        if (!file.exists()) {
+            return null;
+        }
+        if (fileName == null) {
+            fileName = file.getName();
+        }
+        if (file.isDirectory()) {
+            file = ZipUtil.zip(file);
+            fileName = fileName.endsWith(".zip") ? fileName : fileName + ".zip";
+        }
+        try {
+            InputStream in = new FileInputStream(file);
+            return download(fileName, in);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 下载文件
+     *
+     * @param fileName 要下载的文件名
+     * @param filePath 文件路径
+     * @return 文件下载的URL地址
+     */
+    public static String downloadByFilePath(String fileName, String filePath) {
+        return download(fileName, new File(filePath));
+    }
+
+    /**
+     * 通过url下载文件
+     *
+     * @param fileName 要下载的文件流
+     * @param url      url 源文件url
+     * @return 文件下载的URL地址
+     */
+    public static String download(String fileName, URL url) {
+        try {
+            if ("file".equals(url.getProtocol())) {
+                String filePath = URLDecoder.decode(url.getPath(), Charset.defaultCharset().name());
+                if (System.getProperty("os.name").startsWith("Windows") && filePath.startsWith("/")) {
+                    filePath = filePath.substring(1);
+                }
+                if (new File(filePath).isDirectory()) {
+                    return downloadByFilePath(fileName, filePath);
+                }
+            }
+            if (fileName == null) {
+                fileName = url.getPath();
+                while (fileName.endsWith("/")) {
+                    fileName = fileName.substring(0, fileName.length() - 1);
+                }
+                fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
+            }
+            return download(fileName, url.openStream());
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据字节数组下载文件
+     *
+     * @param fileName 要下载的文件名
+     * @param bytes    字节数组
+     * @return 文件下载的URL地址
+     */
+    public static String download(String fileName, byte[] bytes) {
+        InputStream in = new ByteArrayInputStream(bytes);
+        return download(fileName, in);
+    }
+
+    /**
+     * 构建批量下载请求
+     *
+     * @param attachmentUrls 附件url
+     * @return 批量下载请求
+     */
+    @NotNull
+    private static BatchDownloadRequest getBatchDownloadRequest(String[] attachmentUrls) {
+        BatchDownloadRequest.Dir srcDir = new BatchDownloadRequest.Dir("attachments");
+        List<BatchDownloadRequest.File> srcFiles = new ArrayList<>();
+        for (String attachmentUrl : attachmentUrls) {
+            String attachmentName = attachmentUrl.substring(attachmentUrl.lastIndexOf("/") + 1);
+            srcFiles.add(new BatchDownloadRequest.File(attachmentName, attachmentUrl));
+        }
+        srcDir.setFiles(srcFiles.toArray(new BatchDownloadRequest.File[0]));
+        BatchDownloadRequest bdr = new BatchDownloadRequest("test-batch-download");
+        bdr.setDirs(new BatchDownloadRequest.Dir[]{srcDir});
+        return bdr;
+    }
+
+    /**
+     * 获取附件路径
+     *
+     * @param bill
+     * @return
+     */
+    public static String getAttachmentFilePath(DynamicObject bill) {
+        RequestContext ctx = RequestContext.get();
+        String tenantId = ctx.getTenantId();
+        String accountId = ctx.getAccountId();
+        String date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
+        String appId = "unknowappid";
+        DynamicObjectType type = bill.getDynamicObjectType();
+        if (type instanceof MainEntityType) {
+            MainEntityType mainEntityType = (MainEntityType) type;
+            appId = mainEntityType.getAppId();
+        }
+        String entityName = type.getName();
+        return String.join("/", tenantId, accountId, date, appId, entityName, String.valueOf(bill.getPkValue()),
+                "attachments");
+    }
+
+    /**
+     * 获取附件路径
+     *
+     * @param entityNumber
+     * @return
+     */
+    public static String getAttachmentFilePath(String entityNumber) {
+        MainEntityType type = MetadataServiceHelper.getDataEntityType(entityNumber);
+        return getAttachmentFilePath(type);
+    }
+
+    /**
+     * 获取附件路径
+     *
+     * @param type
+     * @return
+     */
+    public static String getAttachmentFilePath(MainEntityType type) {
+        RequestContext ctx = RequestContext.get();
+        String tenantId = ctx.getTenantId();
+        String accountId = ctx.getAccountId();
+        String date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
+        String appId = type.getAppId();
+        String entityName = type.getName();
+        return String.join("/", tenantId, accountId, date, appId, entityName, "attachments");
+    }
+
+    /**
+     * 获取附件路径
+     *
+     * @param type
+     * @return
+     */
+    @SuppressWarnings("unused")
+    public static String getAttachmentFilePath(DynamicObjectType type) {
+        RequestContext ctx = RequestContext.get();
+        String tenantId = ctx.getTenantId();
+        String accountId = ctx.getAccountId();
+        String date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
+        String appId = "unknowappid";
+        if (type instanceof MainEntityType) {
+            MainEntityType mainEntityType = (MainEntityType) type;
+            appId = mainEntityType.getAppId();
+        }
+        String entityName = type.getName();
+        return String.join("/", tenantId, accountId, date, appId, entityName, "0", "attachments");
+    }
+
+    /**
+     * 获取附件路径
+     *
+     * @return
+     */
+    public static String getAttachmentFilePath() {
+        RequestContext ctx = RequestContext.get();
+        String tenantId = ctx.getTenantId();
+        String accountId = ctx.getAccountId();
+        String date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
+        return String.join("/", tenantId, accountId, date, "attachments");
+    }
+
+    /**
+     * @param attachmentDatas
+     * @return
+     */
+    private static List<Map<String, Object>> decodeAttachmentUrls(List<Map<String, Object>> attachmentDatas) {
+        if (!attachmentDatas.isEmpty()) {
+            for (Map<String, Object> attachmentData : attachmentDatas) {
+                //AttachmentServiceHelper.remove(destEntityName, destBillId, destAttachment.get("uid"));
+                String attachmentUrl = String.valueOf(attachmentData.get("url"));
+                attachmentUrl = decodeAttachmentUrl(attachmentUrl);
+                attachmentData.put("url", attachmentUrl);
+            }
+        }
+        return attachmentDatas;
+    }
+
+    @NotNull
+    public static String decodeAttachmentUrl(String attachmentUrl) {
+        try {
+            attachmentUrl = URLDecoder.decode(attachmentUrl, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        int beginIndex = 0, endIndex = attachmentUrl.length();
+        if (attachmentUrl.contains("download.do?path=")) {
+            beginIndex = attachmentUrl.indexOf("download.do?path=") + "download.do?path=".length() + 1;
+        }
+        if (attachmentUrl.contains("&kdedcba=")) {
+            endIndex = attachmentUrl.indexOf("&kdedcba=");
+        }
+        attachmentUrl = attachmentUrl.substring(beginIndex, endIndex);
+        return attachmentUrl;
+    }
+
+}

+ 338 - 0
base/nckd-base-common/src/main/java/nckd/base/common/utils/GzwCommonUtils.java

@@ -0,0 +1,338 @@
+package nckd.base.common.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDBizException;
+import kd.bos.fileservice.FileService;
+import kd.bos.fileservice.FileServiceFactory;
+import nckd.base.common.constant.OperationKeyConst;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+/**
+ * @description:国资委通用方法工具类
+ * @author: dingsixi
+ * @create: 2025/12/22 18:10
+ */
+public class GzwCommonUtils {
+
+    /**
+     * File文件夹相对路径
+     */
+    public final static String FILEPATH = "gzw/";
+
+    /**
+     * @param orgCode          企业统一社会信用代码
+     * @param ver              版本号
+     * @param cusDateStr       当前时间
+     * @param xmlMap           单据XML动态参数  key=资源代号  value = 动态参数
+     * @param billAttrFileList 单据附件
+     * @param apiCode          接口代号
+     * @return 将单据数据动态参数生成XML文件,附件转为FIle文件,合并生成zip文件
+     */
+    public static File createZipFile(String orgCode, String ver, String cusDateStr, Map<String, Object> xmlMap,
+                                     List<File> billAttrFileList, String apiCode) {
+        List<File> xmlFileList = new ArrayList<>();
+        //生成单据数据XML文件
+        for (Map.Entry<String, Object> entry : xmlMap.entrySet()) {
+            File xmlFile = GzwCommonUtils.createXMLFile(orgCode, entry.getKey(), ver, cusDateStr, (List<LinkedHashMap<String, Object>>) entry.getValue());
+            xmlFileList.add(xmlFile);
+        }
+
+        //生成ZIP文件名称
+        String zipFileName = GzwCommonUtils.generateFileName(orgCode, apiCode, ver, cusDateStr, GzwXmlUtils.ZIP);
+
+        //单据XML文件、单据附件File文件合并生成zip文件
+        File zip = GzwXmlUtils.createZipWithAttachments(xmlFileList, billAttrFileList, zipFileName);
+
+        //删除XML、单据附件文件,保留zip
+        xmlFileList.forEach(File::delete);
+        billAttrFileList.forEach(File::delete);
+
+        return zip;
+    }
+
+    /**
+     * @param orgCode    企业统一社会信用代码
+     * @param resCode    资源代号
+     * @param ver        版本号
+     * @param cusDateStr 当前时间
+     * @param dataList   单据数据动态参数
+     * @return 生成XML文件
+     */
+    public static File createXMLFile(String orgCode, String resCode, String ver, String cusDateStr,
+                                     List<LinkedHashMap<String, Object>> dataList) {
+        //生成基础信息XML文件名称
+        String xmlFileName = GzwCommonUtils.generateFileName(orgCode, resCode, ver, cusDateStr, GzwXmlUtils.XML);
+        //生成生成基础信息XML文件
+        return GzwCommonUtils.saveXmlToFile(GzwXmlUtils.generateXml(dataList), xmlFileName);
+    }
+
+    /**
+     * @param attachments      单据附件信息
+     * @param billAttrFileList 单据附件File文件
+     * @param attrDataList     单据附件动态参数
+     * @param fixedData        XML内容的固定参数
+     * @return 将单据附件信息转成动态参数及File文件
+     */
+    public static void getBillAttrDataAndFile(List<Map<String, Object>> attachments,
+                                              List<File> billAttrFileList,
+                                              List<LinkedHashMap<String, Object>> attrDataList,
+                                              Map<String, Object> fixedData) {
+        if (attachments.isEmpty()) {
+            return;
+        }
+        FileService attachmentFileService = FileServiceFactory.getAttachmentFileService();
+
+        // 获取附件
+        for (Map<String, Object> fileMap : attachments) {
+            //将单据附件转为File对象
+            File file = generateBillAttrToFile(fileMap, attachmentFileService);
+            //重新设置附件信息名称 文件名称添加MD5前缀
+//            fileMap.put("name",file.getName());
+            billAttrFileList.add(file);
+
+            //附件动态参数
+            LinkedHashMap<String, Object> attrMapData = createBillAttrData(fileMap);
+            attrMapData.putAll(fixedData);
+            attrDataList.add(attrMapData);
+
+        }
+    }
+
+    /**
+     * @param fileMap 单据附件信息
+     * @return 创建单据附件信息动态参数
+     */
+    public static LinkedHashMap<String, Object> createBillAttrData(Map<String, Object> fileMap) {
+        LinkedHashMap<String, Object> attrMap = new LinkedHashMap<>();
+        attrMap.put("uuid", fileMap.get("uid"));
+        attrMap.put("biz_uuid", fileMap.get("billPkId"));
+        attrMap.put("file_name", fileMap.get("name"));
+        attrMap.put("file_path", fileMap.get("relativeUrl"));
+        attrMap.put("file_type", fileMap.get("type"));
+        attrMap.put("create_time", DateUtil.date2str((Date) fileMap.get("lastModified"), DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
+
+        return attrMap;
+    }
+
+    /**
+     * @param fileMap               单据附件信息
+     * @param attachmentFileService 附件服务
+     * @return 将单据附件转为File对象
+     */
+    public static File generateBillAttrToFile(Map<String, Object> fileMap, FileService attachmentFileService) {
+        String fileName = (String) fileMap.get("name");
+        // 获取相对路径
+        String relativeUrl = (String) fileMap.get("relativeUrl");
+
+        //MD5
+        String md5 = calculateMD5(relativeUrl);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        attachmentFileService.download(relativeUrl, out, null);
+        InputStream in = new ByteArrayInputStream(out.toByteArray());
+        File file = null;
+        try {
+            file = inputStreamToFile(in, FILEPATH + md5 + "_" + fileName);
+        } catch (Exception e) {
+            throw new KDBizException("附件上传异常" + e.getMessage());
+        }
+
+        return file;
+    }
+
+    /**
+     * 生成数据包编号(32位小写UUID)
+     *
+     * @return 32位uuid,如:85b11b185f0d4e0b88d9aa3a97337efa
+     */
+    public static String generatePacketId() {
+        return UUID.randomUUID()
+                .toString()
+                .replace("-", "")
+                .toLowerCase();
+    }
+
+    /**
+     * @return 当前时间年月日时分秒
+     */
+    public static String getCusDateStr() {
+        return DateUtil.date2str(new Date(), "yyyyMMddHHmmss");
+    }
+
+    /**
+     * @param operationKey 操作标识
+     * @param obj          业务数据
+     * @return 获取数据标识( 1  表示新增,2 表示修改,3  表示删除 ),默认为 1
+     */
+    public static String getDataFlag(String operationKey, DynamicObject obj) {
+        //删除 = 3 ,非删除从数据库取出来得数据则是修改=2,不是从数据库取出的则是新增 = 1
+        return operationKey.equals(OperationKeyConst.DELETE) ? "3" : obj.getDataEntityState().getFromDatabase() ? "2" : "1";
+    }
+
+    /**
+     * @param resMap   国资委采集平台对应的表名(文档提供)
+     * @param org      业务单元
+     * @param dataFlag 数据标识 ( 1  表示新增,2 表示修改,3  表示删除 ),默认为 1
+     * @return XML固定参数内容 不要动key值,固定参数
+     */
+    public static Map<String, Object> getFixedData(Map<String, String> resMap, DynamicObject org, String dataFlag) {
+        //单位编码 统一社会信用代码
+        String code = org.getString("uniformsocialcreditcode");
+        //单位名称
+        String orgName = org.getString("name");
+        Map<String, Object> fixedData = new HashMap<>();
+
+        //资源简称
+        fixedData.put("resCaption", resMap.get("nckd_rescaption"));
+        //上报单位 编 码
+        fixedData.put("resCode", resMap.get("nckd_rescode"));
+        //上报单位 编 码
+        fixedData.put("EXT_REPORTSOCIALCODE", code);
+        //单 位 编 码
+        fixedData.put("EXT_CORPSOCIALCODE", code);
+        //数据标识
+        fixedData.put("EXT_DATAFLAG", dataFlag);
+        //单位名称
+        fixedData.put("EXT_CORPNAME", orgName);
+        //可为空
+        fixedData.put("EXT_GUID", "");
+        return fixedData;
+    }
+
+    /**
+     * @param table  数据标准表名(文档提供)
+     * @param resStr 获取费用核算应用参数.资源代号信息
+     * @return 获取资源代号信息
+     */
+    public static Map<String, String> getResMap(String table, String resStr) {
+        if (StringUtils.isEmpty(resStr)) {
+            throw new KDBizException("请前往系统参数-应用-财务云-费用核算配置资源代号信息");
+        }
+        //资源代号信息
+        Map<String, String> res = ((List<Map<String, String>>) JSONObject.parseObject(resStr, List.class))
+                .stream().filter(it -> it.get("nckd_table").equals(table)).findAny().orElse(null);
+        if (null == res) {
+            throw new KDBizException("请前往系统参数-应用-财务云-费用核算配置资源代号信息,配置业务招待制度对应的表名");
+        }
+        return res;
+    }
+
+    /**
+     * 将InputStream转换为File
+     *
+     * @param inputStream 输入流
+     * @param fileName    输出文件名
+     * @return 生成的File对象
+     */
+    public static File inputStreamToFile(InputStream inputStream, String fileName) {
+        if (inputStream == null) {
+            throw new IllegalArgumentException("输入流不能为空");
+        }
+
+        if (fileName == null || fileName.trim().isEmpty()) {
+            throw new IllegalArgumentException("文件名不能为空");
+        }
+
+        File file = new File(fileName);
+
+        // 确保父目录存在
+        File parentDir = file.getParentFile();
+        if (parentDir != null && !parentDir.exists()) {
+            boolean created = parentDir.mkdirs();
+            if (!created) {
+                try {
+                    throw new IOException("无法创建目录: " + parentDir.getAbsolutePath());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        // 使用try-with-resources确保流被关闭
+        try (OutputStream outputStream = new FileOutputStream(file)) {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+
+            // 确保数据写入磁盘
+            outputStream.flush();
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return file;
+    }
+
+    /**
+     * @param orgCode    业务单元.统一社会信用代码
+     * @param resCode    接口代号/资源代号
+     * @param ver        版本号
+     * @param cusDateStr 当前时间
+     * @return
+     */
+    public static String generateFileName(String orgCode, String resCode, String ver, String cusDateStr, String fileType) {
+        return String.format(GzwCommonUtils.FILEPATH + "%s_%s_%s_%s_%s.%s", orgCode, resCode, ver, cusDateStr, GzwCommonUtils.generatePacketId(), fileType);
+    }
+
+    /**
+     * @param xmlContent XML内容
+     * @param filePath   XNL文件名称
+     * @return 将XML字符串保存为文件
+     */
+    public static File saveXmlToFile(String xmlContent, String filePath) {
+        File xmlFile = new File(filePath);
+
+        try (BufferedWriter writer = new BufferedWriter(
+                new OutputStreamWriter(new FileOutputStream(xmlFile), StandardCharsets.UTF_8))) {
+            writer.write(xmlContent);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return xmlFile;
+    }
+
+    /**
+     * @param filePath 文件路径
+     * @return md5
+     */
+    public static String calculateMD5(String filePath) {
+        try (InputStream fis = new FileInputStream(filePath)) {
+            //  获取 MD5 MessageDigest  实例
+            MessageDigest digest = MessageDigest.getInstance("MD5");
+
+            //  用于保存读取的字节数
+            byte[] byteArray = new byte[1024];
+            int bytesCount;
+
+            //  读取文件内容,并更新摘要信息
+            while ((bytesCount = fis.read(byteArray)) != -1) {
+                digest.update(byteArray, 0, bytesCount);
+            }
+
+            //  获取文件的 MD5 值,返回的是字节数组,转换成 16 进制数表示byte[] bytes = digest.digest();
+            byte[] bytes = digest.digest();
+            StringBuilder sb = new StringBuilder();
+            for (byte b : bytes) {
+                sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
+            }
+            return sb.toString().toUpperCase();
+        } catch (NoSuchAlgorithmException | IOException e) {
+            throw new KDBizException("文件名称生成MD5失败:" + e);
+        }
+    }
+
+}

+ 54 - 0
base/nckd-base-common/src/main/java/nckd/base/common/utils/GzwSyncBillUtils.java

@@ -0,0 +1,54 @@
+package nckd.base.common.utils;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import nckd.base.common.constant.BaseFieldConst;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @description:国资委_单据数据转换工具类
+ * @author: dingsixi
+ * @create: 2025/12/22 23:04
+ */
+public class GzwSyncBillUtils {
+
+    /**
+     * 设置国资委采集平台对应的表名(业务招待制度)
+     */
+    public final static String BIZ_REGULATION = "biz_regulation";
+
+    /**
+     * 设置国资委采集平台对应的表名(业务招待附件表)
+     */
+    public final static String BIZ_FILE = "biz_file";
+
+    /**
+     *
+     * @param bizPolicy 业务招待制度
+     * @return 将单据数据转换成XML动态参数内容
+     */
+    public static LinkedHashMap<String,Object> getBizPolicyData(DynamicObject bizPolicy){
+        String id = bizPolicy.getString(BaseFieldConst.ID);
+        //业务单元
+        DynamicObject org = bizPolicy.getDynamicObject("nckd_org");
+        //企业名称
+        String enterpriseName = org.getString("name");
+        //统一社会信用代码
+        String creditCode = bizPolicy.getString("nckd_code");
+        //制度名称
+        String regulationName = bizPolicy.getString("name");
+        //印发时间
+        String issueTime = DateUtil.date2str(bizPolicy.getDate("nckd_issuetime"),DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
+
+        //XML单据参数内容 保证XML生成顺序
+        LinkedHashMap<String,Object> dynamicData = new LinkedHashMap<>();
+        dynamicData.put("uuid",id);
+        dynamicData.put("enterprise_name",enterpriseName);
+        dynamicData.put("credit_code",creditCode);
+        dynamicData.put("regulation_name",regulationName);
+        dynamicData.put("issue_time",issueTime);
+
+        return dynamicData;
+    }
+}

+ 289 - 0
base/nckd-base-common/src/main/java/nckd/base/common/utils/GzwXmlUtils.java

@@ -0,0 +1,289 @@
+package nckd.base.common.utils;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDBizException;
+import org.apache.commons.lang3.ObjectUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @description:国资委XNL工具类
+ * @author: dingsixi
+ * @create: 2025/12/23 18:42
+ */
+public class GzwXmlUtils {
+
+    public final static String XML = "xml";
+    public final static String ZIP = "zip";
+
+    /**
+     * 将多个XML文件和可选的附件打包成ZIP,返回ZIP文件对象
+     * @param xmlFiles XML文件列表(放在根目录)
+     * @param attachments 单据附件列表(放在unStruct文件夹,可为null)
+     * @param zipFileName ZIP文件名
+     * @return ZIP文件对象
+     */
+    public static File createZipWithAttachments(List<File> xmlFiles,
+                                                List<File> attachments,
+                                                String zipFileName) {
+
+        // 1. 创建ZIP文件对象
+        File zipFile = new File(zipFileName);
+
+        // 2. 确保父目录存在
+        File parentDir = zipFile.getParentFile();
+        if (parentDir != null && !parentDir.exists()) {
+            parentDir.mkdirs();
+        }
+
+        // 3. 创建ZIP输出流
+        ZipOutputStream zipOut = null;
+        try {
+            zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
+
+            // 4. 遍历XML文件并添加到ZIP根目录
+            if (xmlFiles != null && !xmlFiles.isEmpty()) {
+                for (File xmlFile : xmlFiles) {
+                    if (xmlFile != null && xmlFile.exists() && xmlFile.isFile()) {
+                        addFileToZip(zipOut, xmlFile, "");
+                    }
+                }
+            }
+
+            // 5. 添加附件到unStruct文件夹(如果有)
+            if (attachments != null && !attachments.isEmpty()) {
+                // 可以添加一个文件夹条目(可选)
+                // ZipEntry folderEntry = new ZipEntry("unStruct/");
+                // zipOut.putNextEntry(folderEntry);
+                // zipOut.closeEntry();
+
+                for (File attachment : attachments) {
+                    if (attachment != null && attachment.exists() && attachment.isFile()) {
+                        addFileToZip(zipOut, attachment, "unStruct/");
+                    }
+                }
+            }
+
+        } catch (IOException e) {
+            // 异常处理:记录日志或返回null
+            e.printStackTrace();
+
+            // 如果创建失败,删除可能已创建的不完整文件
+            if (zipFile.exists()) {
+                zipFile.delete();
+            }
+            return null;
+        } finally {
+            // 确保流被关闭
+            if (zipOut != null) {
+                try {
+                    zipOut.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return zipFile;
+    }
+
+    /**
+     * 向ZIP中添加单个文件
+     * @param zipOut ZIP输出流
+     * @param file 要添加的文件
+     * @param folderPath 文件夹路径(可为空""或"unStruct/")
+     */
+    private static void addFileToZip(ZipOutputStream zipOut, File file, String folderPath) {
+        try {
+            // 构建ZIP条目路径
+            String entryName;
+            if (folderPath != null && !folderPath.isEmpty()) {
+                entryName = folderPath + file.getName();
+            } else {
+                entryName = file.getName();
+            }
+
+            // 创建ZIP条目
+            ZipEntry zipEntry = new ZipEntry(entryName);
+            zipOut.putNextEntry(zipEntry);
+
+            // 将文件内容写入ZIP
+            byte[] buffer = new byte[4096];
+            try (FileInputStream fis = new FileInputStream(file)) {
+                int bytesRead;
+                while ((bytesRead = fis.read(buffer)) != -1) {
+                    zipOut.write(buffer, 0, bytesRead);
+                }
+            }
+
+            zipOut.closeEntry();
+
+        } catch (IOException e) {
+
+        }
+    }
+
+    /**
+     * 只打包XML文件,没有单据附件信息
+     */
+    public static File createZipFromXmlFiles(List<File> xmlFiles, String zipFileName) {
+        return createZipWithAttachments(xmlFiles, null, zipFileName);
+    }
+
+    /**
+     * 生成XML内容(多个Record)
+     * @param recordList Record数据列表,每个Map代表一个Record
+     * @return XML内容字符串
+     * @throws Exception 生成失败时抛出异常
+     */
+    public static String generateXml(List<LinkedHashMap<String, Object>> recordList)  {
+
+        // 1. 创建Document对象
+        Document doc = createDocument(recordList);
+
+        // 2. 生成XML内容字符串
+        return convertToXmlString(doc);
+    }
+
+    /**
+     * 创建Document对象
+     */
+    private static Document createDocument(List<LinkedHashMap<String, Object>> recordList)  {
+
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = null;
+        try {
+            builder = factory.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+        Document doc = builder.newDocument();
+
+        // 创建根元素 Package
+        Element packageElement = doc.createElement("Package");
+        doc.appendChild(packageElement);
+
+        // 创建 Head 部分
+        Element headElement = doc.createElement("Head");
+        packageElement.appendChild(headElement);
+
+        // 检查数据是否有效
+        if (recordList.isEmpty()) {
+            throw new IllegalArgumentException("recordList不能为空");
+        }
+
+        LinkedHashMap<String, Object> firstRecord = recordList.get(0);
+
+        // 添加固定节点:resCaption
+        Element resCaptionElement = doc.createElement("resCaption");
+        resCaptionElement.setTextContent(getRequiredValue(firstRecord, "resCaption"));
+        headElement.appendChild(resCaptionElement);
+
+        // 添加固定节点:resCode
+        Element resCodeElement = doc.createElement("resCode");
+        resCodeElement.setTextContent(getRequiredValue(firstRecord, "resCode"));
+        headElement.appendChild(resCodeElement);
+
+        // 创建 Records 部分
+        Element recordsElement = doc.createElement("Records");
+        packageElement.appendChild(recordsElement);
+
+        // 创建多个Record节点
+        for (int i = 0; i < recordList.size(); i++) {
+            LinkedHashMap<String, Object> recordData = recordList.get(i);
+
+            // 创建 Record 元素
+            Element recordElement = doc.createElement("Record");
+            recordsElement.appendChild(recordElement);
+
+            // 添加动态节点(按LinkedHashMap顺序)
+            for (Map.Entry<String, Object> entry : recordData.entrySet()) {
+                String nodeName = entry.getKey();
+                Object nodeValue = entry.getValue();
+
+                // 跳过已经处理的Head固定字段(这些字段只在第一个Record中)
+                if ("resCaption".equals(nodeName) || "resCode".equals(nodeName)) {
+                    // 只有第一个Record需要包含Head字段,后续Record跳过
+                    if (i > 0) {
+                        continue;
+                    }
+                    // 第一个Record的Head字段已经在上面处理过了,这里也跳过
+                    continue;
+                }
+
+                Element element = doc.createElement(nodeName);
+                element.setTextContent(nodeValue != null ? nodeValue.toString() : "");
+                recordElement.appendChild(element);
+            }
+        }
+
+        return doc;
+    }
+
+    /**
+     * 获取必需的字段值
+     */
+    private static String getRequiredValue(LinkedHashMap<String, Object> dataMap, String key) {
+        if (ObjectUtils.isEmpty(dataMap) || !dataMap.containsKey(key)) {
+            throw new IllegalArgumentException("字段 '" + key + "' 的值不能为空");
+        }
+
+        Object value = dataMap.get(key);
+        if (!key.equals("EXT_GUID") && ObjectUtils.isEmpty(value)) {
+            throw new IllegalArgumentException("字段 '" + key + "' 的值不能为空");
+        }
+
+        return value.toString();
+    }
+
+
+    /**
+     * 将Document转换为XML字符串
+     */
+    private static String convertToXmlString(Document doc) {
+        TransformerFactory transformerFactory = TransformerFactory.newInstance();
+        Transformer transformer = null;
+        try {
+            transformer = transformerFactory.newTransformer();
+        } catch (TransformerConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+
+        // 设置输出属性
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+        // 生成XML字符串
+        StringWriter writer = new StringWriter();
+        try {
+            transformer.transform(new DOMSource(doc), new StreamResult(writer));
+        } catch (TransformerException e) {
+            throw new RuntimeException(e);
+        }
+
+        String xmlString = writer.toString();
+
+        // 移除standalone属性
+        return xmlString.replaceFirst(" standalone=\"no\"", "");
+    }
+
+
+
+}

+ 115 - 0
nckd-fi/src/main/java/nckd/fi/er/opplugin/BizPolicyPushOpPlugin.java

@@ -0,0 +1,115 @@
+package nckd.fi.er.opplugin;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.servicehelper.AttachmentServiceHelper;
+import nckd.base.common.utils.*;
+
+import java.io.File;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @description:业务招待制度推送国资委
+ * @author: dingsixi
+ * @create: 2025/12/22 18:14
+ */
+public class BizPolicyPushOpPlugin extends AbstractOperationServicePlugIn {
+
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+        e.getFieldKeys().addAll(this.billEntityType.getAllFields().keySet());
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+        DynamicObject[] dataEntities = e.getDataEntities();
+        //业务招待制度推送国资委
+        pushGzw(dataEntities, e);
+    }
+
+
+    private void pushGzw(DynamicObject[] bizPolicys, EndOperationTransactionArgs e) {
+        //当前时间年月日时分秒
+        String cusDateStr = GzwCommonUtils.getCusDateStr();
+        //获取费控系统参数
+        Map<String, Object> sysCtrlParameter = ParamUtils.getSysCtrlParameter(ParamUtils.EM);
+        //获取资源代号信息 基础信息
+        Map<String, String> resMap = GzwCommonUtils.getResMap(GzwSyncBillUtils.BIZ_REGULATION, (String) sysCtrlParameter.get("nckd_res"));
+        //获取资源代号信息 附件信息
+        Map<String, String> attrResMap = GzwCommonUtils.getResMap(GzwSyncBillUtils.BIZ_FILE, (String) sysCtrlParameter.get("nckd_res"));
+
+        //获取版本号
+        String ver = (String) sysCtrlParameter.get("nckd_ver");
+        //接口代号
+        String apiCode = (String) sysCtrlParameter.get("nckd_apicode");
+        //基础信息资源代号
+        String resCode = resMap.get("nckd_rescode");
+        //附件信息资源代号
+        String attrResCode = attrResMap.get("nckd_rescode");
+
+
+        Object[] ids = Arrays.stream(bizPolicys).map(it -> it.getString("id")).toArray();
+        //获取所有单据附件面板信息
+        Map<String, List<Map<String, Object>>> bizPolicyAttrMap = AttachmentServiceHelper.getAttachments(this.billEntityType.getName(),
+                ids, "nckd_attachmentpanelap", Boolean.FALSE);
+
+
+        //业务单据根据业务单元统一社会信用代码分组
+        Map<String, List<DynamicObject>> bizPolicyByOrgCodeMap = Arrays.stream(bizPolicys).collect(Collectors.groupingBy(
+                it -> it.getString("nckd_code")
+        ));
+
+        for (Map.Entry<String, List<DynamicObject>> bizPolicyEntrySet : bizPolicyByOrgCodeMap.entrySet()) {
+            //统一社会信用代码
+            String orgCode = bizPolicyEntrySet.getKey();
+            List<DynamicObject> bizPolicyList = bizPolicyEntrySet.getValue();
+            //将所有单据数据转换成动态参数
+            List<LinkedHashMap<String, Object>> dataList = new ArrayList<>();
+            //将所有单据附件数据转换成动态参数
+            List<LinkedHashMap<String, Object>> attrDataList = new ArrayList<>();
+
+            //所有单据附件信息
+            List<File> billAttrFileList = new ArrayList<>();
+
+            //生成XMLRecords内容
+            for (DynamicObject bizPolicy : bizPolicyList) {
+                //业务单元
+                DynamicObject org = bizPolicy.getDynamicObject("nckd_org");
+                //获取数据标识
+                String dataFlag = GzwCommonUtils.getDataFlag(e.getOperationKey(), bizPolicy);
+
+                //将单据数据转换成XML动态参数内容
+                LinkedHashMap<String, Object> dynamicData = GzwSyncBillUtils.getBizPolicyData(bizPolicy);
+                //XML固定参数内容
+                Map<String, Object> fixedData = GzwCommonUtils.getFixedData(resMap, org, dataFlag);
+                dynamicData.putAll(fixedData);
+                dataList.add(dynamicData);
+
+                //获取单据附件信息
+                List<Map<String, Object>> bizPolicyAttr = bizPolicyAttrMap.getOrDefault(bizPolicy.getString("id"), Collections.emptyList());
+                //附件信息转File、附件信息生成动态参数
+                GzwCommonUtils.getBillAttrDataAndFile(bizPolicyAttr,billAttrFileList,attrDataList,fixedData);
+            }
+
+            Map<String,Object> xmlMap = new HashMap<>();
+            //基础信息
+            xmlMap.put(resCode,dataList);
+            //附件信息
+            xmlMap.put(attrResCode,attrDataList);
+
+            //将单据数据动态参数生成XML文件,附件转为FIle文件,合并生成zip文件
+            File zipFile = GzwCommonUtils.createZipFile(orgCode, ver, cusDateStr, xmlMap, billAttrFileList, apiCode);
+
+            //TODO 推送国资委数据采集平台接口,记录日志,单据信息记录是否推送成功
+
+        }
+
+
+    }
+}